1 3 package jodd.db.orm.sqlgen; 4 5 import jodd.db.orm.DbEntityDescriptor; 6 import jodd.db.orm.DbOrm; 7 import jodd.db.orm.DbOrmException; 8 import jodd.util.ClassLoaderUtil; 9 import jodd.util.StringUtil; 10 import jodd.util.ArraysUtil; 11 import jodd.bean.BeanUtil; 12 13 import java.util.HashMap ; 14 import java.util.LinkedHashMap ; 15 import java.util.Map ; 16 17 18 45 public class DbSqlTemplate implements DbSqlGenerator { 46 47 49 protected String template; protected DbOrm dbOrm; protected Map <String , Object > parameters; 53 55 public static enum ColumnAliasType { 56 TABLE_NAME(1), 57 TABLE_REFERENCE(2), 58 COLUMN_CODE(3); 59 int value; 60 ColumnAliasType(int aliasType) { 61 this.value = aliasType; 62 } 63 } 64 65 69 public static boolean DEFAULT_COLUMN_ALIAS = false; 70 71 74 public static ColumnAliasType DEFAULT_COLUMN_ALIAS_TYPE = ColumnAliasType.COLUMN_CODE; 75 76 80 public static boolean DEFAULT_ESCAPE_MACROS = false; 81 82 protected boolean columnAliases; 83 protected ColumnAliasType columnAliasesType; 84 protected boolean escape; 85 86 90 public DbSqlTemplate columnAliases(boolean aliases) { 91 this.columnAliases = aliases; 92 return this; 93 } 94 95 99 public DbSqlTemplate setColumnAliasesType(ColumnAliasType aliasesType) { 100 this.columnAliasesType = aliasesType; 101 this.columnAliases = true; 102 return this; 103 } 104 105 110 public DbSqlTemplate escape(boolean escape) { 111 this.escape = escape; 112 return this; 113 } 114 115 116 118 public DbSqlTemplate(String template) { 119 this(template, DbOrm.getInstance()); 120 } 121 122 public DbSqlTemplate(String template, DbOrm dbOrm) { 123 this.template = template; 124 this.dbOrm = dbOrm; 125 this.parameters = new LinkedHashMap <String , Object >(); 126 this.columnAliases = DEFAULT_COLUMN_ALIAS; 127 this.escape = DEFAULT_ESCAPE_MACROS; 128 this.columnAliasesType = DEFAULT_COLUMN_ALIAS_TYPE; 129 } 130 131 133 136 protected Map <String , Object > references; 137 138 142 public DbSqlTemplate use(String name, Object value) { 143 if (references == null) { 144 references = new HashMap <String , Object >(); 145 } 146 Object oldValue = references.put(name, value); 147 if (oldValue != null) { 148 throw new DbOrmException("Object reference with the same name '" + name + "' already exists."); 149 } 150 return this; 151 } 152 153 154 156 164 protected Map <String , DbEntityDescriptor> tablesRefs; 165 166 169 protected static Map <String , Class > loadedClasses; 170 171 172 209 public String parseTables(String template) { 210 StringBuilder result = new StringBuilder (template.length()); 211 tablesRefs = new HashMap <String , DbEntityDescriptor>(); 212 while (true) { 213 String allTables = nextRegion(result, template, "$T{", "}"); 214 if (allTables == null) { 215 break; 216 } 217 String [] tables = StringUtil.split(allTables, ","); 218 for (int i = 0; i < tables.length; i++) { 219 String tableRef = tables[i].trim(); 221 String tableAlias = null; 222 223 int spaceNdx = tableRef.indexOf(' '); if (spaceNdx != -1) { 225 tableAlias = tableRef.substring(spaceNdx + 1).trim(); 226 tableRef = tableRef.substring(0, spaceNdx); 227 } 228 if ((tableRef.length() > 0) && (tableRef.charAt(0) == '.')) { String packagePrefix = dbOrm.getPackagePrefix(); 230 if (packagePrefix != null) { 231 tableRef = packagePrefix + tableRef; 232 } 233 } 234 235 Class type = null; 236 237 if (references != null) { Object data = references.get(tableRef); 239 if (data != null) { 240 if (data instanceof Class ) { 241 type = (Class ) data; 242 } else { 243 type = data.getClass(); 244 } 245 if (tableAlias == null) { 246 tableAlias = tableRef; 247 } 248 } 249 } 250 251 if (type == null) { 253 if (loadedClasses == null) { 254 loadedClasses = new HashMap <String , Class >(); 255 } 256 type = loadedClasses.get(tableRef); 257 if (type == null) { 258 try { 259 type = ClassLoaderUtil.loadClass(tableRef, DbSqlTemplate.class); 260 loadedClasses.put(tableRef, type); 261 } catch (ClassNotFoundException cnfex) { 262 throw new DbOrmException("Unable to resolve table reference '" + tableRef + "'.", cnfex); 263 } 264 } 265 } 266 267 DbEntityDescriptor ded = dbOrm.lookup(type); 268 String tableName = ded.getTableName(); 269 if (tableAlias != null) { 270 if (tableAlias.equals("-") == true) { 271 tablesRefs.put(tableRef, ded); 272 tableRef = null; 273 } else { 274 tablesRefs.put(tableAlias, ded); 275 tableRef = tableAlias; 276 } 277 } else { 278 tableRef = type.getSimpleName(); 279 tablesRefs.put(tableRef, ded); 280 } 281 282 283 if (i > 0) { 285 result.append(',').append(' '); 286 } 287 result.append(tableName); 288 if (tableRef != null) { 289 result.append(' ').append(tableRef); 290 } 291 } 292 } 293 return result.toString(); 294 } 295 296 298 301 protected Map <String , String []> columnData; 302 303 306 protected int columnCount = 0; 307 308 347 protected String parseColumns(String template) { 348 StringBuilder result = new StringBuilder (template.length()); 349 while (true) { 350 String allColumns = nextRegion(result, template, "$C{", "}"); 351 if (allColumns == null) { 352 break; 353 } 354 String [] columns = StringUtil.split(allColumns, ","); 355 for (int i = 0; i < columns.length; i++) { 356 String column = columns[i].trim(); 357 String tableRef; String tableName = null; 360 boolean resolveObject = true; 361 if (column.startsWith("+") == true) { 362 resolveObject = false; 363 column = column.substring(1); 364 } 365 366 int dotNdx = column.indexOf('.'); 367 if (dotNdx != -1) { 368 tableRef = column.substring(0, dotNdx); column = column.substring(dotNdx + 1); 371 372 DbEntityDescriptor ded = tablesRefs.get(tableRef); 373 if (ded == null) { 374 throw new DbOrmException("Table reference '" + tableRef + "' not found for column '" 375 + tableRef + '.' + column + "'."); 376 } 377 if (columnAliases == true) { 378 tableName = ded.getTableName(); 379 switch (columnAliasesType) { 380 case TABLE_REFERENCE: 381 case COLUMN_CODE: 382 if (columnData == null) { 383 columnData = new HashMap <String , String []>(); 384 } 385 break; 386 } 387 } 388 389 if (column.equals("*") == true) { 391 String [] columnList = ded.getColumns(); 392 if (resolveObject == true) { Object object = null; 394 if (references != null) { 395 object = references.get(tableRef); 396 } 397 if ((object != null) && (object instanceof Class == false)) { 398 String properties[] = ded.getProperties(); 399 String [] resultList = new String [columnList.length]; 400 int size = 0; 401 for (int j = 0; j < properties.length; j++) { 402 String property = properties[j]; 403 Object value = BeanUtil.getDeclaredProperty(object, property); 404 if (value == null) { 405 continue; 406 } 407 resultList[size++] = columnList[j]; 408 } 409 columnList = ArraysUtil.resize(resultList, size); 410 } 411 } 412 for (int j = 0; j < columnList.length; j++) { 413 appendColumnName(result, tableRef, tableName, columnList[j], i + j); 414 } 415 continue; 416 } 417 column = ded.getColumnName(column); 418 if (column == null) { 419 throw new DbOrmException("Unable to find property for column reference '" + columns[i].trim() + "'."); 420 } 421 appendColumnName(result, tableRef, tableName, column, i); 422 } else { 423 DbEntityDescriptor ded = tablesRefs.get(column); 425 if (ded == null) { 426 throw new DbOrmException("Table reference '" + column + "' not found."); 427 } 428 String [] columnList = ded.getColumns(); 429 if (resolveObject == true) { Object object = null; 431 if (references != null) { 432 object = references.get(column); 433 } 434 if ((object != null) && (object instanceof Class == false)) { 435 String properties[] = ded.getProperties(); 436 String [] resultList = new String [columnList.length]; 437 int size = 0; 438 for (int j = 0; j < properties.length; j++) { 439 String property = properties[j]; 440 Object value = BeanUtil.getDeclaredProperty(object, property); 441 if (value == null) { 442 continue; 443 } 444 resultList[size++] = columnList[j]; 445 } 446 columnList = ArraysUtil.resize(resultList, size); 447 } 448 } 449 for (int j = 0; j < columnList.length; j++) { 450 String col = columnList[j]; 451 if (j > 0) { 452 result.append(',').append(' '); 453 } 454 result.append(col); 455 } 459 } 460 } 461 } 462 return result.toString(); 463 } 464 465 468 protected void appendColumnName(StringBuilder result, String tableRef, String tableName, String column, int i) { 469 if (i > 0) { 470 result.append(',').append(' '); 471 } 472 if (tableRef != null) { 473 result.append(tableRef).append('.'); 474 } 475 result.append(column); 476 if (tableName != null) { 477 result.append(" as "); 478 switch (columnAliasesType) { 479 case TABLE_NAME: 480 result.append(tableName).append(dbOrm.getColumnAliasSeparator()).append(column); 481 break; 482 case TABLE_REFERENCE: 483 columnData.put(tableRef, new String [] {tableName}); 484 result.append(tableRef).append(dbOrm.getColumnAliasSeparator()).append(column); 485 break; 486 case COLUMN_CODE: 487 String code = "col_" + Integer.toString(columnCount++) + '_'; 488 columnData.put(code, new String [] {tableName, column}); 489 result.append(code); 490 break; 491 } 492 } 493 } 494 495 496 498 503 public String parseReferences(String template) { 504 StringBuilder result = new StringBuilder (template.length()); 505 int templateLen = template.length(); 506 int lastNdx = 0; 507 while (true) { 508 int ndx = template.indexOf('$', lastNdx); 509 if (ndx == -1) { 510 result.append(template.substring(lastNdx)); 511 break; 512 } 513 if ((ndx < templateLen - 2) && (template.charAt(ndx + 2) == '{')) { ndx += 2; 515 result.append(template.substring(lastNdx, ndx)); 516 lastNdx = ndx; 517 continue; 518 } 519 520 if ((ndx > 0) && (template.charAt(ndx - 1) == '\\')) { result.append(template.substring(lastNdx, ndx - 1)).append('$'); 522 lastNdx = ndx + 1; 523 continue; 524 } 525 526 result.append(template.substring(lastNdx, ndx)); 527 ndx++; 528 529 int endNdx = ndx; while (endNdx < templateLen) { 531 char c = template.charAt(endNdx); 532 if ((c != '.') && (Character.isLetter(c) == false)) { 533 break; 534 } 535 endNdx++; 536 } 537 String column = template.substring(ndx, endNdx); 538 lastNdx = endNdx; 539 540 String tableRef = null; 542 int dotNdx = column.indexOf('.'); 543 if (dotNdx != -1) { 544 tableRef = column.substring(0, dotNdx); 545 column = column.substring(dotNdx + 1); 546 547 DbEntityDescriptor ded = tablesRefs.get(tableRef); 548 if (ded == null) { 549 throw new DbOrmException("Unable to resolve column reference '" + tableRef + '.' + column + "'."); 550 } 551 column = ded.getColumnName(column); 552 } 553 if (tableRef != null) { 554 result.append(tableRef).append('.'); 555 } 556 result.append(column); 557 } 558 return result.toString(); 559 } 560 561 562 564 protected int[] colNdx; 565 protected char escapeChar = '\\'; 566 protected String escapeLeftBoundary; 567 protected String escapeRightBoundary; 568 569 protected String nextRegion(StringBuilder destination, String template, String leftBoundary, String rightBoundary) { 570 return escape == true ? 571 nextRegionWithEscape(destination, template, leftBoundary, rightBoundary) 572 : 573 nextRegionNoEscape(destination, template, leftBoundary, rightBoundary); 574 } 575 576 protected String nextRegionWithEscape(StringBuilder destination, String template, String leftBoundary, String rightBoundary) { 577 int ndx = 0; 578 if (colNdx != null) { 579 ndx = colNdx[3]; 580 } else { 581 escapeLeftBoundary = escapeChar + leftBoundary; 582 escapeRightBoundary = escapeChar + rightBoundary; 583 } 584 colNdx = StringUtil.indexOfRegion(template, leftBoundary, rightBoundary, escapeChar, ndx); 585 if (colNdx == null) { 586 String rest = template.substring(ndx); 587 destination.append(StringUtil.replace(rest, escapeLeftBoundary, leftBoundary)); 588 return null; 589 } 590 591 String rest = template.substring(ndx, colNdx[0]); 592 destination.append(StringUtil.replace(rest, escapeLeftBoundary, leftBoundary)); 593 String result = template.substring(colNdx[1], colNdx[2]).trim(); 594 result = StringUtil.replace(result, escapeRightBoundary, rightBoundary); 595 return result; 596 } 597 598 protected String nextRegionNoEscape(StringBuilder destination, String template, String leftBoundary, String rightBoundary) { 599 int ndx = 0; 600 if (colNdx != null) { 601 ndx = colNdx[3]; 602 } else { 603 escapeLeftBoundary = escapeChar + leftBoundary; 604 escapeRightBoundary = escapeChar + rightBoundary; 605 } 606 colNdx = StringUtil.indexOfRegion(template, leftBoundary, rightBoundary, escapeChar, ndx); 607 if (colNdx == null) { 608 destination.append(template.substring(ndx)); 609 return null; 610 } 611 612 destination.append(template.substring(ndx, colNdx[0])); 613 return template.substring(colNdx[1], colNdx[2]).trim(); 614 } 615 616 617 619 public String generateQuery() { 620 return parseColumns(parseReferences(parseTables(template))); 621 } 622 623 public Map <String , Object > getQueryParameters() { 624 return parameters; 625 } 626 627 public Map <String , String []> getColumnData() { 628 return columnData; 629 } 630 } 631 | Popular Tags |