View Javadoc
1 2 3 package net.sf.voruta; 4 5 import java.util.*; 6 import java.lang.reflect.*; 7 import java.lang.reflect.Array; 8 import java.sql.*; 9 import java.beans.*; 10 import java.io.*; 11 12 13 14 15 /*** 16 * 17 * @author baliuka 18 */ 19 final class SqlParser implements Language,LanguageFactory { 20 21 static final String ENDLN = System.getProperty("line.separator"); 22 23 StringBuffer sb = new StringBuffer(); 24 25 boolean escape; 26 char [] sql; 27 28 int indexMap[]; 29 List fragments = new ArrayList(); 30 int dynamicIndexMap[]; 31 BeanProperties.Property propertyPaths[]; 32 33 ClassFinder finder; 34 Method method; 35 36 37 SqlParser(){ } 38 39 private String getValue(String constName){ 40 41 constName = constName.trim(); 42 43 String value = Db.getProperty(constName); 44 if(value != null){ 45 return value; 46 } 47 try{ 48 int cln = constName.lastIndexOf('.'); 49 if(cln == -1){ 50 51 return method.getDeclaringClass().getField(constName.trim()).get(null).toString(); 52 53 }else{ 54 55 String fld = constName.substring(cln + 1,constName.length()); 56 Object f = finder.findByName( constName.substring(0,cln) ). 57 getField( fld ).get(null); 58 if(ThreadLocal.class.isAssignableFrom(f.getClass())){ 59 return ((ThreadLocal)f).get().toString(); 60 } else{ 61 return f.toString(); 62 } 63 } 64 65 }catch(Exception e){ 66 throw new DbParseException("can not evaluate constant " + constName,e); 67 } 68 } 69 70 private boolean isDigit(char c){ 71 return escape&&Character.isDigit(c) ; 72 } 73 74 private boolean isBlank(char c){ 75 return escape && ( c == '\t' || c == ' ' || 76 c == '\n' || c == '\r' || 77 c == ',' || c == ')' ); 78 } 79 80 private boolean isConst(char c){ 81 return escape && c == '{' ; 82 } 83 84 85 86 String generateSql(Object args[]){ 87 88 StringBuffer buffer = new StringBuffer(); 89 int index = 0; 90 91 for(Iterator i = fragments.iterator(); i.hasNext();){ 92 buffer.append(i.next()); 93 try{ 94 95 buffer.append( args[dynamicIndexMap[index++]] ); 96 97 }catch(ArrayIndexOutOfBoundsException ae){ 98 99 throw new DbException( "dynamic sql parameter " + index + "(" + args.length 100 + ")" + " is out of range " ,ae); 101 } 102 } 103 return buffer.append(sb.toString()).toString(); 104 } 105 106 private Object defaultValue( Class cls )throws Exception{ 107 108 if( cls == null || cls == Object.class ){ 109 110 return null; 111 112 }else if( java.util.Date.class.isAssignableFrom(cls) ){ 113 return cls.getConstructor( new Class[]{ long.class } ) 114 .newInstance( new Object[]{ 115 new Long(System.currentTimeMillis()) 116 }); 117 }else if(cls.isPrimitive()){ 118 if( int.class == cls ){ 119 return new Integer(0); 120 }else if( long.class == cls ) { 121 return new Long(0); 122 }else if( float.class == cls ){ 123 return new Float(0); 124 }else if( double.class == cls ){ 125 return new Double(0); 126 }else if( char.class == cls ){ 127 return new Character('\u0000'); 128 }else if( boolean.class == cls ){ 129 return Boolean.FALSE; 130 }else{ 131 return new Integer(0); 132 } 133 134 }else{ 135 //let driver to convert 136 return new Integer(0); 137 } 138 } 139 140 private void setDefaultParams(PreparedStatement ps)throws Exception{ 141 Class types[] = method.getParameterTypes(); 142 for(int i = 0; i < indexMap.length; i++ ){ 143 144 if( propertyPaths[ i ] != null ){ 145 ps.setObject(1 + i , defaultValue( propertyPaths[i].getType() )); 146 }else{ 147 148 ps.setObject(1 + i , defaultValue( types[indexMap[i]] )); 149 } 150 151 } 152 153 } 154 155 Object[] prepare(Object args[])throws Exception{ 156 157 int len = indexMap.length; 158 Object pargs[] = new Object[ len ]; 159 160 for(int i = 0; i < len; i++ ){ 161 162 try{ 163 164 Object arg ; 165 if(indexMap[i] != -1 ){ 166 arg = args[ indexMap[i] ]; 167 }else{ 168 arg = null; 169 } 170 171 if( propertyPaths[ i ] != null ){ 172 173 pargs[i] = propertyPaths[i].getValue(arg); 174 175 }else{ 176 177 pargs[i] = arg; 178 179 } 180 181 }catch(ArrayIndexOutOfBoundsException ae){ 182 183 throw new DbException( generateSql(args) + " parameter " + i +" is out of range (" + indexMap[i] + ")" ,ae); 184 } 185 } 186 187 return pargs; 188 } 189 190 private void parse(){ 191 192 List indexes = new java.util.ArrayList(); 193 List dynamicIndexes = new java.util.ArrayList(); 194 List paths = new java.util.ArrayList(); 195 int i = 0; 196 197 try{ 198 199 LOOP: 200 for ( ; i < sql.length; i++ ){ 201 202 if(sql[i] == '$'){ 203 if( escape ){ 204 sb.append('$'); 205 escape = false; 206 continue LOOP; 207 } 208 escape = true; 209 continue LOOP; 210 } 211 try{ 212 213 if( isConst(sql[i]) ){ 214 for( int j = i ; j < sql.length ; j++ ){ 215 if(sql[j] == '}'){ 216 int cnt = j - i; 217 String str = new String(sql,i + 1,cnt - 1); 218 try{ 219 dynamicIndexes.add( new Integer( Integer.parseInt(str) - 1 ) ); 220 fragments.add(sb.toString()); 221 sb.delete(0,i); 222 }catch(NumberFormatException nfe){ 223 sb.append(getValue(str)); 224 } 225 i += cnt; 226 continue LOOP; 227 } 228 } 229 } 230 231 232 if ( escape ){ 233 234 if( isDigit(sql[i]) ){ 235 for( int j = i; j <= sql.length; j++ ){ 236 if(j == sql.length || !isDigit(sql[j]) ){ 237 int cnt = j - i; 238 Integer p = new Integer(Integer.parseInt(new String(sql,i,cnt)) - 1 ); 239 indexes.add( p ); 240 sb.append('?'); 241 i += cnt - 1 ; 242 if( i < sql.length - 1 && sql[i + 1] == '.'){ 243 i++; 244 for( j = i ; j < sql.length && !isBlank(sql[j]); j++ ); 245 cnt = j - i - 1; 246 String pName = new String(sql,i + 1,cnt); 247 BeanProperties.Property prop = 248 BeanProperties.getProperty(method.getParameterTypes()[p.intValue()],pName ); 249 paths.add( prop ); 250 i += cnt ; 251 252 }else{ 253 paths.add(null); 254 } 255 continue LOOP; 256 } 257 } 258 }else{ //ThreadLocal 259 260 for( int j = i; j <= sql.length; j++ ){ 261 if(j == sql.length || sql[j] ==' ' || sql[j]=='\n' ){ 262 int cnt = j - i; 263 String name = new String(sql,i,cnt); 264 int ind = name.lastIndexOf('.'); 265 Class declarer; 266 String fieldName; 267 if(ind == -1 ){ 268 declarer = method.getDeclaringClass(); 269 fieldName = name; 270 }else{ 271 declarer = finder.findByName( name.substring(0,ind) ); 272 fieldName = name.substring(ind); 273 } 274 indexes.add( new Integer(-1) ); 275 sb.append('?'); 276 paths.add(BeanProperties.getProperty(declarer,fieldName)); 277 i += cnt ; 278 continue LOOP; 279 } 280 } 281 throw new DbParseException("ThreadLocal parameter expected"); 282 } 283 284 }//end escape 285 286 sb.append(sql[i]); 287 288 }finally{ 289 290 escape = false; 291 292 } 293 294 } 295 }catch(Exception e){ 296 char blanks[] = new char[ i + 1 ]; 297 Arrays.fill(blanks,' '); 298 blanks[ i ] = '^'; 299 StringBuffer msg = new StringBuffer( ENDLN ); 300 msg.append( sql ); 301 msg.append( ENDLN ); 302 msg.append( blanks ); 303 throw new DbParseException(msg.toString(),e); 304 } 305 306 307 propertyPaths = (BeanProperties.Property []) 308 paths.toArray( new BeanProperties.Property[]{} ); 309 310 dynamicIndexMap = new int[dynamicIndexes.size()]; 311 312 for( int j = 0; j < dynamicIndexMap.length; j++ ){ 313 dynamicIndexMap[j] = ((Integer)dynamicIndexes.get(j)).intValue(); 314 } 315 316 indexMap = new int[indexes.size()]; 317 for( int j = 0; j < indexMap.length; j++ ){ 318 indexMap[j] = ((Integer)indexes.get(j)).intValue(); 319 } 320 321 } 322 323 public Object executeQuery(String connection, Object[] args, 324 ResultSetHandler handler, Object[] userObj) 325 throws Exception{ 326 327 return DbUtils.executeQuery( connection, generateSql(args) , 328 prepare(args), handler,method.getReturnType(), args); 329 } 330 331 public int executeUpdate(String connection, Object[] args) throws Exception{ 332 333 return DbUtils.executeUpdate(connection, generateSql(args), prepare(args)); 334 } 335 336 /*** voruta calls it once to prepare/compile query string 337 * 338 */ 339 public void compile(ClassFinder finder, Method method, 340 String query, Properties tags) throws Exception{ 341 342 this.sql = query.toCharArray(); 343 this.method = method; 344 this.finder = finder; 345 346 347 parse(); 348 349 } 350 351 public Language newInstance() { 352 return new SqlParser(); 353 } 354 355 private boolean canExplain(){ 356 String s = sb.toString().trim().toUpperCase(); 357 if( s.length() == 0 || dynamicIndexMap.length > 0){ 358 return false; 359 } 360 if( s.startsWith("SELECT") || 361 s.startsWith("UPDATE") || 362 s.startsWith("DELETE") || s.startsWith("INSERT") ){ 363 return true; 364 } 365 return false; 366 } 367 368 public void explain(String connection, java.io.PrintWriter pw) throws Exception { 369 370 if(!canExplain()){ 371 return; 372 } 373 374 Connection c = ThreadLocalConnection.get(connection); 375 376 if(c.getWarnings() != null){ 377 DbUtils.getLog().warn(c.getWarnings()); 378 } 379 380 String query = Db.getExplainPrefix() + " " + sb.toString(); 381 PreparedStatement ps = c.prepareStatement(query); 382 try{ 383 384 setDefaultParams(ps); 385 386 StringBuffer buffer = new StringBuffer(); 387 try{ 388 389 buffer.append( method ); 390 buffer.append('\n'); 391 buffer.append( query ); 392 393 if( ps.execute() ){ 394 395 ResultSet rs = ps.getResultSet(); 396 try{ 397 398 399 ResultSetMetaData md = rs.getMetaData(); 400 buffer.append('\n'); 401 for(int i = 1; i <= md.getColumnCount(); i++ ){ 402 buffer.append(md.getColumnName(i)); 403 buffer.append("\t"); 404 } 405 buffer.append('\n'); 406 while(rs.next()){ 407 for(int i = 1; i <= md.getColumnCount(); i++ ){ 408 buffer.append(rs.getObject(i)); 409 buffer.append("\t"); 410 } 411 buffer.append('\n'); 412 413 } 414 415 416 }finally{ 417 rs.close(); 418 } 419 420 } 421 }finally{ 422 pw.print(buffer.toString()); 423 } 424 425 SQLWarning warn = c.getWarnings(); 426 427 if(warn != null){ 428 429 pw.print(warn.getMessage()); 430 431 432 }; 433 434 c.clearWarnings(); 435 436 }finally{ 437 ps.close(); 438 } 439 440 } 441 442 public void explain(String connection) throws Exception { 443 444 StringWriter sw = new StringWriter(); 445 PrintWriter pw = new PrintWriter(sw); 446 explain(connection,pw); 447 pw.flush(); 448 DbUtils.getLog().info(sw.getBuffer().toString()); 449 450 } 451 452 453 454 455 456 } 457 458 459 460

This page was automatically generated by Maven