0001: /*
0002: * sqlc 1
0003: * SQL Compiler
0004: * Copyright (C) 2003 Hammurapi Group
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2 of the License, or (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * URL: http://www.hammurapi.biz/products/sqlc/index.html
0021: * e-Mail: support@hammurapi.biz
0022: */
0023: package biz.hammurapi.sqlc;
0024:
0025: import java.io.File;
0026: import java.io.FileInputStream;
0027: import java.io.FileReader;
0028: import java.io.IOException;
0029: import java.io.Reader;
0030: import java.sql.SQLException;
0031: import java.util.ArrayList;
0032: import java.util.Collection;
0033: import java.util.HashMap;
0034: import java.util.Iterator;
0035: import java.util.Map;
0036: import java.util.Properties;
0037:
0038: import org.apache.bcel.classfile.JavaClass;
0039: import org.apache.tools.ant.BuildException;
0040: import org.apache.tools.ant.Project;
0041: import org.apache.tools.ant.Task;
0042:
0043: import biz.hammurapi.ant.ConnectionEntry;
0044: import biz.hammurapi.ant.ObjectEntry;
0045: import biz.hammurapi.codegen.DocumentingConsumer;
0046: import biz.hammurapi.codegen.GenerationException;
0047: import biz.hammurapi.codegen.HtmlDocConsumer;
0048: import biz.hammurapi.codegen.XmlDocConsumer;
0049: import biz.hammurapi.config.Context;
0050: import biz.hammurapi.config.MapContext;
0051: import biz.hammurapi.sql.SQLProcessor;
0052: import biz.hammurapi.sql.hypersonic.HypersonicInMemoryDataSource;
0053: import biz.hammurapi.sql.metadata.ColumnDescriptor;
0054: import biz.hammurapi.sql.metadata.DefaultGenerationPolicy;
0055: import biz.hammurapi.sql.metadata.GenerationPolicy;
0056: import biz.hammurapi.sql.metadata.IndexDescriptor;
0057: import biz.hammurapi.sql.metadata.KeyDescriptor;
0058: import biz.hammurapi.sql.metadata.KeyEntry;
0059: import biz.hammurapi.sql.metadata.Metadata;
0060: import biz.hammurapi.sql.metadata.TableDescriptor;
0061: import biz.hammurapi.sql.metadata.Metadata.TableAcceptor;
0062:
0063: /**
0064: * Compiles SQL query to interface and engine classes using metadata obtained
0065: * from the database.
0066: * <section name="Example" suppress-description="yes">
0067: * SQLC uses <a href="http://jakarta.apache.org/bcel/">BCEL</a> and <a href="http://www.antlr.org">ANTLR</a>
0068: for code generation. If you have pvcommons.jar, bcel-5.1.jar and antlr.jar in
0069: the system classpath
0070: or in Ant lib directory then SQLC task can be defined as
0071: <pre style="border: 1px solid ; background-color: rgb(204, 204, 204);"><taskdef name="sqlc" classname="biz.hammurapi.sqlc.StatementCompilerTask"/><br/></pre>
0072: Otherwise jars which are not in the classpath shall be specified in task definition
0073: classpath. SQLC connects to the target database during generation,
0074: therefore database driver shall also be present in the classpath<br/>
0075: <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>
0076: Usage:
0077: <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>
0078: </section>
0079: * @ant.element name="statementcompiler"
0080: * @author Pavel Vlasov
0081: * @version $Revision: 1.14 $
0082: */
0083: public class StatementCompilerTask extends Task {
0084: ObjectEntry generationPolicyEntry;
0085:
0086: private StatementCompilerTask parent;
0087: private boolean inheritMetadata;
0088:
0089: /**
0090: * Use parent's metadata file if any.
0091: * Valid for nested tasks. Default is false.
0092: * @ant.non-required
0093: * @param inheritMetadata
0094: */
0095: public void setInheritMetadata(boolean inheritMetadata) {
0096: this .inheritMetadata = inheritMetadata;
0097: }
0098:
0099: public StatementCompilerTask() {
0100: // Default constructor
0101: }
0102:
0103: private StatementCompilerTask(StatementCompilerTask parent) {
0104: this .parent = parent;
0105: setProject(parent.getProject());
0106: }
0107:
0108: private String engineVisibility = "public";
0109: private String engineMethodsVisibility = "public";
0110: private String interfaceImplVisibility = "public";
0111:
0112: private Collection generatedInterfaces = new ArrayList();
0113:
0114: /**
0115: * Visibility of engine methods. Valid values are "public" (default), "protected" and
0116: * "" (empty string for default/package visibility).
0117: * @ant.non-required
0118: * @param engineMethodsVisibility
0119: */
0120: public void setEngineMethodsVisibility(
0121: String engineMethodsVisibility) {
0122: if ("public".equals(engineMethodsVisibility)
0123: || "protected".equals(engineMethodsVisibility)
0124: || "".equals(engineMethodsVisibility)) {
0125: this .engineMethodsVisibility = engineMethodsVisibility;
0126: } else {
0127: throw new BuildException("Invalid visibility value: '"
0128: + engineMethodsVisibility + "'");
0129: }
0130: }
0131:
0132: private Boolean useSqlTypes;
0133:
0134: /**
0135: * If true then generated classes will use setObject(int, Object, int) method
0136: * instead of setObject(int, Object) to set parameters of object type.
0137: * Nested tasks inherit parent setting unless overriden.
0138: * @ant.non-required
0139: * @param useSqlTypes
0140: */
0141: public void setUseSqlTypes(boolean useSqlTypes) {
0142: this .useSqlTypes = useSqlTypes ? Boolean.TRUE : Boolean.FALSE;
0143: }
0144:
0145: Boolean getUseSqlTypes() {
0146: return useSqlTypes;
0147: }
0148:
0149: private File metadataFile;
0150:
0151: /**
0152: * Metadata file
0153: * @param metadataFile
0154: * @ant.non-required
0155: */
0156: public void setMetadata(File metadataFile) {
0157: this .metadataFile = metadataFile;
0158: }
0159:
0160: private String indexName;
0161:
0162: /**
0163: * Documentation index file name.
0164: * Defaults to 'index.xml' for XML output
0165: * and 'index.html' for HTML output.
0166: * @ant.non-required
0167: * @param indexName
0168: */
0169: public void setIndexName(String indexName) {
0170: this .indexName = indexName;
0171: }
0172:
0173: /**
0174: * Visibility of engine class. Valid values are "public" (default), and
0175: * "" (empty string for default/package visibility).
0176: * @ant.non-required
0177: * @param engineVisibility
0178: */
0179: public void setEngineVisibility(String engineVisibility) {
0180: if ("public".equals(engineMethodsVisibility)
0181: || "".equals(engineMethodsVisibility)) {
0182: this .engineVisibility = engineVisibility;
0183: } else {
0184: throw new BuildException("Invalid visibility value: '"
0185: + engineMethodsVisibility + "'");
0186: }
0187: }
0188:
0189: /**
0190: * Visibility of interface implementation classes. Valid values are "public" (default), and
0191: * "" (empty string for default/package visibility).
0192: * @ant.non-required
0193: */
0194: public void setInterfaceImplVisibility(
0195: String interfaceImplVisibility) {
0196: if ("public".equals(engineMethodsVisibility)
0197: || "".equals(engineMethodsVisibility)) {
0198: this .interfaceImplVisibility = interfaceImplVisibility;
0199: } else {
0200: throw new BuildException("Invalid visibility value: '"
0201: + engineMethodsVisibility + "'");
0202: }
0203: }
0204:
0205: /**
0206: * Generation policy. Must implement biz.hammurapi.sql.metadata.GenerationPolicy
0207: * interface.
0208: * @ant.non-required
0209: * @return
0210: */
0211: public ObjectEntry createGenerationPolicy() {
0212: if (generationPolicyEntry == null) {
0213: generationPolicyEntry = new ObjectEntry() {
0214: protected void validateClass(Class clazz)
0215: throws BuildException {
0216: super .validateClass(clazz);
0217: Class theClass = GenerationPolicy.class;
0218: if (!theClass.isAssignableFrom(clazz)) {
0219: throw new BuildException(clazz.getName()
0220: + " doesn't implement "
0221: + theClass.getName());
0222: }
0223: }
0224: };
0225: return generationPolicyEntry;
0226: }
0227:
0228: throw new BuildException("Generation policy already defined");
0229: }
0230:
0231: private SQLProcessor processor;
0232: private File dir;
0233: private String packageName;
0234: private Collection statements = new ArrayList();
0235: private File docDir;
0236: private Boolean outputXml;
0237: private Collection interfaces = new ArrayList();
0238: private ConnectionEntry connectionEntry;
0239:
0240: private Context nameMap;
0241:
0242: private File script;
0243:
0244: /**
0245: * If this attribute is true then:
0246: * 1) All queries and updates generated for tables will have table
0247: * and schema name enclosed into ${ and } e.g. ${BANK}.${ACCOUNT}
0248: * 2) SQLProcessor used to obtain
0249: * This attribute creates blank name map, if you need a prepopulated name map use
0250: * @ant.non-required
0251: * @param useNameMap
0252: */
0253: public void setNameMap(boolean useNameMap) {
0254: if (nameMap != null) {
0255: throw new BuildException("Name map is already set");
0256: }
0257:
0258: if (useNameMap) {
0259: nameMap = new Context() {
0260: public Object get(String name) {
0261: return null;
0262: }
0263: };
0264: } else {
0265: nameMap = null;
0266: }
0267: }
0268:
0269: /**
0270: * Reads name map from property file.
0271: * @ant.non-required
0272: */
0273: public void setNameMapFile(File nameMapFile) {
0274: if (nameMap != null) {
0275: throw new BuildException("Name map is already set");
0276: }
0277:
0278: try {
0279: Properties props = new Properties();
0280: props.load(new FileInputStream(nameMapFile));
0281: nameMap = new MapContext(props);
0282: } catch (IOException e) {
0283: throw new BuildException("Cannot load name map.");
0284: }
0285: }
0286:
0287: /**
0288: * Interface which generated interfaces shall try to extend.
0289: * @ant.non-required
0290: * @param entry
0291: */
0292: public void addConfiguredInterface(InterfaceEntry entry) {
0293: interfaces.add(entry.getInterface());
0294: }
0295:
0296: /**
0297: * If true documentation will be generated in XML format for
0298: * further styling.
0299: * @ant.non-required
0300: */
0301: public void setXmlDoc(boolean xmlDoc) {
0302: outputXml = xmlDoc ? Boolean.TRUE : Boolean.FALSE;
0303: }
0304:
0305: /**
0306: * Database connection.
0307: * @ant.non-required
0308: * @param ce
0309: */
0310: public void addConnection(ConnectionEntry ce) {
0311: if (processor == null && connectionEntry == null) {
0312: this .connectionEntry = ce;
0313: } else {
0314: throw new BuildException(
0315: "Either script or connection has already been set");
0316: }
0317: }
0318:
0319: /**
0320: * DDL script file. Statements shall be separated by semicolons.
0321: * If this attribute is set then in-memory Hypersonic database is created,
0322: * gets populated using script and query is compiled against this database.
0323: * If neither connection nor script is specified the uninitialized Hypersonic
0324: * in-memory database will be used. It can be useful if several sqlc tasks
0325: * are executed in a row against the same database.
0326: * @ant.non-required
0327: * @param script
0328: */
0329: public void setScript(File script) {
0330: if (connectionEntry == null) {
0331: this .script = script;
0332: } else {
0333: throw new BuildException("Connection has already been set");
0334: }
0335: }
0336:
0337: public void addConfiguredScript(ScriptEntry scriptEntry) {
0338: if (connectionEntry == null) {
0339: try {
0340: if (processor == null) {
0341: processor = new SQLProcessor(
0342: new HypersonicInMemoryDataSource(
0343: (Reader) null), nameMap);
0344: }
0345: processor.executeScript(scriptEntry.getScript());
0346: } catch (ClassNotFoundException e) {
0347: throw new BuildException(
0348: "Hypersonic database driver is not found", e);
0349: } catch (IOException e) {
0350: throw new BuildException("Cannot read script", e);
0351: } catch (SQLException e) {
0352: throw new BuildException("Cannot execute script", e);
0353: }
0354: } else {
0355: throw new BuildException("Connection has already been set");
0356: }
0357: }
0358:
0359: /**
0360: * Directory to output compiled classes
0361: * @ant.required
0362: * @param dir
0363: */
0364: public void setDir(File dir) {
0365: if (dir.exists() && dir.isDirectory()) {
0366: this .dir = dir;
0367: } else {
0368: throw new BuildException(dir.getAbsolutePath()
0369: + " does not exist or is not a directory");
0370: }
0371: }
0372:
0373: /**
0374: * Package for generated classes
0375: * @ant.required
0376: * @param packageName
0377: */
0378: public void setPackage(String packageName) {
0379: this .packageName = packageName;
0380: }
0381:
0382: /**
0383: * Query to compile
0384: * @ant.non-required
0385: * @param query
0386: */
0387: public void addQuery(QueryEntry query) {
0388: query.setTask(this );
0389: statements.add(query);
0390: }
0391:
0392: /**
0393: * Query to compile
0394: * @ant.non-required
0395: * @param update
0396: */
0397: public void addUpdate(UpdateEntry update) {
0398: update.setTask(this );
0399: statements.add(update);
0400: }
0401:
0402: /**
0403: * Statements xml file. Top element should be 'statements', containing 'query' and 'update' elements which
0404: * have the same format as nested 'query' and 'update' elements.
0405: * @ant.non-required
0406: * @param queries
0407: */
0408: public void addConfiguredStatements(StatementsEntry queries) {
0409: queries.setTask(this );
0410: queries.getStatements(this .statements, this .tableEntries);
0411: }
0412:
0413: /**
0414: * Statements from the database.
0415: * @ant.non-required
0416: * @param queries
0417: */
0418: public void addConfiguredDbStatements(DbStatementsEntry queries) {
0419: queries.getStatements(getProcessor(), this , this .statements);
0420: }
0421:
0422: private String masterEngineName;
0423: private Collection tableEntries = new ArrayList();
0424:
0425: // private String statementQuery;
0426:
0427: // /**
0428: // * Query to execute to retrieve a statement by name. If this property is set then
0429: // * statement SQL will not be hardcoded into generated engines but retrieved from the database
0430: // * when engine is instantiated. This gives flexibility to modify query, e.g. the way tables
0431: // * are joined, use nested selects instead of joins, without recompiling and redeploying
0432: // * the application. Of course statements shall be parameter and column compatible.
0433: // * The query take one parameter of type String and shall return one column of type String with
0434: // * statement's SQL.
0435: // * @ant.non-required
0436: // * @param queriesQuery
0437: // */
0438: // public void setStatementQuery(String statementQuery) {
0439: // this.statementQuery=statementQuery;
0440: // }
0441:
0442: public void execute() throws BuildException {
0443: getProcessor();
0444:
0445: try {
0446: if (dir == null) {
0447: if (parent == null) {
0448: throw new BuildException("Output dir is not set");
0449: }
0450:
0451: dir = parent.dir;
0452: if (dir == null) {
0453: throw new BuildException(
0454: "Output dir is not set and parent dir is not set");
0455: }
0456: }
0457:
0458: if (parent != null) {
0459: if (generationPolicyEntry == null) {
0460: generationPolicyEntry = parent.generationPolicyEntry;
0461: }
0462:
0463: if (docDir == null) {
0464: docDir = parent.docDir;
0465: }
0466:
0467: if (outputXml == null) {
0468: outputXml = parent.outputXml;
0469: }
0470:
0471: if (useSqlTypes == null) {
0472: useSqlTypes = parent.useSqlTypes;
0473: }
0474:
0475: interfaces.addAll(parent.interfaces);
0476:
0477: Iterator git = parent.generatedInterfaces.iterator();
0478: while (git.hasNext()) {
0479: String iName = (String) git.next();
0480: log("Adding inherited interface: " + iName,
0481: Project.MSG_VERBOSE);
0482: InterfaceEntry ie = new InterfaceEntry();
0483: ie.setClassLoader(parent.getClass()
0484: .getClassLoader());
0485: ie.setProject(getProject());
0486: ie.setName(iName);
0487: ie.createClasspath().setLocation(parent.dir);
0488: addConfiguredInterface(ie);
0489: }
0490:
0491: if (inheritMetadata && metadataFile == null
0492: && parent != null
0493: && parent.metadataFile != null) {
0494: metadataFile = parent.metadataFile;
0495: }
0496: }
0497:
0498: DocumentingConsumer consumer;
0499: if (Boolean.TRUE.equals(outputXml)) {
0500: consumer = new XmlDocConsumer(dir, docDir,
0501: indexName == null ? "index.xml" : indexName) {
0502: public void consume(JavaClass javaClass)
0503: throws GenerationException {
0504: super .consume(javaClass);
0505: if (javaClass.isInterface()) {
0506: generatedInterfaces.add(javaClass
0507: .getClassName());
0508: }
0509: log(javaClass.getClassName(),
0510: Project.MSG_VERBOSE);
0511: }
0512: };
0513: } else {
0514: consumer = new HtmlDocConsumer(dir, docDir,
0515: indexName == null ? "index.xml" : indexName) {
0516: public void consume(JavaClass javaClass)
0517: throws GenerationException {
0518: super .consume(javaClass);
0519: if (javaClass.isInterface()) {
0520: generatedInterfaces.add(javaClass
0521: .getClassName());
0522: }
0523: log(javaClass.getClassName(),
0524: Project.MSG_VERBOSE);
0525: }
0526: };
0527: }
0528:
0529: Collection namedStatements = new ArrayList();
0530:
0531: GenerationPolicy generationPolicy;
0532: if (generationPolicyEntry == null) {
0533: generationPolicy = new DefaultGenerationPolicy() {
0534: {
0535: packageName = "";
0536: }
0537: };
0538: } else {
0539: generationPolicy = (GenerationPolicy) generationPolicyEntry
0540: .getObject(this .getClass().getClassLoader());
0541: }
0542:
0543: if (!tableEntries.isEmpty()) {
0544: log("Processing table entries", Project.MSG_VERBOSE);
0545: final Map teMap = new HashMap();
0546:
0547: Metadata metadata;
0548:
0549: if (this .metadataFile == null) {
0550: metadata = new Metadata(processor, new String[] {
0551: "TABLE", "VIEW" }, generationPolicy,
0552: new TableAcceptor() {
0553: public boolean accept(String catalog,
0554: String schema, String table) {
0555: Iterator it = tableEntries
0556: .iterator();
0557: while (it.hasNext()) {
0558: TableEntry tableEntry = (TableEntry) it
0559: .next();
0560: Object key = Metadata.toKey(
0561: catalog, schema, table);
0562: if (tableEntry.accept(catalog,
0563: schema, table)) {
0564: teMap.put(key, tableEntry);
0565: log("Accepted: " + key,
0566: Project.MSG_VERBOSE);
0567: return true;
0568: }
0569:
0570: log("Rejected: " + key,
0571: Project.MSG_VERBOSE);
0572: }
0573: return false;
0574: }
0575: });
0576: } else {
0577: metadata = Metadata.load(metadataFile);
0578: Iterator tdit = metadata.getTableDescriptors()
0579: .iterator();
0580: Z: while (tdit.hasNext()) {
0581: TableDescriptor td = (TableDescriptor) tdit
0582: .next();
0583: Iterator it = tableEntries.iterator();
0584: while (it.hasNext()) {
0585: TableEntry tableEntry = (TableEntry) it
0586: .next();
0587: Object key = Metadata.toKey(
0588: td.getCatalog(), td.getSchema(), td
0589: .getTableName());
0590: if (tableEntry.accept(td.getCatalog(), td
0591: .getSchema(), td.getTableName())) {
0592: teMap.put(key, tableEntry);
0593: log("Accepted: " + key,
0594: Project.MSG_VERBOSE);
0595: continue Z;
0596: }
0597:
0598: log("Rejected: " + key, Project.MSG_VERBOSE);
0599: }
0600: //tdit.remove();
0601: }
0602: }
0603:
0604: Iterator wit = metadata.getWarnings().iterator();
0605: while (wit.hasNext()) {
0606: log((String) wit.next(), Project.MSG_WARN);
0607: }
0608:
0609: Iterator tdit = metadata.getTableDescriptors()
0610: .iterator();
0611: while (tdit.hasNext()) {
0612: TableDescriptor td = (TableDescriptor) tdit.next();
0613: TableEntry te = (TableEntry) teMap.get(Metadata
0614: .toKey(td.getCatalog(), td.getSchema(), td
0615: .getTableName()));
0616: if (te == null) {
0617: log(
0618: "Table entry not found for "
0619: + td.getName(),
0620: Project.MSG_VERBOSE);
0621: continue;
0622: }
0623:
0624: log("Processing table " + td.getName(),
0625: Project.MSG_VERBOSE);
0626:
0627: String tableName = te.isQualifiedTableName() ? td
0628: .getName() : td.getTableName();
0629: if (nameMap != null) {
0630: tableName = "${" + tableName + "}";
0631: }
0632:
0633: boolean generateMutators = te.getGenerateMutators();
0634: // Select all
0635: String entityType = te.isQualifiedMethodName() ? td
0636: .getEntityType() : DefaultGenerationPolicy
0637: .convert(td.getTableName(), "_");
0638: String methodNamePostfix = entityType.indexOf('.') == -1 ? entityType
0639: : "_" + entityType.replace('.', '_');
0640:
0641: // Delete all
0642: NamedUpdate namedUpdate = new NamedUpdate("Delete"
0643: + methodNamePostfix,
0644: "Deletes all records from " + tableName,
0645: "DELETE FROM " + tableName, null, null);
0646: namedUpdate.setUseSqlTypes(te.getUseSqlTypes());
0647: namedStatements.add(injectVisibility(namedUpdate));
0648:
0649: NamedQuery selectAll = injectVisibility(new NamedQuery(
0650: methodNamePostfix, "Selects all rows from "
0651: + tableName, false,
0652: "SELECT * FROM " + tableName, null, null,
0653: td.getColumnDescriptors(), generateMutators));
0654: selectAll.setUseSqlTypes(te.getUseSqlTypes());
0655: te.setColTypes(selectAll);
0656: if (te.getSmartBase() != null) {
0657: selectAll.setSmartBaseName(te.getSmartBase());
0658: }
0659: namedStatements.add(selectAll);
0660:
0661: // By primary key
0662: StringBuffer sb = new StringBuffer();
0663: StringBuffer icsb = new StringBuffer();
0664: StringBuffer ivsb = new StringBuffer();
0665: Collection parameters = new ArrayList();
0666: sb.append(tableName);
0667: sb.append(" WHERE ");
0668: boolean addAnd = false;
0669: Iterator cit = td.getColumnDescriptors().iterator();
0670: while (cit.hasNext()) {
0671: ColumnDescriptor cd = (ColumnDescriptor) cit
0672: .next();
0673: icsb.append(cd.getDbName());
0674: ivsb.append("?");
0675: if (cit.hasNext()) {
0676: icsb.append(",");
0677: ivsb.append(",");
0678: }
0679:
0680: if (cd.isPrimaryKey()) {
0681: if (addAnd) {
0682: sb.append(" AND ");
0683: }
0684: parameters.add(cd);
0685: sb.append(cd.getDbName());
0686: sb.append("=?");
0687: addAnd = true;
0688: }
0689: }
0690:
0691: if (!parameters.isEmpty()) {
0692: NamedQuery select = injectVisibility(new NamedQuery(
0693: methodNamePostfix,
0694: "Selects by primary key from "
0695: + tableName, true,
0696: "SELECT * FROM " + sb.toString(),
0697: parameters, null, td
0698: .getColumnDescriptors(),
0699: generateMutators));
0700: select.setUseSqlTypes(te.getUseSqlTypes());
0701: te.setColTypes(select);
0702: setParameterTypes(te, parameters, select);
0703: if (te.getSmartBase() != null) {
0704: select.setSmartBaseName(te.getSmartBase());
0705: }
0706: namedStatements.add(select);
0707:
0708: // Delete
0709: NamedUpdate delete = new NamedUpdate("Delete"
0710: + methodNamePostfix,
0711: "Deletes by primary key from "
0712: + tableName, "DELETE FROM "
0713: + sb.toString(), parameters,
0714: null);
0715: delete.setUseSqlTypes(te.getUseSqlTypes());
0716: setParameterTypes(te, parameters, delete);
0717: namedStatements.add(injectVisibility(delete));
0718:
0719: generateUpdate(namedStatements, td,
0720: methodNamePostfix, parameters, te);
0721:
0722: }
0723:
0724: // Insert
0725: if (te.isParamPerColumnInsert()) {
0726: NamedUpdate insert = new NamedUpdate("Insert"
0727: + methodNamePostfix,
0728: "Inserts new record into " + tableName,
0729: "INSERT INTO " + tableName + " ("
0730: + icsb + ") VALUES (" + ivsb
0731: + ")", td
0732: .getColumnDescriptors(), null);
0733: insert.setUseSqlTypes(te.getUseSqlTypes());
0734: setParameterTypes(te,
0735: td.getColumnDescriptors(), insert);
0736: namedStatements.add(injectVisibility(insert));
0737: }
0738:
0739: if (isToBeGenerated(td, te)) {
0740: NamedParameterObjectUpdate poInsert = new NamedParameterObjectUpdate(
0741: "Insert" + methodNamePostfix,
0742: "Inserts new record into " + tableName,
0743: "INSERT INTO " + tableName + " ("
0744: + icsb + ") VALUES (" + ivsb
0745: + ")", td
0746: .getColumnDescriptors(), null,
0747: generateMutators);
0748: poInsert
0749: .setMode(NamedInterfaceGeneratingStatement.MODE_INSERT);
0750: poInsert.setTableName(tableName);
0751: poInsert.setUseSqlTypes(te.getUseSqlTypes());
0752: if (te.getSmartBase() != null) {
0753: poInsert
0754: .setSmartBaseName(te.getSmartBase());
0755: }
0756: setParameterTypes(te,
0757: td.getColumnDescriptors(), poInsert);
0758: te.setColTypes(poInsert);
0759: namedStatements.add(injectVisibility(poInsert));
0760: }
0761:
0762: generateIndices(namedStatements, td, te, tableName,
0763: methodNamePostfix);
0764: generateImportedKeys(namedStatements, td, te,
0765: tableName, methodNamePostfix);
0766: }
0767: }
0768:
0769: Iterator it = statements.iterator();
0770: while (it.hasNext()) {
0771: StatementEntry statementEntry = (StatementEntry) it
0772: .next();
0773: log("Processing " + statementEntry.name,
0774: Project.MSG_VERBOSE);
0775: namedStatements.add(statementEntry.getStatement(
0776: processor, generationPolicy));
0777: }
0778:
0779: // if (useSqlTypes!=null) {
0780: // Iterator sit=namedStatements.iterator();
0781: // while (sit.hasNext()) {
0782: // ((NamedStatement) sit.next()).setUseSqlTypes(useSqlTypes.booleanValue());
0783: // }
0784: // }
0785:
0786: NamedStatement.generate(packageName, masterEngineName,
0787: engineVisibility, engineMethodsVisibility,
0788: namedStatements, interfaces, consumer);
0789:
0790: consumer.close();
0791: // Collection verifyErrors=ClassGeneratorBase.verify(toVerify, null);
0792: // it=verifyErrors.iterator();
0793: // while (it.hasNext()) {
0794: // log((String) it.next(), Project.MSG_WARN);
0795: // }
0796: } catch (GenerationException e) {
0797: e.printStackTrace();
0798: throw new BuildException("Could not generate: " + e, e);
0799: } catch (SQLException e) {
0800: // e.printStackTrace();
0801: throw new BuildException("Could not read query metadata: "
0802: + e, e);
0803: // } catch (Exception e) {
0804: // e.printStackTrace();
0805: } catch (IOException e) {
0806: throw new BuildException("Could not load metadata file: "
0807: + e, e);
0808: } catch (ClassNotFoundException e) {
0809: throw new BuildException("Could not load metadata file: "
0810: + e, e);
0811: }
0812:
0813: Iterator it = subTasks.iterator();
0814: while (it.hasNext()) {
0815: ((Task) it.next()).execute();
0816: }
0817:
0818: if (connectionEntry != null) {
0819: connectionEntry.shutdown();
0820: }
0821: }
0822:
0823: /**
0824: * @param namedStatements
0825: * @param td
0826: * @param te
0827: * @param tableName
0828: * @param generateMutators
0829: * @param methodNamePostfix
0830: * @throws GenerationException
0831: */
0832: private void generateIndices(Collection namedStatements,
0833: TableDescriptor td, TableEntry te, String tableName,
0834: String methodNamePostfix) throws GenerationException {
0835: boolean generateMutators = te.getGenerateMutators();
0836: // Process indices
0837: Iterator it = td.getIndices().iterator();
0838: while (it.hasNext()) {
0839: IndexDescriptor id = (IndexDescriptor) it.next();
0840: if (id.getInfo() != null) {
0841: String columnList = getColumnList(id);
0842:
0843: if (id.getInfo().isEQ()) {
0844: StringBuffer isb = new StringBuffer(tableName);
0845: isb.append(" WHERE ");
0846: Iterator icit = id.getColumns().iterator();
0847: while (icit.hasNext()) {
0848: KeyEntry ke = (KeyEntry) icit.next();
0849: isb.append(" ");
0850: isb.append(ke.getColumnName());
0851: isb.append("=?");
0852: if (icit.hasNext()) {
0853: isb.append(" AND ");
0854: }
0855: }
0856:
0857: NamedUpdate deleteByIndex = new NamedUpdate(
0858: "Delete" + methodNamePostfix
0859: + id.getInfo().getJavaName() + "EQ",
0860: "Deletes row(s) with equal index value(s): "
0861: + columnList, "DELETE FROM "
0862: + isb.toString(), id.getColumns(),
0863: null);
0864: setParameterTypes(te, id.getColumns(),
0865: deleteByIndex);
0866: deleteByIndex.setUseSqlTypes(te.getUseSqlTypes());
0867: namedStatements
0868: .add(injectVisibility(deleteByIndex));
0869:
0870: NamedQuery selectByIndex = new NamedQuery(
0871: methodNamePostfix
0872: + id.getInfo().getJavaName() + "EQ",
0873: "Selects row(s) with equal index value(s): "
0874: + columnList, id.isUnique(),
0875: "SELECT * FROM " + isb.toString(), id
0876: .getColumns(), null, td
0877: .getColumnDescriptors(),
0878: generateMutators);
0879: setParameterTypes(te, id.getColumns(),
0880: selectByIndex);
0881: te.setColTypes(selectByIndex);
0882: if (te.getSmartBase() != null) {
0883: selectByIndex.setSmartBaseName(te
0884: .getSmartBase());
0885: }
0886: selectByIndex.setUseSqlTypes(te.getUseSqlTypes());
0887: namedStatements
0888: .add(injectVisibility(selectByIndex));
0889: }
0890:
0891: if (id.getInfo().isNE()) {
0892: StringBuffer isb = new StringBuffer(tableName);
0893: isb.append(" WHERE ");
0894: Iterator icit = id.getColumns().iterator();
0895: while (icit.hasNext()) {
0896: KeyEntry ke = (KeyEntry) icit.next();
0897: isb.append(" ");
0898: isb.append(ke.getColumnName());
0899: isb.append("=?");
0900: if (icit.hasNext()) {
0901: isb.append(" AND ");
0902: }
0903: }
0904:
0905: NamedUpdate dbx = new NamedUpdate("Delete"
0906: + methodNamePostfix
0907: + id.getInfo().getJavaName() + "NE",
0908: "Deletes row(s) with not equal index value(s): "
0909: + columnList, "DELETE FROM "
0910: + isb.toString(), id.getColumns(),
0911: null);
0912: setParameterTypes(te, id.getColumns(), dbx);
0913: dbx.setUseSqlTypes(te.getUseSqlTypes());
0914: namedStatements.add(injectVisibility(dbx));
0915:
0916: NamedQuery sbx = new NamedQuery(methodNamePostfix
0917: + id.getInfo().getJavaName() + "NE",
0918: "Selects row(s) with not equal index value(s): "
0919: + columnList, false,
0920: "SELECT * FROM " + isb.toString(), id
0921: .getColumns(), null, td
0922: .getColumnDescriptors(),
0923: generateMutators);
0924: setParameterTypes(te, id.getColumns(), sbx);
0925: te.setColTypes(sbx);
0926: sbx.setUseSqlTypes(te.getUseSqlTypes());
0927: if (te.getSmartBase() != null) {
0928: sbx.setSmartBaseName(te.getSmartBase());
0929: }
0930: namedStatements.add(injectVisibility(sbx));
0931: }
0932:
0933: if (id.getInfo().isGE()) {
0934: StringBuffer isb = new StringBuffer(tableName);
0935: isb.append(" WHERE ");
0936: String operator = ">";
0937: String descOperator = "<";
0938: String lastOperator = ">=";
0939: String lastDescOperator = "<=";
0940:
0941: Collection parameters2 = generateComparison(id,
0942: isb, operator, descOperator, lastOperator,
0943: lastDescOperator);
0944: NamedUpdate dbx = new NamedUpdate("Delete"
0945: + methodNamePostfix
0946: + id.getInfo().getJavaName() + "GE",
0947: "Deletes row(s) with greater or equal index value(s): "
0948: + columnList, "DELETE FROM "
0949: + isb.toString(), id.getColumns(),
0950: parameters2);
0951: setParameterTypes(te, id.getColumns(), dbx);
0952: dbx.setUseSqlTypes(te.getUseSqlTypes());
0953: namedStatements.add(injectVisibility(dbx));
0954:
0955: generateOrderBy(id, isb);
0956:
0957: NamedQuery sbx = new NamedQuery(
0958: methodNamePostfix
0959: + id.getInfo().getJavaName() + "GE",
0960: "Selects row(s) with greater or equal index value(s) ordered by insex. Columns: "
0961: + columnList, false,
0962: "SELECT * FROM " + isb.toString(), id
0963: .getColumns(), parameters2, td
0964: .getColumnDescriptors(),
0965: generateMutators);
0966: setParameterTypes(te, id.getColumns(), sbx);
0967: te.setColTypes(sbx);
0968: sbx.setUseSqlTypes(te.getUseSqlTypes());
0969: if (te.getSmartBase() != null) {
0970: sbx.setSmartBaseName(te.getSmartBase());
0971: }
0972: namedStatements.add(injectVisibility(sbx));
0973: }
0974:
0975: if (id.getInfo().isGT()) {
0976: StringBuffer isb = new StringBuffer(tableName);
0977: isb.append(" WHERE ");
0978: String operator = ">";
0979: String descOperator = "<";
0980: String lastOperator = ">";
0981: String lastDescOperator = "<";
0982:
0983: Collection parameters2 = generateComparison(id,
0984: isb, operator, descOperator, lastOperator,
0985: lastDescOperator);
0986:
0987: NamedUpdate dbx = new NamedUpdate("Delete"
0988: + methodNamePostfix
0989: + id.getInfo().getJavaName() + "GT",
0990: "Deletes row(s) with greater index value(s): "
0991: + columnList, "DELETE FROM "
0992: + isb.toString(), id.getColumns(),
0993: parameters2);
0994: setParameterTypes(te, id.getColumns(), dbx);
0995: dbx.setUseSqlTypes(te.getUseSqlTypes());
0996: namedStatements.add(injectVisibility(dbx));
0997:
0998: generateOrderBy(id, isb);
0999:
1000: NamedQuery sbx = new NamedQuery(methodNamePostfix
1001: + id.getInfo().getJavaName() + "GT",
1002: "Selects row(s) with greater index value(s), ordered by index. Columns: "
1003: + columnList, false,
1004: "SELECT * FROM " + isb.toString(), id
1005: .getColumns(), parameters2, td
1006: .getColumnDescriptors(),
1007: generateMutators);
1008: setParameterTypes(te, id.getColumns(), sbx);
1009: te.setColTypes(sbx);
1010: sbx.setUseSqlTypes(te.getUseSqlTypes());
1011: if (te.getSmartBase() != null) {
1012: sbx.setSmartBaseName(te.getSmartBase());
1013: }
1014: namedStatements.add(injectVisibility(sbx));
1015: }
1016:
1017: if (id.getInfo().isLE()) {
1018: StringBuffer isb = new StringBuffer(tableName);
1019: isb.append(" WHERE ");
1020: String operator = "<";
1021: String descOperator = ">";
1022: String lastOperator = "<=";
1023: String lastDescOperator = ">=";
1024:
1025: Collection parameters2 = generateComparison(id,
1026: isb, operator, descOperator, lastOperator,
1027: lastDescOperator);
1028: NamedUpdate dbx = new NamedUpdate("Delete"
1029: + methodNamePostfix
1030: + id.getInfo().getJavaName() + "LE",
1031: "Deletes row(s) with less or equal index value(s): "
1032: + columnList, "DELETE FROM "
1033: + isb.toString(), id.getColumns(),
1034: parameters2);
1035: setParameterTypes(te, id.getColumns(), dbx);
1036: dbx.setUseSqlTypes(te.getUseSqlTypes());
1037: namedStatements.add(injectVisibility(dbx));
1038:
1039: generateOrderBy(id, isb);
1040:
1041: NamedQuery sbx = new NamedQuery(methodNamePostfix
1042: + id.getInfo().getJavaName() + "LE",
1043: "Selects row(s) with less or equal index value(s) ordered by index. Columns: "
1044: + columnList, false,
1045: "SELECT * FROM " + isb.toString(), id
1046: .getColumns(), parameters2, td
1047: .getColumnDescriptors(),
1048: generateMutators);
1049: setParameterTypes(te, id.getColumns(), sbx);
1050: te.setColTypes(sbx);
1051: sbx.setUseSqlTypes(te.getUseSqlTypes());
1052: if (te.getSmartBase() != null) {
1053: sbx.setSmartBaseName(te.getSmartBase());
1054: }
1055: namedStatements.add(injectVisibility(sbx));
1056: }
1057:
1058: if (id.getInfo().isLT()) {
1059: StringBuffer isb = new StringBuffer(tableName);
1060: isb.append(" WHERE ");
1061: String operator = "<";
1062: String descOperator = ">";
1063: String lastOperator = "<";
1064: String lastDescOperator = ">";
1065:
1066: Collection parameters2 = generateComparison(id,
1067: isb, operator, descOperator, lastOperator,
1068: lastDescOperator);
1069:
1070: NamedUpdate dbx = new NamedUpdate("Delete"
1071: + methodNamePostfix
1072: + id.getInfo().getJavaName() + "LT",
1073: "Deletes row(s) with less index value(s): "
1074: + columnList, "DELETE FROM "
1075: + isb.toString(), id.getColumns(),
1076: parameters2);
1077: setParameterTypes(te, id.getColumns(), dbx);
1078: dbx.setUseSqlTypes(te.getUseSqlTypes());
1079: namedStatements.add(injectVisibility(dbx));
1080:
1081: generateOrderBy(id, isb);
1082:
1083: NamedQuery sbx = new NamedQuery(methodNamePostfix
1084: + id.getInfo().getJavaName() + "LT",
1085: "Selects row(s) with less index value(s) ordered by index. Columns: "
1086: + columnList, false,
1087: "SELECT * FROM " + isb.toString(), id
1088: .getColumns(), parameters2, td
1089: .getColumnDescriptors(),
1090: generateMutators);
1091: setParameterTypes(te, id.getColumns(), sbx);
1092: te.setColTypes(sbx);
1093: sbx.setUseSqlTypes(te.getUseSqlTypes());
1094: if (te.getSmartBase() != null) {
1095: sbx.setSmartBaseName(te.getSmartBase());
1096: }
1097: namedStatements.add(injectVisibility(sbx));
1098: }
1099:
1100: if (id.getInfo().isOrdered()) {
1101: StringBuffer osb = new StringBuffer(tableName);
1102: generateOrderBy(id, osb);
1103:
1104: NamedQuery namedQuery = new NamedQuery(
1105: methodNamePostfix
1106: + id.getInfo().getJavaName()
1107: + "Ordered",
1108: "Selects ordered by index: " + columnList,
1109: false, "SELECT * FROM " + osb.toString(),
1110: null, null, td.getColumnDescriptors(),
1111: generateMutators);
1112:
1113: namedQuery.setUseSqlTypes(te.getUseSqlTypes());
1114: if (te.getSmartBase() != null) {
1115: namedQuery.setSmartBaseName(te.getSmartBase());
1116: }
1117:
1118: namedStatements.add(injectVisibility(namedQuery));
1119: }
1120: }
1121: }
1122: }
1123:
1124: private Collection subTasks = new ArrayList();
1125:
1126: /**
1127: * Subtask. Inherits output directory, documentation directory,
1128: * database connection, skipIndices attribute, outputXml attribute
1129: * and injected/generated interfaces from the parent task.
1130: * @ant.non-required
1131: * @return
1132: */
1133: public StatementCompilerTask createSqlc() {
1134: StatementCompilerTask ret = new StatementCompilerTask(this );
1135: subTasks.add(ret);
1136: return ret;
1137: }
1138:
1139: /**
1140: * @param namedStatements
1141: * @param td
1142: * @param te
1143: * @param tableName
1144: * @param generateMutators
1145: * @param methodNamePostfix
1146: * @throws GenerationException
1147: */
1148: private void generateImportedKeys(Collection namedStatements,
1149: TableDescriptor td, TableEntry te, String tableName,
1150: String methodNamePostfix) throws GenerationException {
1151: boolean generateMutators = te.getGenerateMutators();
1152: Iterator it = td.getImportedKeys().values().iterator();
1153: while (it.hasNext()) {
1154: KeyDescriptor kd = (KeyDescriptor) it.next();
1155: if (kd.getName() != null) {
1156: String columnList = getColumnList(kd);
1157:
1158: StringBuffer fksb = new StringBuffer(tableName);
1159: fksb.append(" WHERE ");
1160: Iterator icit = kd.getColumns().iterator();
1161: while (icit.hasNext()) {
1162: KeyEntry ke = (KeyEntry) icit.next();
1163: fksb.append(" ");
1164: fksb.append(ke.getColumnName());
1165: fksb.append("=?");
1166: if (icit.hasNext()) {
1167: fksb.append(" AND ");
1168: }
1169: }
1170:
1171: NamedQuery selectByFk = new NamedQuery(
1172: methodNamePostfix + "By" + kd.getName(),
1173: "Selects rows from " + tableName
1174: + " by foreign key " + kd.getDbName()
1175: + " columns value(s): " + columnList,
1176: false, "SELECT * FROM " + fksb.toString(), kd
1177: .getColumns(), null, td
1178: .getColumnDescriptors(),
1179: generateMutators);
1180: setParameterTypes(te, kd.getColumns(), selectByFk);
1181: te.setColTypes(selectByFk);
1182: selectByFk.setUseSqlTypes(te.getUseSqlTypes());
1183: namedStatements.add(injectVisibility(selectByFk));
1184: if (te.getSmartBase() != null) {
1185: selectByFk.setSmartBaseName(te.getSmartBase());
1186: }
1187:
1188: NamedUpdate deleteByFk = new NamedUpdate("Delete"
1189: + methodNamePostfix + "By" + kd.getName(),
1190: "Deletes rows from " + tableName
1191: + " by foreign key " + kd.getDbName()
1192: + " columns value(s): " + columnList,
1193: "DELETE FROM " + fksb.toString(), kd
1194: .getColumns(), null);
1195: setParameterTypes(te, kd.getColumns(), deleteByFk);
1196: deleteByFk.setUseSqlTypes(te.getUseSqlTypes());
1197: namedStatements.add(injectVisibility(deleteByFk));
1198:
1199: }
1200: }
1201: }
1202:
1203: /**
1204: * @param namedStatement
1205: */
1206: private NamedStatement injectVisibility(
1207: NamedStatement namedStatement) {
1208: namedStatement
1209: .setEngineMethodsVisibility(engineMethodsVisibility);
1210: namedStatement.setEngineVisibility(engineVisibility);
1211: return namedStatement;
1212: }
1213:
1214: /**
1215: * @param namedQuery
1216: */
1217: private NamedQuery injectVisibility(NamedQuery namedQuery) {
1218: injectVisibility((NamedStatement) namedQuery);
1219: namedQuery.setInterfaceImplVisibility(interfaceImplVisibility);
1220: return namedQuery;
1221: }
1222:
1223: /**
1224: *
1225: */
1226: private SQLProcessor getProcessor() {
1227: if (processor == null) {
1228: if (script != null) {
1229: try {
1230: processor = new SQLProcessor(
1231: new HypersonicInMemoryDataSource(
1232: (Reader) null), nameMap);
1233: processor.executeScript(new FileReader(script));
1234: } catch (ClassNotFoundException e) {
1235: throw new BuildException(
1236: "Hypersonic database driver is not found",
1237: e);
1238: } catch (IOException e) {
1239: throw new BuildException("Cannot read script", e);
1240: } catch (SQLException e) {
1241: throw new BuildException("Cannot execute script", e);
1242: }
1243: } else if (connectionEntry != null) {
1244: processor = connectionEntry.getProcessor(nameMap);
1245: } else if (parent != null) {
1246: processor = parent.getProcessor();
1247: } else {
1248: try {
1249: processor = new SQLProcessor(
1250: new HypersonicInMemoryDataSource(
1251: (Reader) null), nameMap);
1252: } catch (ClassNotFoundException e) {
1253: throw new BuildException(
1254: "Hypersonic driver is not found in the classpath",
1255: e);
1256: } catch (IOException e) {
1257: throw new BuildException(
1258: "Hypersonic in-memory database cannot be created",
1259: e);
1260: } catch (SQLException e) {
1261: throw new BuildException(
1262: "Hypersonic in-memory database cannot be created",
1263: e);
1264: }
1265: }
1266: }
1267:
1268: return processor;
1269: }
1270:
1271: /**
1272: * @param id
1273: * @return
1274: */
1275: private String getColumnList(IndexDescriptor id) {
1276: StringBuffer clb = new StringBuffer();
1277: Iterator iicit = id.getColumns().iterator();
1278: while (iicit.hasNext()) {
1279: KeyEntry ke = (KeyEntry) iicit.next();
1280: clb.append(ke.getColumnName());
1281: if (ke.isDescending()) {
1282: clb.append(" (DESC)");
1283: }
1284: if (iicit.hasNext()) {
1285: clb.append(", ");
1286: }
1287: }
1288:
1289: String columnList = clb.toString();
1290: return columnList;
1291: }
1292:
1293: /**
1294: * @param kd
1295: * @return
1296: */
1297: private String getColumnList(KeyDescriptor kd) {
1298: StringBuffer clb = new StringBuffer();
1299: Iterator iicit = kd.getColumns().iterator();
1300: while (iicit.hasNext()) {
1301: KeyEntry ke = (KeyEntry) iicit.next();
1302: clb.append(ke.getColumnName());
1303: if (ke.isDescending()) {
1304: clb.append(" (DESC)");
1305: }
1306: if (iicit.hasNext()) {
1307: clb.append(", ");
1308: }
1309: }
1310:
1311: String columnList = clb.toString();
1312: return columnList;
1313: }
1314:
1315: /**
1316: * @param te
1317: * @param parameters
1318: * @param statement
1319: * @throws GenerationException
1320: */
1321: private void setParameterTypes(final TableEntry te,
1322: Collection parameters, NamedStatement statement)
1323: throws GenerationException {
1324: Iterator it = parameters.iterator();
1325: for (int k = 1; it.hasNext(); k++) {
1326: Object next = it.next();
1327: if (next instanceof KeyEntry) {
1328: statement.setParamType(k, te
1329: .getColType((KeyEntry) next));
1330: } else if (next instanceof ColumnDescriptor) {
1331: statement.setParamType(k, te
1332: .getColType((ColumnDescriptor) next));
1333: } else {
1334: throw new GenerationException("Unexpected type: "
1335: + next.getClass());
1336: }
1337: }
1338: }
1339:
1340: /**
1341: * @param namedStatements
1342: * @param td
1343: * @param entityType
1344: * @param parameters
1345: * @param te
1346: * @throws GenerationException
1347: */
1348: private void generateUpdate(Collection namedStatements,
1349: TableDescriptor td, String methodNamePostfix,
1350: Collection parameters, TableEntry te)
1351: throws GenerationException {
1352: // Update
1353: if (columnsCount(td, te) > parameters.size()) {
1354: StringBuffer usb = new StringBuffer("UPDATE ");
1355: usb.append(td.getName());
1356: usb.append(" SET ");
1357:
1358: Collection uprm = new ArrayList();
1359: Iterator uit = td.getColumnDescriptors().iterator();
1360: while (uit.hasNext()) {
1361: ColumnDescriptor cd = (ColumnDescriptor) uit.next();
1362: if (!cd.isPrimaryKey()) {
1363: usb.append(cd.getDbName());
1364: usb.append("=?");
1365: if (uit.hasNext()) {
1366: usb.append(", ");
1367: }
1368: uprm.add(cd);
1369: }
1370: }
1371:
1372: usb.append(" WHERE ");
1373:
1374: uit = td.getColumnDescriptors().iterator();
1375: boolean addAnd = false;
1376: while (uit.hasNext()) {
1377: ColumnDescriptor cd = (ColumnDescriptor) uit.next();
1378: if (cd.isPrimaryKey()) {
1379: if (addAnd) {
1380: usb.append(" AND ");
1381: }
1382: usb.append(cd.getDbName());
1383: usb.append("=?");
1384: uprm.add(cd);
1385: addAnd = true;
1386: }
1387: }
1388:
1389: NamedParameterObjectUpdate update = new NamedParameterObjectUpdate(
1390: "Update" + methodNamePostfix,
1391: "Updates record with by primary key in "
1392: + td.getName(), usb.toString(), uprm, null,
1393: te.getGenerateMutators());
1394:
1395: update
1396: .setMode(NamedInterfaceGeneratingStatement.MODE_UPDATE);
1397: update.setTableName(td.getName());
1398: update.setUseSqlTypes(te.getUseSqlTypes());
1399: if (te.getSmartBase() != null) {
1400: update.setSmartBaseName(te.getSmartBase());
1401: }
1402:
1403: setParameterTypes(te, uprm, update);
1404: te.setColTypes(update);
1405:
1406: namedStatements.add(injectVisibility(update));
1407: }
1408: }
1409:
1410: /**
1411: * @param id
1412: * @param isb
1413: * @param operator
1414: * @param descOperator
1415: * @param lastOperator
1416: * @param lastDescOperator
1417: * @return
1418: */
1419: private Collection generateComparison(IndexDescriptor id,
1420: StringBuffer isb, String operator, String descOperator,
1421: String lastOperator, String lastDescOperator) {
1422: int pcounter = 0;
1423: Collection parameters2 = new ArrayList();
1424: Iterator icit = id.getColumns().iterator();
1425: while (icit.hasNext()) {
1426: KeyEntry ke = (KeyEntry) icit.next();
1427: isb.append(" ");
1428: isb.append(ke.getColumnName());
1429: if (icit.hasNext()) {
1430: isb.append(ke.isDescending() ? descOperator : operator);
1431: } else {
1432: isb.append(ke.isDescending() ? lastDescOperator
1433: : lastOperator);
1434: }
1435: isb.append("?");
1436: parameters2.add(ke);
1437: if (icit.hasNext()) {
1438: pcounter++;
1439: isb.append(" OR (");
1440: isb.append(ke.getColumnName());
1441: isb.append("=? AND (");
1442: parameters2.add(ke);
1443: }
1444: }
1445:
1446: while (pcounter-- > 0) {
1447: isb.append("))");
1448: }
1449: return parameters2;
1450: }
1451:
1452: /**
1453: * @param id
1454: * @param osb
1455: */
1456: private void generateOrderBy(IndexDescriptor id, StringBuffer osb) {
1457: osb.append(" ORDER BY ");
1458: Iterator icit = id.getColumns().iterator();
1459: while (icit.hasNext()) {
1460: KeyEntry ke = (KeyEntry) icit.next();
1461: osb.append(" ");
1462: osb.append(ke.getColumnName());
1463: if (ke.isDescending()) {
1464: osb.append(" DESC");
1465: }
1466: if (icit.hasNext()) {
1467: osb.append(", ");
1468: }
1469: }
1470: }
1471:
1472: /**
1473: * Directory to output HTML documentation.
1474: * @ant.non-required
1475: * @param documentation
1476: */
1477: public void setDocDir(File docDir) {
1478: if (docDir.exists() && docDir.isDirectory()) {
1479: this .docDir = docDir;
1480: } else {
1481: throw new BuildException(docDir.getAbsolutePath()
1482: + " does not exist or is not a directory");
1483: }
1484: }
1485:
1486: /**
1487: * Class name for master engine. Set this attribute if you want
1488: * all engine methods be held in one class and avoid generation of
1489: * an engine class per statement.
1490: * @ant.non-required
1491: * @param masterEngineName
1492: */
1493: public void setMasterEngine(String masterEngineName) {
1494: this .masterEngineName = masterEngineName;
1495: }
1496:
1497: /**
1498: * Table entry to generate statements from table metadata and then compile them.
1499: * @ant.non-required
1500: * @param generateForTables
1501: */
1502: public void addTable(TableEntry tableEntry) {
1503: tableEntry.setTask(this );
1504: tableEntries.add(tableEntry);
1505: }
1506:
1507: /**
1508: * @return
1509: */
1510: protected boolean isToBeGenerated(TableDescriptor td, TableEntry te) {
1511: return columnsCount(td, te) > 1;
1512: }
1513:
1514: /**
1515: * @param td
1516: * @param te
1517: * @return
1518: */
1519: private int columnsCount(TableDescriptor td, TableEntry te) {
1520: Iterator it = td.getColumnDescriptors().iterator();
1521: int count = 0;
1522: while (it.hasNext()) {
1523: Object o = it.next();
1524: if (!(o instanceof ColumnDescriptor && te
1525: .isSkipColumn((ColumnDescriptor) o))) {
1526: count++;
1527: }
1528: }
1529: return count;
1530: }
1531:
1532: private String smartBase;
1533:
1534: /**
1535: * @ant.ignore
1536: * @return Returns the smartBase.
1537: */
1538: String getSmartBase() {
1539: return smartBase;
1540: }
1541:
1542: /**
1543: * Base class (fully qualified name) for generated smart implementations. Default is biz.hammurapi.sql.DatabaseObject
1544: * The base class is supposed to have same constructors and methods as DatabaseObject. The idea is
1545: * to subclass DatabaseObject to introduce additional qualities like audit, metrics, security, ... whithout
1546: * changing class' contract. This setting is not inherited from the parent task.
1547: * @ant.non-required
1548: * @param smartBase The smartBase to set.
1549: */
1550: public void setSmartBase(String smartBase) {
1551: this.smartBase = smartBase;
1552: }
1553: }
|