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    }