001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.sqlc; 005 006 import java.io.File; 007 import java.io.FileInputStream; 008 import java.io.FileReader; 009 import java.io.IOException; 010 import java.io.Reader; 011 import java.sql.SQLException; 012 import java.util.ArrayList; 013 import java.util.Collection; 014 import java.util.HashMap; 015 import java.util.Iterator; 016 import java.util.Map; 017 import java.util.Properties; 018 019 import org.apache.bcel.classfile.JavaClass; 020 import org.apache.tools.ant.BuildException; 021 import org.apache.tools.ant.Project; 022 import org.apache.tools.ant.Task; 023 024 import biz.hammurapi.ant.ConnectionEntry; 025 import biz.hammurapi.ant.ObjectEntry; 026 import biz.hammurapi.codegen.DocumentingConsumer; 027 import biz.hammurapi.codegen.GenerationException; 028 import biz.hammurapi.codegen.HtmlDocConsumer; 029 import biz.hammurapi.codegen.XmlDocConsumer; 030 import biz.hammurapi.config.Context; 031 import biz.hammurapi.config.MapContext; 032 import biz.hammurapi.sql.SQLProcessor; 033 import biz.hammurapi.sql.hypersonic.HypersonicInMemoryDataSource; 034 import biz.hammurapi.sql.metadata.ColumnDescriptor; 035 import biz.hammurapi.sql.metadata.DefaultGenerationPolicy; 036 import biz.hammurapi.sql.metadata.GenerationPolicy; 037 import biz.hammurapi.sql.metadata.IndexDescriptor; 038 import biz.hammurapi.sql.metadata.KeyDescriptor; 039 import biz.hammurapi.sql.metadata.KeyEntry; 040 import biz.hammurapi.sql.metadata.Metadata; 041 import biz.hammurapi.sql.metadata.TableDescriptor; 042 import biz.hammurapi.sql.metadata.Metadata.TableAcceptor; 043 044 045 /** 046 * Compiles SQL query to interface and engine classes using metadata obtained 047 * from the database. 048 * <section name="Example" suppress-description="yes"> 049 * SQLC uses <a href="http://jakarta.apache.org/bcel/">BCEL</a> and <a href="http://www.antlr.org">ANTLR</a> 050 for code generation. If you have pvcommons.jar, bcel-5.1.jar and antlr.jar in 051 the system classpath 052 or in Ant lib directory then SQLC task can be defined as 053 <pre style="border: 1px solid ; background-color: rgb(204, 204, 204);"><taskdef name="sqlc" classname="biz.hammurapi.sqlc.StatementCompilerTask"/><br/></pre> 054 Otherwise jars which are not in the classpath shall be specified in task definition 055 classpath. SQLC connects to the target database during generation, 056 therefore database driver shall also be present in the classpath<br/> 057 <pre style="border: 1px solid ; background-color: rgb(204, 204, 204);"><taskdef name="sqlc" classname="biz.hammurapi.sqlc.StatementCompilerTask"><br/> <classpath><br/> <fileset dir="${tools}/bcel-5.1" includes="bcel-5.1.jar"/><br/> <fileset dir="${tools}/ANTLR" includes="antlr.jar"/><br/> <fileset dir="${tools}/pvcommons/java-1.4" includes="pvcommons.jar"/><br/> <fileset dir="${tools}/hsqldb/lib" includes="hsqldb.jar"/><br/> </classpath><br/></taskdef><br/></pre> 058 Usage: 059 <pre style="border: 1px solid ; background-color: rgb(204, 204, 204);"><sqlc <br/> script="src/com/pavelvlasov/jsel/impl/Hypersonic.sql"<br/> dir="sqlc_generated"<br/> docDir="sqlcDoc"<br/> package="biz.hammurapi.jsel.impl.sql"<br/> masterEngine="Engine"<br/>> <br/> <query name="CompilationUnit" singleRow="yes"><br/> SELECT * FROM COMPILATION_UNIT WHERE ID=?<br/> </query><br/><br/> <query name="CompilationUnitByStoreLevel"><br/> SELECT * FROM COMPILATION_UNIT C<br/> WHERE REPOSITORY=? AND C.STORE_LEVEL=? AND<br/> EXISTS(SELECT * FROM COMPILATION_UNIT_SCAN S<br/> WHERE S.COMPILATION_UNIT_ID=C.ID AND<br/> S.REPOSITORY=C.REPOSITORY AND S.SCAN_ID=?)<br/> </query><br/> <br/> <update name="DeleteCompilationUnit"><br/> DELETE FROM COMPILATION_UNIT WHERE ID=?<br/> </update> <br/></sqlc><br/></pre> 060 </section> 061 * @ant.element name="statementcompiler" 062 * @author Pavel Vlasov 063 * @version $Revision: 1.14 $ 064 */ 065 public class StatementCompilerTask extends Task { 066 ObjectEntry generationPolicyEntry; 067 068 private StatementCompilerTask parent; 069 private boolean inheritMetadata; 070 071 /** 072 * Use parent's metadata file if any. 073 * Valid for nested tasks. Default is false. 074 * @ant.non-required 075 * @param inheritMetadata 076 */ 077 public void setInheritMetadata(boolean inheritMetadata) { 078 this.inheritMetadata=inheritMetadata; 079 } 080 081 public StatementCompilerTask() { 082 // Default constructor 083 } 084 085 private StatementCompilerTask(StatementCompilerTask parent) { 086 this.parent=parent; 087 setProject(parent.getProject()); 088 } 089 090 private String engineVisibility="public"; 091 private String engineMethodsVisibility="public"; 092 private String interfaceImplVisibility="public"; 093 094 private Collection generatedInterfaces=new ArrayList(); 095 096 /** 097 * Visibility of engine methods. Valid values are "public" (default), "protected" and 098 * "" (empty string for default/package visibility). 099 * @ant.non-required 100 * @param engineMethodsVisibility 101 */ 102 public void setEngineMethodsVisibility(String engineMethodsVisibility) { 103 if ("public".equals(engineMethodsVisibility) || "protected".equals(engineMethodsVisibility) || "".equals(engineMethodsVisibility)) { 104 this.engineMethodsVisibility = engineMethodsVisibility; 105 } else { 106 throw new BuildException("Invalid visibility value: '"+engineMethodsVisibility+"'"); 107 } 108 } 109 110 private Boolean useSqlTypes; 111 112 /** 113 * If true then generated classes will use setObject(int, Object, int) method 114 * instead of setObject(int, Object) to set parameters of object type. 115 * Nested tasks inherit parent setting unless overriden. 116 * @ant.non-required 117 * @param useSqlTypes 118 */ 119 public void setUseSqlTypes(boolean useSqlTypes) { 120 this.useSqlTypes = useSqlTypes ? Boolean.TRUE : Boolean.FALSE; 121 } 122 123 Boolean getUseSqlTypes() { 124 return useSqlTypes; 125 } 126 127 private File metadataFile; 128 129 /** 130 * Metadata file 131 * @param metadataFile 132 * @ant.non-required 133 */ 134 public void setMetadata(File metadataFile) { 135 this.metadataFile=metadataFile; 136 } 137 138 private String indexName; 139 140 /** 141 * Documentation index file name. 142 * Defaults to 'index.xml' for XML output 143 * and 'index.html' for HTML output. 144 * @ant.non-required 145 * @param indexName 146 */ 147 public void setIndexName(String indexName) { 148 this.indexName=indexName; 149 } 150 151 /** 152 * Visibility of engine class. Valid values are "public" (default), and 153 * "" (empty string for default/package visibility). 154 * @ant.non-required 155 * @param engineVisibility 156 */ 157 public void setEngineVisibility(String engineVisibility) { 158 if ("public".equals(engineMethodsVisibility) || "".equals(engineMethodsVisibility)) { 159 this.engineVisibility = engineVisibility; 160 } else { 161 throw new BuildException("Invalid visibility value: '"+engineMethodsVisibility+"'"); 162 } 163 } 164 165 /** 166 * Visibility of interface implementation classes. Valid values are "public" (default), and 167 * "" (empty string for default/package visibility). 168 * @ant.non-required 169 */ 170 public void setInterfaceImplVisibility(String interfaceImplVisibility) { 171 if ("public".equals(engineMethodsVisibility) || "".equals(engineMethodsVisibility)) { 172 this.interfaceImplVisibility = interfaceImplVisibility; 173 } else { 174 throw new BuildException("Invalid visibility value: '"+engineMethodsVisibility+"'"); 175 } 176 } 177 178 /** 179 * Generation policy. Must implement biz.hammurapi.sql.metadata.GenerationPolicy 180 * interface. 181 * @ant.non-required 182 * @return 183 */ 184 public ObjectEntry createGenerationPolicy() { 185 if (generationPolicyEntry==null) { 186 generationPolicyEntry = new ObjectEntry() { 187 protected void validateClass(Class clazz) throws BuildException { 188 super.validateClass(clazz); 189 Class theClass = GenerationPolicy.class; 190 if (!theClass.isAssignableFrom(clazz)) { 191 throw new BuildException(clazz.getName()+" doesn't implement "+theClass.getName()); 192 } 193 } 194 }; 195 return generationPolicyEntry; 196 } 197 198 throw new BuildException("Generation policy already defined"); 199 } 200 201 private SQLProcessor processor; 202 private File dir; 203 private String packageName; 204 private Collection statements=new ArrayList(); 205 private File docDir; 206 private Boolean outputXml; 207 private Collection interfaces=new ArrayList(); 208 private ConnectionEntry connectionEntry; 209 210 private Context nameMap; 211 212 private File script; 213 214 /** 215 * If this attribute is true then: 216 * 1) All queries and updates generated for tables will have table 217 * and schema name enclosed into ${ and } e.g. ${BANK}.${ACCOUNT} 218 * 2) SQLProcessor used to obtain 219 * This attribute creates blank name map, if you need a prepopulated name map use 220 * @ant.non-required 221 * @param useNameMap 222 */ 223 public void setNameMap(boolean useNameMap) { 224 if (nameMap!=null) { 225 throw new BuildException("Name map is already set"); 226 } 227 228 if (useNameMap) { 229 nameMap=new Context() { 230 public Object get(String name) { 231 return null; 232 } 233 }; 234 } else { 235 nameMap=null; 236 } 237 } 238 239 /** 240 * Reads name map from property file. 241 * @ant.non-required 242 */ 243 public void setNameMapFile(File nameMapFile) { 244 if (nameMap!=null) { 245 throw new BuildException("Name map is already set"); 246 } 247 248 try { 249 Properties props=new Properties(); 250 props.load(new FileInputStream(nameMapFile)); 251 nameMap=new MapContext(props); 252 } catch (IOException e) { 253 throw new BuildException("Cannot load name map."); 254 } 255 } 256 257 /** 258 * Interface which generated interfaces shall try to extend. 259 * @ant.non-required 260 * @param entry 261 */ 262 public void addConfiguredInterface(InterfaceEntry entry) { 263 interfaces.add(entry.getInterface()); 264 } 265 266 /** 267 * If true documentation will be generated in XML format for 268 * further styling. 269 * @ant.non-required 270 */ 271 public void setXmlDoc(boolean xmlDoc) { 272 outputXml=xmlDoc ? Boolean.TRUE : Boolean.FALSE; 273 } 274 275 /** 276 * Database connection. 277 * @ant.non-required 278 * @param ce 279 */ 280 public void addConnection(ConnectionEntry ce) { 281 if (processor==null && connectionEntry==null) { 282 this.connectionEntry=ce; 283 } else { 284 throw new BuildException("Either script or connection has already been set"); 285 } 286 } 287 288 /** 289 * DDL script file. Statements shall be separated by semicolons. 290 * If this attribute is set then in-memory Hypersonic database is created, 291 * gets populated using script and query is compiled against this database. 292 * If neither connection nor script is specified the uninitialized Hypersonic 293 * in-memory database will be used. It can be useful if several sqlc tasks 294 * are executed in a row against the same database. 295 * @ant.non-required 296 * @param script 297 */ 298 public void setScript(File script) { 299 if (connectionEntry==null) { 300 this.script=script; 301 } else { 302 throw new BuildException("Connection has already been set"); 303 } 304 } 305 306 public void addConfiguredScript(ScriptEntry scriptEntry) { 307 if (connectionEntry==null) { 308 try { 309 if (processor==null) { 310 processor=new SQLProcessor(new HypersonicInMemoryDataSource((Reader) null), nameMap); 311 } 312 processor.executeScript(scriptEntry.getScript()); 313 } catch (ClassNotFoundException e) { 314 throw new BuildException("Hypersonic database driver is not found", e); 315 } catch (IOException e) { 316 throw new BuildException("Cannot read script", e); 317 } catch (SQLException e) { 318 throw new BuildException("Cannot execute script", e); 319 } 320 } else { 321 throw new BuildException("Connection has already been set"); 322 } 323 } 324 325 /** 326 * Directory to output compiled classes 327 * @ant.required 328 * @param dir 329 */ 330 public void setDir(File dir) { 331 if (dir.exists() && dir.isDirectory()) { 332 this.dir=dir; 333 } else { 334 throw new BuildException(dir.getAbsolutePath()+" does not exist or is not a directory"); 335 } 336 } 337 338 /** 339 * Package for generated classes 340 * @ant.required 341 * @param packageName 342 */ 343 public void setPackage(String packageName) { 344 this.packageName=packageName; 345 } 346 347 /** 348 * Query to compile 349 * @ant.non-required 350 * @param query 351 */ 352 public void addQuery(QueryEntry query) { 353 query.setTask(this); 354 statements.add(query); 355 } 356 357 /** 358 * Query to compile 359 * @ant.non-required 360 * @param update 361 */ 362 public void addUpdate(UpdateEntry update) { 363 update.setTask(this); 364 statements.add(update); 365 } 366 367 /** 368 * Statements xml file. Top element should be 'statements', containing 'query' and 'update' elements which 369 * have the same format as nested 'query' and 'update' elements. 370 * @ant.non-required 371 * @param queries 372 */ 373 public void addConfiguredStatements(StatementsEntry queries) { 374 queries.setTask(this); 375 queries.getStatements(this.statements, this.tableEntries); 376 } 377 378 /** 379 * Statements from the database. 380 * @ant.non-required 381 * @param queries 382 */ 383 public void addConfiguredDbStatements(DbStatementsEntry queries) { 384 queries.getStatements(getProcessor(), this, this.statements); 385 } 386 387 private String masterEngineName; 388 private Collection tableEntries=new ArrayList(); 389 // private String statementQuery; 390 391 392 // /** 393 // * Query to execute to retrieve a statement by name. If this property is set then 394 // * statement SQL will not be hardcoded into generated engines but retrieved from the database 395 // * when engine is instantiated. This gives flexibility to modify query, e.g. the way tables 396 // * are joined, use nested selects instead of joins, without recompiling and redeploying 397 // * the application. Of course statements shall be parameter and column compatible. 398 // * The query take one parameter of type String and shall return one column of type String with 399 // * statement's SQL. 400 // * @ant.non-required 401 // * @param queriesQuery 402 // */ 403 // public void setStatementQuery(String statementQuery) { 404 // this.statementQuery=statementQuery; 405 // } 406 407 public void execute() throws BuildException { 408 getProcessor(); 409 410 try { 411 if (dir==null) { 412 if (parent==null) { 413 throw new BuildException("Output dir is not set"); 414 } 415 416 dir=parent.dir; 417 if (dir==null) { 418 throw new BuildException("Output dir is not set and parent dir is not set"); 419 } 420 } 421 422 if (parent!=null) { 423 if (generationPolicyEntry==null) { 424 generationPolicyEntry=parent.generationPolicyEntry; 425 } 426 427 if (docDir==null) { 428 docDir=parent.docDir; 429 } 430 431 if (outputXml==null) { 432 outputXml=parent.outputXml; 433 } 434 435 if (useSqlTypes==null) { 436 useSqlTypes=parent.useSqlTypes; 437 } 438 439 interfaces.addAll(parent.interfaces); 440 441 Iterator git=parent.generatedInterfaces.iterator(); 442 while (git.hasNext()) { 443 String iName = (String) git.next(); 444 log("Adding inherited interface: "+iName, Project.MSG_VERBOSE); 445 InterfaceEntry ie=new InterfaceEntry(); 446 ie.setClassLoader(parent.getClass().getClassLoader()); 447 ie.setProject(getProject()); 448 ie.setName(iName); 449 ie.createClasspath().setLocation(parent.dir); 450 addConfiguredInterface(ie); 451 } 452 453 if (inheritMetadata && metadataFile==null && parent!=null && parent.metadataFile!=null) { 454 metadataFile=parent.metadataFile; 455 } 456 } 457 458 DocumentingConsumer consumer; 459 if (Boolean.TRUE.equals(outputXml)) { 460 consumer = new XmlDocConsumer(dir, docDir, indexName==null ? "index.xml" : indexName) { 461 public void consume(JavaClass javaClass) throws GenerationException { 462 super.consume(javaClass); 463 if (javaClass.isInterface()) { 464 generatedInterfaces.add(javaClass.getClassName()); 465 } 466 log(javaClass.getClassName(), Project.MSG_VERBOSE); 467 } 468 }; 469 } else { 470 consumer = new HtmlDocConsumer(dir, docDir, indexName==null ? "index.xml" : indexName) { 471 public void consume(JavaClass javaClass) throws GenerationException { 472 super.consume(javaClass); 473 if (javaClass.isInterface()) { 474 generatedInterfaces.add(javaClass.getClassName()); 475 } 476 log(javaClass.getClassName(), Project.MSG_VERBOSE); 477 } 478 }; 479 } 480 481 Collection namedStatements=new ArrayList(); 482 483 GenerationPolicy generationPolicy; 484 if (generationPolicyEntry==null) { 485 generationPolicy = new DefaultGenerationPolicy() { 486 { 487 packageName=""; 488 } 489 }; 490 } else { 491 generationPolicy=(GenerationPolicy) generationPolicyEntry.getObject(this.getClass().getClassLoader()); 492 } 493 494 if (!tableEntries.isEmpty()) { 495 log("Processing table entries", Project.MSG_VERBOSE); 496 final Map teMap=new HashMap(); 497 498 Metadata metadata; 499 500 if (this.metadataFile==null) { 501 metadata=new Metadata( 502 processor, 503 new String[] {"TABLE", "VIEW"}, 504 generationPolicy, 505 new TableAcceptor() { 506 public boolean accept(String catalog, String schema, String table) { 507 Iterator it=tableEntries.iterator(); 508 while (it.hasNext()) { 509 TableEntry tableEntry = (TableEntry) it.next(); 510 Object key = Metadata.toKey(catalog, schema, table); 511 if (tableEntry.accept(catalog, schema, table)) { 512 teMap.put(key, tableEntry); 513 log("Accepted: "+key, Project.MSG_VERBOSE); 514 return true; 515 } 516 517 log("Rejected: "+key, Project.MSG_VERBOSE); 518 } 519 return false; 520 } 521 }); 522 } else { 523 metadata = Metadata.load(metadataFile); 524 Iterator tdit=metadata.getTableDescriptors().iterator(); 525 Z: 526 while (tdit.hasNext()) { 527 TableDescriptor td=(TableDescriptor) tdit.next(); 528 Iterator it=tableEntries.iterator(); 529 while (it.hasNext()) { 530 TableEntry tableEntry = (TableEntry) it.next(); 531 Object key = Metadata.toKey(td.getCatalog(), td.getSchema(), td.getTableName()); 532 if (tableEntry.accept(td.getCatalog(), td.getSchema(), td.getTableName())) { 533 teMap.put(key, tableEntry); 534 log("Accepted: "+key, Project.MSG_VERBOSE); 535 continue Z; 536 } 537 538 log("Rejected: "+key, Project.MSG_VERBOSE); 539 } 540 //tdit.remove(); 541 } 542 } 543 544 Iterator wit = metadata.getWarnings().iterator(); 545 while (wit.hasNext()) { 546 log((String) wit.next(), Project.MSG_WARN); 547 } 548 549 Iterator tdit=metadata.getTableDescriptors().iterator(); 550 while (tdit.hasNext()) { 551 TableDescriptor td=(TableDescriptor) tdit.next(); 552 TableEntry te=(TableEntry) teMap.get(Metadata.toKey(td.getCatalog(), td.getSchema(), td.getTableName())); 553 if (te==null) { 554 log("Table entry not found for "+td.getName(), Project.MSG_VERBOSE); 555 continue; 556 } 557 558 log("Processing table "+td.getName(), Project.MSG_VERBOSE); 559 560 String tableName = te.isQualifiedTableName() ? td.getName() : td.getTableName(); 561 if (nameMap!=null) { 562 tableName="${"+tableName+"}"; 563 } 564 565 boolean generateMutators=te.getGenerateMutators(); 566 // Select all 567 String entityType = te.isQualifiedMethodName() ? td.getEntityType() : DefaultGenerationPolicy.convert(td.getTableName(), "_"); 568 String methodNamePostfix = entityType.indexOf('.')==-1 ? entityType : "_"+entityType.replace('.', '_'); 569 570 // Delete all 571 NamedUpdate namedUpdate = new NamedUpdate("Delete"+methodNamePostfix, "Deletes all records from "+tableName, "DELETE FROM "+tableName, null, null); 572 namedUpdate.setUseSqlTypes(te.getUseSqlTypes()); 573 namedStatements.add(injectVisibility(namedUpdate)); 574 575 NamedQuery selectAll = injectVisibility(new NamedQuery(methodNamePostfix, "Selects all rows from "+tableName, false, "SELECT * FROM "+tableName, null, null, td.getColumnDescriptors(), generateMutators)); 576 selectAll.setUseSqlTypes(te.getUseSqlTypes()); 577 te.setColTypes(selectAll); 578 if (te.getSmartBase()!=null) { 579 selectAll.setSmartBaseName(te.getSmartBase()); 580 } 581 namedStatements.add(selectAll); 582 583 // By primary key 584 StringBuffer sb=new StringBuffer(); 585 StringBuffer icsb=new StringBuffer(); 586 StringBuffer ivsb=new StringBuffer(); 587 Collection parameters=new ArrayList(); 588 sb.append(tableName); 589 sb.append(" WHERE "); 590 boolean addAnd=false; 591 Iterator cit=td.getColumnDescriptors().iterator(); 592 while (cit.hasNext()) { 593 ColumnDescriptor cd=(ColumnDescriptor) cit.next(); 594 icsb.append(cd.getDbName()); 595 ivsb.append("?"); 596 if (cit.hasNext()) { 597 icsb.append(","); 598 ivsb.append(","); 599 } 600 601 if (cd.isPrimaryKey()) { 602 if (addAnd) { 603 sb.append(" AND "); 604 } 605 parameters.add(cd); 606 sb.append(cd.getDbName()); 607 sb.append("=?"); 608 addAnd=true; 609 } 610 } 611 612 if (!parameters.isEmpty()) { 613 NamedQuery select = injectVisibility(new NamedQuery(methodNamePostfix, "Selects by primary key from "+tableName, true, "SELECT * FROM "+sb.toString(), parameters, null, td.getColumnDescriptors(), generateMutators)); 614 select.setUseSqlTypes(te.getUseSqlTypes()); 615 te.setColTypes(select); 616 setParameterTypes(te, parameters, select); 617 if (te.getSmartBase()!=null) { 618 select.setSmartBaseName(te.getSmartBase()); 619 } 620 namedStatements.add(select); 621 622 // Delete 623 NamedUpdate delete = new NamedUpdate("Delete"+methodNamePostfix, "Deletes by primary key from "+tableName, "DELETE FROM "+sb.toString(), parameters, null); 624 delete.setUseSqlTypes(te.getUseSqlTypes()); 625 setParameterTypes(te, parameters, delete); 626 namedStatements.add(injectVisibility(delete)); 627 628 generateUpdate(namedStatements, td, methodNamePostfix, parameters, te); 629 630 } 631 632 // Insert 633 if (te.isParamPerColumnInsert()) { 634 NamedUpdate insert = new NamedUpdate("Insert"+methodNamePostfix, "Inserts new record into "+tableName, "INSERT INTO "+tableName+" ("+icsb+") VALUES ("+ivsb+")", td.getColumnDescriptors(), null); 635 insert.setUseSqlTypes(te.getUseSqlTypes()); 636 setParameterTypes(te, td.getColumnDescriptors(), insert); 637 namedStatements.add(injectVisibility(insert)); 638 } 639 640 if (isToBeGenerated(td,te)) { 641 NamedParameterObjectUpdate poInsert = new NamedParameterObjectUpdate( 642 "Insert"+methodNamePostfix, "Inserts new record into "+tableName, 643 "INSERT INTO "+tableName+" ("+icsb+") VALUES ("+ivsb+")", 644 td.getColumnDescriptors(), 645 null, 646 generateMutators); 647 poInsert.setMode(NamedInterfaceGeneratingStatement.MODE_INSERT); 648 poInsert.setTableName(tableName); 649 poInsert.setUseSqlTypes(te.getUseSqlTypes()); 650 if (te.getSmartBase()!=null) { 651 poInsert.setSmartBaseName(te.getSmartBase()); 652 } 653 setParameterTypes(te, td.getColumnDescriptors(), poInsert); 654 te.setColTypes(poInsert); 655 namedStatements.add(injectVisibility(poInsert)); 656 } 657 658 generateIndices(namedStatements, td, te, tableName, methodNamePostfix); 659 generateImportedKeys(namedStatements, td, te, tableName, methodNamePostfix); 660 } 661 } 662 663 Iterator it=statements.iterator(); 664 while (it.hasNext()) { 665 StatementEntry statementEntry = (StatementEntry) it.next(); 666 log("Processing "+statementEntry.name, Project.MSG_VERBOSE); 667 namedStatements.add(statementEntry.getStatement(processor, generationPolicy)); 668 } 669 670 // if (useSqlTypes!=null) { 671 // Iterator sit=namedStatements.iterator(); 672 // while (sit.hasNext()) { 673 // ((NamedStatement) sit.next()).setUseSqlTypes(useSqlTypes.booleanValue()); 674 // } 675 // } 676 677 NamedStatement.generate(packageName, masterEngineName, engineVisibility, engineMethodsVisibility, namedStatements, interfaces, consumer); 678 679 consumer.close(); 680 // Collection verifyErrors=ClassGeneratorBase.verify(toVerify, null); 681 // it=verifyErrors.iterator(); 682 // while (it.hasNext()) { 683 // log((String) it.next(), Project.MSG_WARN); 684 // } 685 } catch (GenerationException e) { 686 e.printStackTrace(); 687 throw new BuildException("Could not generate: "+e,e); 688 } catch (SQLException e) { 689 // e.printStackTrace(); 690 throw new BuildException("Could not read query metadata: "+e,e); 691 // } catch (Exception e) { 692 // e.printStackTrace(); 693 } catch (IOException e) { 694 throw new BuildException("Could not load metadata file: "+e,e); 695 } catch (ClassNotFoundException e) { 696 throw new BuildException("Could not load metadata file: "+e,e); 697 } 698 699 Iterator it=subTasks.iterator(); 700 while (it.hasNext()) { 701 ((Task) it.next()).execute(); 702 } 703 704 if (connectionEntry!=null) { 705 connectionEntry.shutdown(); 706 } 707 } 708 709 /** 710 * @param namedStatements 711 * @param td 712 * @param te 713 * @param tableName 714 * @param generateMutators 715 * @param methodNamePostfix 716 * @throws GenerationException 717 */ 718 private void generateIndices(Collection namedStatements, TableDescriptor td, TableEntry te, String tableName, String methodNamePostfix) throws GenerationException { 719 boolean generateMutators=te.getGenerateMutators(); 720 // Process indices 721 Iterator it=td.getIndices().iterator(); 722 while (it.hasNext()) { 723 IndexDescriptor id=(IndexDescriptor) it.next(); 724 if (id.getInfo()!=null) { 725 String columnList = getColumnList(id); 726 727 if (id.getInfo().isEQ()) { 728 StringBuffer isb=new StringBuffer(tableName); 729 isb.append(" WHERE "); 730 Iterator icit=id.getColumns().iterator(); 731 while (icit.hasNext()) { 732 KeyEntry ke=(KeyEntry) icit.next(); 733 isb.append(" "); 734 isb.append(ke.getColumnName()); 735 isb.append("=?"); 736 if (icit.hasNext()) { 737 isb.append(" AND "); 738 } 739 } 740 741 NamedUpdate deleteByIndex = new NamedUpdate( 742 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"EQ", 743 "Deletes row(s) with equal index value(s): "+columnList, 744 "DELETE FROM "+isb.toString(), 745 id.getColumns(), 746 null); 747 setParameterTypes(te, id.getColumns(), deleteByIndex); 748 deleteByIndex.setUseSqlTypes(te.getUseSqlTypes()); 749 namedStatements.add(injectVisibility(deleteByIndex)); 750 751 NamedQuery selectByIndex = new NamedQuery( 752 methodNamePostfix+id.getInfo().getJavaName()+"EQ", 753 "Selects row(s) with equal index value(s): "+columnList, 754 id.isUnique(), 755 "SELECT * FROM "+isb.toString(), 756 id.getColumns(), 757 null, 758 td.getColumnDescriptors(), 759 generateMutators); 760 setParameterTypes(te, id.getColumns(), selectByIndex); 761 te.setColTypes(selectByIndex); 762 if (te.getSmartBase()!=null) { 763 selectByIndex.setSmartBaseName(te.getSmartBase()); 764 } 765 selectByIndex.setUseSqlTypes(te.getUseSqlTypes()); 766 namedStatements.add(injectVisibility(selectByIndex)); 767 } 768 769 if (id.getInfo().isNE()) { 770 StringBuffer isb=new StringBuffer(tableName); 771 isb.append(" WHERE "); 772 Iterator icit=id.getColumns().iterator(); 773 while (icit.hasNext()) { 774 KeyEntry ke=(KeyEntry) icit.next(); 775 isb.append(" "); 776 isb.append(ke.getColumnName()); 777 isb.append("=?"); 778 if (icit.hasNext()) { 779 isb.append(" AND "); 780 } 781 } 782 783 NamedUpdate dbx = new NamedUpdate( 784 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"NE", 785 "Deletes row(s) with not equal index value(s): "+columnList, 786 "DELETE FROM "+isb.toString(), 787 id.getColumns(), 788 null); 789 setParameterTypes(te, id.getColumns(), dbx); 790 dbx.setUseSqlTypes(te.getUseSqlTypes()); 791 namedStatements.add(injectVisibility(dbx)); 792 793 NamedQuery sbx = new NamedQuery( 794 methodNamePostfix+id.getInfo().getJavaName()+"NE", 795 "Selects row(s) with not equal index value(s): "+columnList, 796 false, 797 "SELECT * FROM "+isb.toString(), 798 id.getColumns(), 799 null, 800 td.getColumnDescriptors(), 801 generateMutators); 802 setParameterTypes(te, id.getColumns(), sbx); 803 te.setColTypes(sbx); 804 sbx.setUseSqlTypes(te.getUseSqlTypes()); 805 if (te.getSmartBase()!=null) { 806 sbx.setSmartBaseName(te.getSmartBase()); 807 } 808 namedStatements.add(injectVisibility(sbx)); 809 } 810 811 if (id.getInfo().isGE()) { 812 StringBuffer isb=new StringBuffer(tableName); 813 isb.append(" WHERE "); 814 String operator=">"; 815 String descOperator="<"; 816 String lastOperator=">="; 817 String lastDescOperator="<="; 818 819 Collection parameters2 = generateComparison(id, isb, operator, descOperator, lastOperator, lastDescOperator); 820 NamedUpdate dbx = new NamedUpdate( 821 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"GE", 822 "Deletes row(s) with greater or equal index value(s): "+columnList, 823 "DELETE FROM "+isb.toString(), 824 id.getColumns(), 825 parameters2); 826 setParameterTypes(te, id.getColumns(), dbx); 827 dbx.setUseSqlTypes(te.getUseSqlTypes()); 828 namedStatements.add(injectVisibility(dbx)); 829 830 generateOrderBy(id, isb); 831 832 NamedQuery sbx = new NamedQuery( 833 methodNamePostfix+id.getInfo().getJavaName()+"GE", 834 "Selects row(s) with greater or equal index value(s) ordered by insex. Columns: "+columnList, 835 false, 836 "SELECT * FROM "+isb.toString(), 837 id.getColumns(), 838 parameters2, 839 td.getColumnDescriptors(), 840 generateMutators); 841 setParameterTypes(te, id.getColumns(), sbx); 842 te.setColTypes(sbx); 843 sbx.setUseSqlTypes(te.getUseSqlTypes()); 844 if (te.getSmartBase()!=null) { 845 sbx.setSmartBaseName(te.getSmartBase()); 846 } 847 namedStatements.add(injectVisibility(sbx)); 848 } 849 850 if (id.getInfo().isGT()) { 851 StringBuffer isb=new StringBuffer(tableName); 852 isb.append(" WHERE "); 853 String operator=">"; 854 String descOperator="<"; 855 String lastOperator=">"; 856 String lastDescOperator="<"; 857 858 Collection parameters2 = generateComparison(id, isb, operator, descOperator, lastOperator, lastDescOperator); 859 860 NamedUpdate dbx = new NamedUpdate( 861 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"GT", 862 "Deletes row(s) with greater index value(s): "+columnList, 863 "DELETE FROM "+isb.toString(), 864 id.getColumns(), 865 parameters2); 866 setParameterTypes(te, id.getColumns(), dbx); 867 dbx.setUseSqlTypes(te.getUseSqlTypes()); 868 namedStatements.add(injectVisibility(dbx)); 869 870 generateOrderBy(id, isb); 871 872 NamedQuery sbx = new NamedQuery( 873 methodNamePostfix+id.getInfo().getJavaName()+"GT", 874 "Selects row(s) with greater index value(s), ordered by index. Columns: "+columnList, 875 false, 876 "SELECT * FROM "+isb.toString(), 877 id.getColumns(), 878 parameters2, 879 td.getColumnDescriptors(), 880 generateMutators); 881 setParameterTypes(te, id.getColumns(), sbx); 882 te.setColTypes(sbx); 883 sbx.setUseSqlTypes(te.getUseSqlTypes()); 884 if (te.getSmartBase()!=null) { 885 sbx.setSmartBaseName(te.getSmartBase()); 886 } 887 namedStatements.add(injectVisibility(sbx)); 888 } 889 890 if (id.getInfo().isLE()) { 891 StringBuffer isb=new StringBuffer(tableName); 892 isb.append(" WHERE "); 893 String operator="<"; 894 String descOperator=">"; 895 String lastOperator="<="; 896 String lastDescOperator=">="; 897 898 Collection parameters2 = generateComparison(id, isb, operator, descOperator, lastOperator, lastDescOperator); 899 NamedUpdate dbx = new NamedUpdate( 900 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"LE", 901 "Deletes row(s) with less or equal index value(s): "+columnList, 902 "DELETE FROM "+isb.toString(), 903 id.getColumns(), 904 parameters2); 905 setParameterTypes(te, id.getColumns(), dbx); 906 dbx.setUseSqlTypes(te.getUseSqlTypes()); 907 namedStatements.add(injectVisibility(dbx)); 908 909 generateOrderBy(id, isb); 910 911 NamedQuery sbx = new NamedQuery( 912 methodNamePostfix+id.getInfo().getJavaName()+"LE", 913 "Selects row(s) with less or equal index value(s) ordered by index. Columns: "+columnList, 914 false, 915 "SELECT * FROM "+isb.toString(), 916 id.getColumns(), 917 parameters2, 918 td.getColumnDescriptors(), 919 generateMutators); 920 setParameterTypes(te, id.getColumns(), sbx); 921 te.setColTypes(sbx); 922 sbx.setUseSqlTypes(te.getUseSqlTypes()); 923 if (te.getSmartBase()!=null) { 924 sbx.setSmartBaseName(te.getSmartBase()); 925 } 926 namedStatements.add(injectVisibility(sbx)); 927 } 928 929 if (id.getInfo().isLT()) { 930 StringBuffer isb=new StringBuffer(tableName); 931 isb.append(" WHERE "); 932 String operator="<"; 933 String descOperator=">"; 934 String lastOperator="<"; 935 String lastDescOperator=">"; 936 937 Collection parameters2 = generateComparison(id, isb, operator, descOperator, lastOperator, lastDescOperator); 938 939 NamedUpdate dbx = new NamedUpdate( 940 "Delete"+methodNamePostfix+id.getInfo().getJavaName()+"LT", 941 "Deletes row(s) with less index value(s): "+columnList, 942 "DELETE FROM "+isb.toString(), 943 id.getColumns(), 944 parameters2); 945 setParameterTypes(te, id.getColumns(), dbx); 946 dbx.setUseSqlTypes(te.getUseSqlTypes()); 947 namedStatements.add(injectVisibility(dbx)); 948 949 generateOrderBy(id, isb); 950 951 NamedQuery sbx = new NamedQuery( 952 methodNamePostfix+id.getInfo().getJavaName()+"LT", 953 "Selects row(s) with less index value(s) ordered by index. Columns: "+columnList, 954 false, 955 "SELECT * FROM "+isb.toString(), 956 id.getColumns(), 957 parameters2, 958 td.getColumnDescriptors(), 959 generateMutators); 960 setParameterTypes(te, id.getColumns(), sbx); 961 te.setColTypes(sbx); 962 sbx.setUseSqlTypes(te.getUseSqlTypes()); 963 if (te.getSmartBase()!=null) { 964 sbx.setSmartBaseName(te.getSmartBase()); 965 } 966 namedStatements.add(injectVisibility(sbx)); 967 } 968 969 if (id.getInfo().isOrdered()) { 970 StringBuffer osb=new StringBuffer(tableName); 971 generateOrderBy(id, osb); 972 973 NamedQuery namedQuery = new NamedQuery( 974 methodNamePostfix+id.getInfo().getJavaName()+"Ordered", 975 "Selects ordered by index: "+columnList, 976 false, 977 "SELECT * FROM "+osb.toString(), 978 null, 979 null, 980 td.getColumnDescriptors(), 981 generateMutators); 982 983 namedQuery.setUseSqlTypes(te.getUseSqlTypes()); 984 if (te.getSmartBase()!=null) { 985 namedQuery.setSmartBaseName(te.getSmartBase()); 986 } 987 988 namedStatements.add(injectVisibility(namedQuery)); 989 } 990 } 991 } 992 } 993 994 private Collection subTasks=new ArrayList(); 995 996 /** 997 * Subtask. Inherits output directory, documentation directory, 998 * database connection, skipIndices attribute, outputXml attribute 999 * and injected/generated interfaces from the parent task. 1000 * @ant.non-required 1001 * @return 1002 */ 1003 public StatementCompilerTask createSqlc() { 1004 StatementCompilerTask ret = new StatementCompilerTask(this); 1005 subTasks.add(ret); 1006 return ret; 1007 } 1008 1009 /** 1010 * @param namedStatements 1011 * @param td 1012 * @param te 1013 * @param tableName 1014 * @param generateMutators 1015 * @param methodNamePostfix 1016 * @throws GenerationException 1017 */ 1018 private void generateImportedKeys(Collection namedStatements, TableDescriptor td, TableEntry te, String tableName, String methodNamePostfix) throws GenerationException { 1019 boolean generateMutators=te.getGenerateMutators(); 1020 Iterator it=td.getImportedKeys().values().iterator(); 1021 while (it.hasNext()) { 1022 KeyDescriptor kd=(KeyDescriptor) it.next(); 1023 if (kd.getName()!=null) { 1024 String columnList = getColumnList(kd); 1025 1026 StringBuffer fksb=new StringBuffer(tableName); 1027 fksb.append(" WHERE "); 1028 Iterator icit=kd.getColumns().iterator(); 1029 while (icit.hasNext()) { 1030 KeyEntry ke=(KeyEntry) icit.next(); 1031 fksb.append(" "); 1032 fksb.append(ke.getColumnName()); 1033 fksb.append("=?"); 1034 if (icit.hasNext()) { 1035 fksb.append(" AND "); 1036 } 1037 } 1038 1039 NamedQuery selectByFk = new NamedQuery( 1040 methodNamePostfix+"By"+kd.getName(), 1041 "Selects rows from "+tableName+" by foreign key "+kd.getDbName()+" columns value(s): "+columnList, 1042 false, 1043 "SELECT * FROM "+fksb.toString(), 1044 kd.getColumns(), 1045 null, 1046 td.getColumnDescriptors(), 1047 generateMutators); 1048 setParameterTypes(te, kd.getColumns(), selectByFk); 1049 te.setColTypes(selectByFk); 1050 selectByFk.setUseSqlTypes(te.getUseSqlTypes()); 1051 namedStatements.add(injectVisibility(selectByFk)); 1052 if (te.getSmartBase()!=null) { 1053 selectByFk.setSmartBaseName(te.getSmartBase()); 1054 } 1055 1056 NamedUpdate deleteByFk = new NamedUpdate( 1057 "Delete"+methodNamePostfix+"By"+kd.getName(), 1058 "Deletes rows from "+tableName+" by foreign key "+kd.getDbName()+" columns value(s): "+columnList, 1059 "DELETE FROM "+fksb.toString(), 1060 kd.getColumns(), 1061 null); 1062 setParameterTypes(te, kd.getColumns(), deleteByFk); 1063 deleteByFk.setUseSqlTypes(te.getUseSqlTypes()); 1064 namedStatements.add(injectVisibility(deleteByFk)); 1065 1066 1067 } 1068 } 1069 } 1070 1071 /** 1072 * @param namedStatement 1073 */ 1074 private NamedStatement injectVisibility(NamedStatement namedStatement) { 1075 namedStatement.setEngineMethodsVisibility(engineMethodsVisibility); 1076 namedStatement.setEngineVisibility(engineVisibility); 1077 return namedStatement; 1078 } 1079 1080 /** 1081 * @param namedQuery 1082 */ 1083 private NamedQuery injectVisibility(NamedQuery namedQuery) { 1084 injectVisibility((NamedStatement) namedQuery); 1085 namedQuery.setInterfaceImplVisibility(interfaceImplVisibility); 1086 return namedQuery; 1087 } 1088 1089 /** 1090 * 1091 */ 1092 private SQLProcessor getProcessor() { 1093 if (processor==null) { 1094 if (script!=null) { 1095 try { 1096 processor=new SQLProcessor(new HypersonicInMemoryDataSource((Reader) null), nameMap); 1097 processor.executeScript(new FileReader(script)); 1098 } catch (ClassNotFoundException e) { 1099 throw new BuildException("Hypersonic database driver is not found", e); 1100 } catch (IOException e) { 1101 throw new BuildException("Cannot read script", e); 1102 } catch (SQLException e) { 1103 throw new BuildException("Cannot execute script", e); 1104 } 1105 } else if (connectionEntry!=null) { 1106 processor=connectionEntry.getProcessor(nameMap); 1107 } else if (parent!=null) { 1108 processor=parent.getProcessor(); 1109 } else { 1110 try { 1111 processor=new SQLProcessor(new HypersonicInMemoryDataSource((Reader) null), nameMap); 1112 } catch (ClassNotFoundException e) { 1113 throw new BuildException("Hypersonic driver is not found in the classpath", e); 1114 } catch (IOException e) { 1115 throw new BuildException("Hypersonic in-memory database cannot be created", e); 1116 } catch (SQLException e) { 1117 throw new BuildException("Hypersonic in-memory database cannot be created", e); 1118 } 1119 } 1120 } 1121 1122 return processor; 1123 } 1124 1125 /** 1126 * @param id 1127 * @return 1128 */ 1129 private String getColumnList(IndexDescriptor id) { 1130 StringBuffer clb=new StringBuffer(); 1131 Iterator iicit=id.getColumns().iterator(); 1132 while (iicit.hasNext()) { 1133 KeyEntry ke=(KeyEntry) iicit.next(); 1134 clb.append(ke.getColumnName()); 1135 if (ke.isDescending()) { 1136 clb.append(" (DESC)"); 1137 } 1138 if (iicit.hasNext()) { 1139 clb.append(", "); 1140 } 1141 } 1142 1143 String columnList=clb.toString(); 1144 return columnList; 1145 } 1146 1147 /** 1148 * @param kd 1149 * @return 1150 */ 1151 private String getColumnList(KeyDescriptor kd) { 1152 StringBuffer clb=new StringBuffer(); 1153 Iterator iicit=kd.getColumns().iterator(); 1154 while (iicit.hasNext()) { 1155 KeyEntry ke=(KeyEntry) iicit.next(); 1156 clb.append(ke.getColumnName()); 1157 if (ke.isDescending()) { 1158 clb.append(" (DESC)"); 1159 } 1160 if (iicit.hasNext()) { 1161 clb.append(", "); 1162 } 1163 } 1164 1165 String columnList=clb.toString(); 1166 return columnList; 1167 } 1168 1169 /** 1170 * @param te 1171 * @param parameters 1172 * @param statement 1173 * @throws GenerationException 1174 */ 1175 private void setParameterTypes(final TableEntry te, Collection parameters, NamedStatement statement) throws GenerationException { 1176 Iterator it=parameters.iterator(); 1177 for (int k=1; it.hasNext(); k++) { 1178 Object next = it.next(); 1179 if (next instanceof KeyEntry) { 1180 statement.setParamType(k, te.getColType((KeyEntry) next)); 1181 } else if (next instanceof ColumnDescriptor) { 1182 statement.setParamType(k, te.getColType((ColumnDescriptor) next)); 1183 } else { 1184 throw new GenerationException("Unexpected type: "+next.getClass()); 1185 } 1186 } 1187 } 1188 1189 /** 1190 * @param namedStatements 1191 * @param td 1192 * @param entityType 1193 * @param parameters 1194 * @param te 1195 * @throws GenerationException 1196 */ 1197 private void generateUpdate(Collection namedStatements, TableDescriptor td, String methodNamePostfix, Collection parameters, TableEntry te) throws GenerationException { 1198 // Update 1199 if (columnsCount(td,te)>parameters.size()) { 1200 StringBuffer usb=new StringBuffer("UPDATE "); 1201 usb.append(td.getName()); 1202 usb.append(" SET "); 1203 1204 Collection uprm=new ArrayList(); 1205 Iterator uit=td.getColumnDescriptors().iterator(); 1206 while (uit.hasNext()) { 1207 ColumnDescriptor cd=(ColumnDescriptor) uit.next(); 1208 if (!cd.isPrimaryKey()) { 1209 usb.append(cd.getDbName()); 1210 usb.append("=?"); 1211 if (uit.hasNext()) { 1212 usb.append(", "); 1213 } 1214 uprm.add(cd); 1215 } 1216 } 1217 1218 usb.append(" WHERE "); 1219 1220 uit=td.getColumnDescriptors().iterator(); 1221 boolean addAnd=false; 1222 while (uit.hasNext()) { 1223 ColumnDescriptor cd=(ColumnDescriptor) uit.next(); 1224 if (cd.isPrimaryKey()) { 1225 if (addAnd) { 1226 usb.append(" AND "); 1227 } 1228 usb.append(cd.getDbName()); 1229 usb.append("=?"); 1230 uprm.add(cd); 1231 addAnd=true; 1232 } 1233 } 1234 1235 NamedParameterObjectUpdate update = new NamedParameterObjectUpdate( 1236 "Update"+methodNamePostfix, "Updates record with by primary key in "+td.getName(), 1237 usb.toString(), 1238 uprm, 1239 null, 1240 te.getGenerateMutators()); 1241 1242 update.setMode(NamedInterfaceGeneratingStatement.MODE_UPDATE); 1243 update.setTableName(td.getName()); 1244 update.setUseSqlTypes(te.getUseSqlTypes()); 1245 if (te.getSmartBase()!=null) { 1246 update.setSmartBaseName(te.getSmartBase()); 1247 } 1248 1249 setParameterTypes(te, uprm, update); 1250 te.setColTypes(update); 1251 1252 namedStatements.add(injectVisibility(update)); 1253 } 1254 } 1255 1256 /** 1257 * @param id 1258 * @param isb 1259 * @param operator 1260 * @param descOperator 1261 * @param lastOperator 1262 * @param lastDescOperator 1263 * @return 1264 */ 1265 private Collection generateComparison(IndexDescriptor id, StringBuffer isb, String operator, String descOperator, String lastOperator, String lastDescOperator) { 1266 int pcounter=0; 1267 Collection parameters2=new ArrayList(); 1268 Iterator icit=id.getColumns().iterator(); 1269 while (icit.hasNext()) { 1270 KeyEntry ke=(KeyEntry) icit.next(); 1271 isb.append(" "); 1272 isb.append(ke.getColumnName()); 1273 if (icit.hasNext()) { 1274 isb.append(ke.isDescending() ? descOperator : operator); 1275 } else { 1276 isb.append(ke.isDescending() ? lastDescOperator : lastOperator); 1277 } 1278 isb.append("?"); 1279 parameters2.add(ke); 1280 if (icit.hasNext()) { 1281 pcounter++; 1282 isb.append(" OR ("); 1283 isb.append(ke.getColumnName()); 1284 isb.append("=? AND ("); 1285 parameters2.add(ke); 1286 } 1287 } 1288 1289 while (pcounter-->0) { 1290 isb.append("))"); 1291 } 1292 return parameters2; 1293 } 1294 1295 /** 1296 * @param id 1297 * @param osb 1298 */ 1299 private void generateOrderBy(IndexDescriptor id, StringBuffer osb) { 1300 osb.append(" ORDER BY "); 1301 Iterator icit=id.getColumns().iterator(); 1302 while (icit.hasNext()) { 1303 KeyEntry ke=(KeyEntry) icit.next(); 1304 osb.append(" "); 1305 osb.append(ke.getColumnName()); 1306 if (ke.isDescending()) { 1307 osb.append(" DESC"); 1308 } 1309 if (icit.hasNext()) { 1310 osb.append(", "); 1311 } 1312 } 1313 } 1314 1315 /** 1316 * Directory to output HTML documentation. 1317 * @ant.non-required 1318 * @param documentation 1319 */ 1320 public void setDocDir(File docDir) { 1321 if (docDir.exists() && docDir.isDirectory()) { 1322 this.docDir=docDir; 1323 } else { 1324 throw new BuildException(docDir.getAbsolutePath()+" does not exist or is not a directory"); 1325 } 1326 } 1327 1328 /** 1329 * Class name for master engine. Set this attribute if you want 1330 * all engine methods be held in one class and avoid generation of 1331 * an engine class per statement. 1332 * @ant.non-required 1333 * @param masterEngineName 1334 */ 1335 public void setMasterEngine(String masterEngineName) { 1336 this.masterEngineName = masterEngineName; 1337 } 1338 1339 /** 1340 * Table entry to generate statements from table metadata and then compile them. 1341 * @ant.non-required 1342 * @param generateForTables 1343 */ 1344 public void addTable(TableEntry tableEntry) { 1345 tableEntry.setTask(this); 1346 tableEntries.add(tableEntry); 1347 } 1348 1349 /** 1350 * @return 1351 */ 1352 protected boolean isToBeGenerated(TableDescriptor td, TableEntry te) { 1353 return columnsCount(td, te)>1; 1354 } 1355 1356 /** 1357 * @param td 1358 * @param te 1359 * @return 1360 */ 1361 private int columnsCount(TableDescriptor td, TableEntry te) { 1362 Iterator it=td.getColumnDescriptors().iterator(); 1363 int count=0; 1364 while (it.hasNext()) { 1365 Object o = it.next(); 1366 if (!(o instanceof ColumnDescriptor && te.isSkipColumn((ColumnDescriptor) o))) { 1367 count++; 1368 } 1369 } 1370 return count; 1371 } 1372 1373 private String smartBase; 1374 1375 /** 1376 * @ant.ignore 1377 * @return Returns the smartBase. 1378 */ 1379 String getSmartBase() { 1380 return smartBase; 1381 } 1382 1383 /** 1384 * Base class (fully qualified name) for generated smart implementations. Default is biz.hammurapi.sql.DatabaseObject 1385 * The base class is supposed to have same constructors and methods as DatabaseObject. The idea is 1386 * to subclass DatabaseObject to introduce additional qualities like audit, metrics, security, ... whithout 1387 * changing class' contract. This setting is not inherited from the parent task. 1388 * @ant.non-required 1389 * @param smartBase The smartBase to set. 1390 */ 1391 public void setSmartBase(String smartBase) { 1392 this.smartBase = smartBase; 1393 } 1394 }