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 }