KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ws > jaxme > js > pattern > VersionGenerator


1 /*
2  * Copyright 2003, 2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15
16  */

17 package org.apache.ws.jaxme.js.pattern;
18
19 import java.sql.Connection JavaDoc;
20 import java.sql.Date JavaDoc;
21 import java.sql.PreparedStatement JavaDoc;
22 import java.sql.ResultSet JavaDoc;
23 import java.sql.SQLException JavaDoc;
24 import java.sql.Time JavaDoc;
25 import java.sql.Timestamp JavaDoc;
26 import java.sql.Types JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Set JavaDoc;
34
35 import org.apache.ws.jaxme.js.DirectAccessible;
36 import org.apache.ws.jaxme.js.JavaComment;
37 import org.apache.ws.jaxme.js.JavaConstructor;
38 import org.apache.ws.jaxme.js.JavaField;
39 import org.apache.ws.jaxme.js.JavaInnerClass;
40 import org.apache.ws.jaxme.js.JavaMethod;
41 import org.apache.ws.jaxme.js.JavaQName;
42 import org.apache.ws.jaxme.js.JavaQNameImpl;
43 import org.apache.ws.jaxme.js.JavaSource;
44 import org.apache.ws.jaxme.js.LocalJavaField;
45 import org.apache.ws.jaxme.logging.Logger;
46 import org.apache.ws.jaxme.logging.LoggerAccess;
47 import org.apache.ws.jaxme.sqls.Column;
48 import org.apache.ws.jaxme.sqls.ColumnSet;
49 import org.apache.ws.jaxme.sqls.ForeignKey;
50 import org.apache.ws.jaxme.sqls.InsertStatement;
51 import org.apache.ws.jaxme.sqls.SelectStatement;
52 import org.apache.ws.jaxme.sqls.Table;
53
54
55 /** <p>The VersionGenerator is able to clone a version of a row
56  * in a database. That is nothing special. A simple INSERT does
57  * the same.</p>
58  * <p>The difference is that the VersionGenerator is able to
59  * clone rows in other tables referencing the cloned table
60  * as well, updating the references, and clone and update rows
61  * referencing these cloned and updated rows, and so on.</p>
62  * <p>In other words: The VersionGenerator derives a new
63  * version of a complex object stored in the database.</p>
64  * <p>The VersionGenerator operates on a list of tables. This
65  * list must not contain forward or self references. In other
66  * words: Under no circumstances may a table in the list contain
67  * a foreign key referencing another table, which follows later.</p>
68  *
69  * @author <a HREF="mailto:joe@ispsoft.de">Jochen Wiedmann</a>
70  */

71 public class VersionGenerator {
72     /** <p>The ColumnUpdater is able to update one or more columns in a
73      * table. Such a column update is required, because the cloned tables
74      * must not have the same primary key.</p>
75      * <p>The typical use is to specify a ColumnUpdater for the head
76      * table, which bumps the version number. For any child table
77      * you could specify a ColumnUpdater which generates a new primary
78      * key.</p>
79      */

80     public interface ColumnUpdater {
81         /** <p>Generates code for updating a table row. The row is supplied
82          * as an array of objects. The order of objects matches the columns
83          * of the given table.</p>
84          * @param pMethod The method being created; may be used to generate
85          * local fields, etc.
86          * @param pTableInfo The table being updated
87          * @param pMap A local Java field with a Map of rows, that have already
88          * been cloned. The keys and values are both instances of the generated
89          * inner class DataCache. The keys are holding the primary keys of
90          * the original rows (which have been cloned) and the values are the
91          * primary keys of the created rows (the generated clones).
92          * @param pConnection A local Java field with an open database connection
93          * @param pRow A local Java field with an array of values being cloned
94          */

95         public void update(JavaMethod pMethod, TableInfo pTableInfo,
96                            DirectAccessible pConnection,
97                            DirectAccessible pMap,
98                            DirectAccessible pRow);
99     }
100     
101     /** <p>This class is used internally to maintain the informations on
102      * the tables being cloned.</p>
103      */

104     public static class TableInfo {
105         private final Table table;
106         private final String JavaDoc propertyName;
107         private boolean isReferenced;
108         private boolean hasPrimaryKey;
109         private final List JavaDoc columnUpdaters = new ArrayList JavaDoc();
110         TableInfo(Table pTable, String JavaDoc pPropertyName) {
111             table = pTable;
112             propertyName = pPropertyName;
113         }
114         public Table getTable() { return table; }
115         public String JavaDoc getPropertyName() { return propertyName; }
116         public void add(ColumnUpdater pColumnUpdater) {
117             columnUpdaters.add(pColumnUpdater);
118         }
119         public Iterator JavaDoc getColumnUpdaters() { return columnUpdaters.iterator(); }
120         
121         public void setReferenced(boolean pReferenced) {
122             isReferenced = true;
123         }
124         public boolean isReferenced() {
125             return isReferenced;
126         }
127         public boolean hasPrimaryKey() {
128             return hasPrimaryKey;
129         }
130         public void setPrimaryKey(boolean pPrimaryKey) {
131             hasPrimaryKey = pPrimaryKey;
132         }
133     }
134     
135     /** <p>An implementation of {@link ColumnUpdater}, which updates foreign
136      * key references.</p>
137      */

138     private class ForeignKeyUpdater implements ColumnUpdater {
139         private final ForeignKey foreignKey;
140         private final TableInfo referencedTable;
141         
142         private ForeignKeyUpdater(ForeignKey pForeignKey, TableInfo pReferencedTable) {
143             foreignKey = pForeignKey;
144             referencedTable = pReferencedTable;
145         }
146         
147         public void update(JavaMethod pMethod, TableInfo pTableInfo,
148                            DirectAccessible pConnection,
149                            DirectAccessible pMap, DirectAccessible pRow) {
150             Table table = foreignKey.getTable();
151             List JavaDoc params = new ArrayList JavaDoc();
152             for (Iterator JavaDoc iter = foreignKey.getColumns(); iter.hasNext(); ) {
153                 Column primaryKeyColumn = (Column) iter.next();
154                 int index = -1;
155                 int i = 0;
156                 for (Iterator JavaDoc iter2 = table.getColumns(); iter2.hasNext(); ++i) {
157                     Column tableColumn = (Column) iter2.next();
158                     if (tableColumn.equals(primaryKeyColumn)) {
159                         index = i;
160                         break;
161                     }
162                 }
163                 if (index == -1) {
164                     throw new IllegalStateException JavaDoc("Key column " + primaryKeyColumn.getQName() + " not found.");
165                 }
166                 if (params.size() > 0) {
167                     params.add(" || ");
168                 }
169                 params.add(new Object JavaDoc[]{pRow, "[" + index + "] != null"});
170             }
171
172             pMethod.addIf(params);
173             LocalJavaField referencedKey = getCacheDataClassInstance(pMethod,
174                                                                      referencedTable,
175                                                                      foreignKey, pRow);
176             LocalJavaField mappedKey = pMethod.newJavaField(referencedKey.getType());
177             mappedKey.addLine("(", referencedKey.getType(), ") ", pMap, ".get(",
178                     referencedKey, ")");
179             pMethod.addIf(mappedKey, " == null");
180             pMethod.addThrowNew(IllegalStateException JavaDoc.class,
181                     JavaSource.getQuoted("Unknown reference: "), " + ",
182                     referencedKey);
183             pMethod.addEndIf();
184             int valNum = 0;
185             for (Iterator JavaDoc iter = referencedTable.getTable().getPrimaryKey().getColumns(); iter.hasNext(); ) {
186                 Column referencedKeyColumn = (Column) iter.next();
187                 Column localColumn = null;
188                 for (Iterator JavaDoc iter2 = foreignKey.getColumnLinks(); iter2.hasNext(); ) {
189                     ForeignKey.ColumnLink link = (ForeignKey.ColumnLink) iter2.next();
190                     if (link.getReferencedColumn().equals(referencedKeyColumn)) {
191                         localColumn = link.getLocalColumn();
192                         break;
193                     }
194                 }
195                 if (localColumn == null) {
196                     throw new IllegalStateException JavaDoc("Unable to find the column referencing " + referencedKeyColumn.getQName());
197                 }
198                 
199                 int colNum = -1, i = 0;
200                 for (Iterator JavaDoc iter2 = pTableInfo.getTable().getColumns(); iter2.hasNext(); ) {
201                     Column col = (Column) iter2.next();
202                     if (col.equals(localColumn)) {
203                         colNum = i;
204                         break;
205                     }
206                     ++i;
207                 }
208                 if (colNum == -1) {
209                     throw new IllegalStateException JavaDoc("Unable to find the column " + localColumn.getQName() + " in table " + pTableInfo.getTable().getQName());
210                 }
211                 pMethod.addLine(pRow, "[" + colNum + "] = ", mappedKey, ".getValues()[" + valNum++ + "];");
212             }
213
214             pMethod.addEndIf();
215         }
216     }
217     
218     private List JavaDoc tablesByOrder = new ArrayList JavaDoc();
219     private Map JavaDoc tablesByName = new HashMap JavaDoc();
220     private Set JavaDoc propertyNames = new HashSet JavaDoc();
221     private boolean generatingLogging;
222     
223     private boolean isReferencing(Table pReferencingTable, Table pReferencedTable) {
224         for (Iterator JavaDoc iter = pReferencingTable.getForeignKeys(); iter.hasNext(); ) {
225             ForeignKey foreignKey = (ForeignKey) iter.next();
226             if (foreignKey.getReferencedTable().equals(pReferencedTable)) {
227                 return true;
228             }
229         }
230         return false;
231     }
232     
233     /** <p>Returns whether the generator is creating logging statements. By default
234      * no logging statements are created.</p>
235      * <p>The default
236      * implementation creates logging statements suitable for the JaxMe logging
237      * package. To change this, create a subclass and overwrite the following
238      * methods: {@link #logEntering(JavaMethod, Object)},
239      * {@link #logExiting(JavaMethod, Object)}, {@link #logFinest(JavaMethod, Object, Object)},
240      * {@link #logFinestEntering(JavaMethod, Object)}, and
241      * {@link #logFinestExiting(JavaMethod, Object)}.</p>
242      * @see #setGeneratingLogging(boolean)
243      */

244     public boolean isGeneratingLogging() {
245         return generatingLogging;
246     }
247     
248     /** <p>Sets whether the generator is creating logging statements. By default
249      * no logging statements are created.</p>
250      * <p>The default
251      * implementation creates logging statements suitable for the JaxMe logging
252      * package. To change this, create a subclass and overwrite the following
253      * methods: {@link #logEntering(JavaMethod, Object)},
254      * {@link #logExiting(JavaMethod, Object)}, {@link #logFinest(JavaMethod, Object, Object)},
255      * {@link #logFinestEntering(JavaMethod, Object)}, and
256      * {@link #logFinestExiting(JavaMethod, Object)}.</p>
257      * @see #isGeneratingLogging()
258      */

259     public void setGeneratingLogging(boolean pGeneratingLogging) {
260         generatingLogging = pGeneratingLogging;
261     }
262     
263     /** <p>Creates the code for initialization of the logging framework.
264      * The default implementation generates code creating an instance of
265      * {@link org.apache.ws.jaxme.logging.Logger}.</p>
266      */

267     protected void initLogging(JavaSource pSource) {
268         if (isGeneratingLogging()) {
269             JavaField jf = pSource.newJavaField("logger", Logger.class, JavaSource.PRIVATE);
270             jf.setFinal(true);
271             jf.setStatic(true);
272             jf.addLine(LoggerAccess.class, ".getLogger(", pSource.getQName(), ".class)");
273         }
274     }
275     
276     /** <p>Creates code for logging the entrance into a method with
277      * fine level.</p>
278      * <p><em>Note:</em> The method should consider the {@link #isGeneratingLogging()}
279      * value.</p>
280      *
281      * @param pMethod The method in which a logging statement should be inserted
282      * @param pValues An array of additional values, possibly null
283      */

284     protected void logEntering(JavaMethod pMethod, Object JavaDoc pValues) {
285         if (isGeneratingLogging()) {
286             LocalJavaField mName = pMethod.newJavaField(String JavaDoc.class, "mName");
287             mName.addLine(JavaSource.getQuoted(pMethod.getLoggingSignature()));
288             mName.setFinal(true);
289             if (pValues == null) {
290                 pMethod.addLine("logger.entering(", mName, ");");
291             } else {
292                 pMethod.addLine("logger.entering(", mName, ", ", pValues, ");");
293             }
294         }
295     }
296     
297     /** <p>Creates code for logging the entrance into a method with
298      * finest level.</p>
299      * <p><em>Note:</em> The method should consider the {@link #isGeneratingLogging()}
300      * value.</p>
301      * <p><em>Implementation note:</em> The default implementation is
302      * equivalent to <code>logFinest(pMethod, "->", pValues)</code>.</p>
303      *
304      * @param pMethod The method in which a logging statement should be inserted
305      * @param pValues An array of additional values, possibly null
306      */

307     protected void logFinestEntering(JavaMethod pMethod, Object JavaDoc pValues) {
308         if (isGeneratingLogging()) {
309             LocalJavaField mName = pMethod.newJavaField(String JavaDoc.class, "mName");
310             mName.addLine(JavaSource.getQuoted(pMethod.getLoggingSignature()));
311             mName.setFinal(true);
312             logFinest(pMethod, JavaSource.getQuoted("->"), pValues);
313         }
314     }
315     
316     /** <p>Creates code for logging the exit from a method with
317      * fine level.</p>
318      * <p><em>Note:</em> The method should consider the {@link #isGeneratingLogging()}
319      * value.</p>
320      *
321      * @param pMethod The method in which a logging statement should be inserted
322      * @param pValues An array of additional values, possibly null
323      */

324     protected void logExiting(JavaMethod pMethod, Object JavaDoc pValues) {
325         if (isGeneratingLogging()) {
326             if (pValues == null) {
327                 pMethod.addLine("logger.exiting(mName);");
328             } else {
329                 pMethod.addLine("logger.exiting(mName, ", pValues, ");");
330             }
331         }
332     }
333     
334     /** <p>Creates code for logging the exit from a method with
335      * fine level.</p>
336      * <p><em>Note:</em> The method should consider the {@link #isGeneratingLogging()}
337      * value.</p>
338      * <p><em>Implementation note:</em> The default implementation is
339      * equivalent to <code>logFinest(pMethod, "<-", pValues)</code>.</p>
340      *
341      * @param pMethod The method in which a logging statement should be inserted
342      * @param pValues An array of additional values, possibly null
343      */

344     protected void logFinestExiting(JavaMethod pMethod, Object JavaDoc pValues) {
345         logFinest(pMethod, JavaSource.getQuoted("->"), pValues);
346     }
347     
348     /** <p>Creates code for logging a message with finest level.</p>
349      * <p><em>Note:</em> The method should consider the {@link #isGeneratingLogging()}
350      * value.</p>
351      *
352      * @param pMethod The method in which a logging statement should be inserted
353      * @param pMsg The message being logged
354      * @param pValues An array of additional values, possibly null
355      */

356     protected void logFinest(JavaMethod pMethod, Object JavaDoc pMsg, Object JavaDoc pValues) {
357         if (isGeneratingLogging()) {
358             if (pValues == null) {
359                 pMethod.addLine("logger.finest(mName, ", pMsg, ");");
360             } else {
361                 pMethod.addLine("logger.finest(mName, ", pMsg, ", ", pValues, ");");
362             }
363         }
364     }
365     
366     /** Adds a new table to the list of tables. The table must not
367      * contain a forward reference. Additionally, the table must not
368      * be referenced by any other table, which has already been added
369      * to the list.</p>
370      * @param pTable The table being cloned
371      * @param pUpdater The column updater to use for changing the
372      * updated columns.
373      */

374     public void addTable(Table pTable, ColumnUpdater pUpdater) {
375         String JavaDoc name = pTable.getQName();
376         if (tablesByName.containsKey(pTable)) {
377             throw new IllegalStateException JavaDoc("A table " + name + " has already been added.");
378         }
379         if (isReferencing(pTable, pTable)) {
380             throw new IllegalStateException JavaDoc("The table " + name + " is containing self references.");
381         }
382         for (Iterator JavaDoc iter = tablesByOrder.iterator(); iter.hasNext(); ) {
383             TableInfo tableInfo = (TableInfo) iter.next();
384             if (isReferencing(tableInfo.getTable(), pTable)) {
385                 throw new IllegalStateException JavaDoc("The table " + tableInfo.getTable().getQName() +
386                         " contains a forward reference to the table " + name + ".");
387             }
388         }
389         
390         String JavaDoc s = pTable.getName().getName();
391         String JavaDoc t = s;
392         int num = 0;
393         for (;;) {
394             t = Character.toUpperCase(t.charAt(0)) + t.substring(1);
395             if (!propertyNames.contains(t)) {
396                 break;
397             }
398             t = s + num++;
399         }
400         propertyNames.add(t);
401         
402         TableInfo tableInfo = new TableInfo(pTable, t);
403         tableInfo.setPrimaryKey(pTable.getPrimaryKey() != null);
404         if (pUpdater != null) {
405             tableInfo.add(pUpdater);
406         }
407         
408         for (Iterator JavaDoc iter = pTable.getForeignKeys(); iter.hasNext(); ) {
409             ForeignKey foreignKey = (ForeignKey) iter.next();
410             Table referencedTable = foreignKey.getReferencedTable();
411             for (Iterator JavaDoc iter2 = tablesByOrder.iterator(); iter2.hasNext(); ) {
412                 TableInfo referencedTableInfo = (TableInfo) iter2.next();
413                 if (referencedTableInfo.getTable().equals(referencedTable)) {
414                     ColumnUpdater columnUpdater = new ForeignKeyUpdater(foreignKey, referencedTableInfo);
415                     tableInfo.add(columnUpdater);
416                     if (!referencedTableInfo.hasPrimaryKey()) {
417                         throw new IllegalStateException JavaDoc("The table " + pTable.getQName() +
418                                 " is referencing table " + referencedTable.getQName() +
419                         ", which doesn't have a primary key.");
420                     }
421                     referencedTableInfo.setReferenced(true);
422                 }
423             }
424         }
425         
426         tablesByName.put(name, tableInfo);
427         tablesByOrder.add(tableInfo);
428     }
429     
430     /** <p>Returns the name of the inner class CacheData.</p>
431      */

432     protected JavaQName getCacheDataClassName(JavaQName pQName) {
433         return JavaQNameImpl.getInnerInstance(pQName, "CacheData");
434     }
435     
436     /** <p>Generates the innner class CacheData.</p>
437      */

438     protected JavaInnerClass getCacheDataClass(JavaSource pSource) {
439         JavaInnerClass jic = pSource.newJavaInnerClass("CacheData", JavaSource.PRIVATE);
440         
441         JavaField name = jic.newJavaField("name", String JavaDoc.class, JavaSource.PRIVATE);
442         name.setFinal(true);
443         JavaField values = jic.newJavaField("values", Object JavaDoc[].class, JavaSource.PRIVATE);
444         values.setFinal(true);
445         
446         JavaConstructor jcon = jic.newJavaConstructor(JavaSource.PRIVATE);
447         DirectAccessible pName = jcon.addParam(String JavaDoc.class, "pName");
448         DirectAccessible pValues = jcon.addParam(Object JavaDoc[].class, "pValues");
449         jcon.addLine(name, " = ", pName, ";");
450         jcon.addLine(values, " = ", pValues, ";");
451         
452         JavaMethod getNameMethod = jic.newJavaMethod("getName", String JavaDoc.class, JavaSource.PUBLIC);
453         getNameMethod.addLine("return ", name, ";");
454         
455         JavaMethod getValuesMethod = jic.newJavaMethod("getValues", Object JavaDoc[].class, JavaSource.PUBLIC);
456         getValuesMethod.addLine("return ", values, ";");
457         
458         {
459             JavaMethod jm = jic.newJavaMethod("toString", String JavaDoc.class, JavaSource.PUBLIC);
460             LocalJavaField sb = jm.newJavaField(StringBuffer JavaDoc.class, "sb");
461             sb.addLine("new ", StringBuffer JavaDoc.class, "(", name, ")");
462             DirectAccessible loopVar = jm.addForArray(values);
463             jm.addLine(sb, ".append(", JavaSource.getQuoted(", "), ").append(",
464                     values, "[", loopVar, "]);");
465             jm.addEndFor();
466             jm.addLine("return ", sb, ".toString();");
467             
468         }
469         
470         {
471             JavaMethod jm = jic.newJavaMethod("hashCode", int.class, JavaSource.PUBLIC);
472             LocalJavaField hashCodeResult = jm.newJavaField(int.class, "result");
473             hashCodeResult.addLine(name, ".hashCode() + ", values, ".length;");
474             DirectAccessible loopVar = jm.addForArray(values);
475             LocalJavaField o = jm.newJavaField(Object JavaDoc.class, "o");
476             o.addLine(values, "[", loopVar, "]");
477             jm.addIf(o, " != null");
478             jm.addLine(hashCodeResult, " += ", o, ".hashCode();");
479             jm.addEndIf();
480             jm.addEndFor();
481             jm.addLine("return ", hashCodeResult, ";");
482         }
483         
484         {
485             JavaMethod jm = jic.newJavaMethod("equals", boolean.class, JavaSource.PUBLIC);
486             DirectAccessible o = jm.addParam(Object JavaDoc.class, "o");
487             jm.addIf(o, " == null || !(", o, " instanceof ", jic.getQName(), ")");
488             jm.addLine("return false;");
489             jm.addEndIf();
490             LocalJavaField other = jm.newJavaField(jic.getQName(), "other");
491             other.addLine("(", jic.getQName(), ") ", o);
492             jm.addIf("!", name, ".equals(", other, ".name) || ", values, ".length != ",
493                     other, ".values.length");
494             jm.addLine("return false;");
495             jm.addEndIf();
496             DirectAccessible loopVar = jm.addForArray(values);
497             LocalJavaField v = jm.newJavaField(Object JavaDoc.class, "v");
498             v.addLine(values, "[", loopVar, "]");
499             jm.addIf(v, " == null");
500             jm.addIf(other, ".values[", loopVar, "] != null");
501             jm.addLine("return false;");
502             jm.addEndIf();
503             jm.addElse();
504             jm.addIf("!", v, ".equals(", other, ".values[", loopVar, "])");
505             jm.addLine("return false;");
506             jm.addEndIf();
507             jm.addEndIf();
508             jm.addEndFor();
509             jm.addLine("return true;");
510         }
511         
512         return jic;
513     }
514     
515     /** <p>Generates code for reading a ResultSet's column.</p>
516      */

517     protected void setResultSetValue(JavaMethod pMethod, Column pColumn,
518                                      DirectAccessible pResultSet,
519                                      int pColumnNum, Object JavaDoc pTarget) {
520         Integer JavaDoc p = new Integer JavaDoc(pColumnNum+1);
521         
522         Column.Type type = pColumn.getType();
523         if (Column.Type.BIGINT.equals(type)) {
524             LocalJavaField l = pMethod.newJavaField(long.class);
525             l.addLine(pResultSet, ".getLong(", p, ");");
526             pMethod.addIf("!", pResultSet, ".wasNull()");
527             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
528                     Long JavaDoc.class, "(", l, ");");
529             pMethod.addEndIf();
530         } else if (pColumn.isBinaryColumn()) {
531             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ",
532                     pResultSet, ".getBytes(", p, ");");
533         } else if (Column.Type.BIT.equals(type)) {
534             LocalJavaField b = pMethod.newJavaField(boolean.class);
535             b.addLine(pResultSet, ".getBoolean(", p, ");");
536             pMethod.addIf("!", pResultSet, ".wasNull()");
537             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ", b,
538                     " ? ", Boolean JavaDoc.class, ".TRUE : ", Boolean JavaDoc.class, ".FALSE;");
539             pMethod.addEndIf();
540         } else if (pColumn.isStringColumn()) {
541             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ",
542                     pResultSet, ".getString(", p, ");");
543         } else if (Column.Type.DATE.equals(type)) {
544             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ",
545                     pResultSet, ".getDate(", p, ");");
546         } else if (Column.Type.DOUBLE.equals(type)) {
547             LocalJavaField d = pMethod.newJavaField(double.class);
548             d.addLine(pResultSet, ".getDouble(", p, ");");
549             pMethod.addIf("!", pResultSet, ".wasNull()");
550             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
551                     Double JavaDoc.class, "(", d, ");");
552             pMethod.addEndIf();
553         } else if (Column.Type.FLOAT.equals(type)) {
554             LocalJavaField f = pMethod.newJavaField(float.class);
555             f.addLine(pResultSet, ".getFloat(", p, ");");
556             pMethod.addIf("!", pResultSet, ".wasNull()");
557             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
558                     Float JavaDoc.class, "(", f, ");");
559             pMethod.addEndIf();
560         } else if (Column.Type.INTEGER.equals(type)) {
561             LocalJavaField i = pMethod.newJavaField(int.class);
562             i.addLine(pResultSet, ".getInt(", p, ");");
563             pMethod.addIf("!", pResultSet, ".wasNull()");
564             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
565                     Integer JavaDoc.class, "(", i, ");");
566             pMethod.addEndIf();
567         } else if (Column.Type.SMALLINT.equals(type)) {
568             LocalJavaField s = pMethod.newJavaField(short.class);
569             s.addLine(pResultSet, ".getShort(", p, ");");
570             pMethod.addIf("!", pResultSet, ".wasNull()");
571             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
572                     Short JavaDoc.class, "(", s, ");");
573             pMethod.addEndIf();
574         } else if (Column.Type.TIME.equals(type)) {
575             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ",
576                     pResultSet, ".getTime(", p, ");");
577         } else if (Column.Type.TIMESTAMP.equals(type)) {
578             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = ",
579                     pResultSet, ".getTimestamp(", p, ");");
580         } else if (Column.Type.TINYINT.equals(type)) {
581             LocalJavaField b = pMethod.newJavaField(short.class);
582             b.addLine(pResultSet, ".getByte(", p, ");");
583             pMethod.addIf("!", pResultSet, ".wasNull()");
584             pMethod.addLine(pTarget, "[", Integer.toString(pColumnNum), "] = new ",
585                     Byte JavaDoc.class, "(", b, ");");
586             pMethod.addEndIf();
587         } else {
588             throw new IllegalStateException JavaDoc("Unknown column type: " + type);
589         }
590     }
591     
592     /** <p>Generates code for setting a PreparedStatement's parameter.</p>
593      */

594     protected void setPreparedStatementValue(JavaMethod pMethod, Column pColumn,
595                                              Object JavaDoc pStmt, Object JavaDoc pParamNum,
596                                              Object JavaDoc pValue) {
597         if (!(pValue instanceof DirectAccessible)) {
598             LocalJavaField v = pMethod.newJavaField(Object JavaDoc.class);
599             v.addLine(pValue);
600             pValue = v;
601         }
602         
603         Column.Type type = pColumn.getType();
604         pMethod.addIf(pValue, " == null");
605         pMethod.addLine(pStmt, ".setNull(", pParamNum, ", ", Types JavaDoc.class, ".",
606                 type, ");");
607         pMethod.addElse();
608         if (Column.Type.BIGINT.equals(type)) {
609             pMethod.addLine(pStmt, ".setLong(", pParamNum, ", ((", Long JavaDoc.class,
610                     ") ", pValue, ").longValue());");
611         } else if (pColumn.isBinaryColumn()) {
612             pMethod.addLine(pStmt, ".setBytes(", pParamNum, ", (", byte[].class,
613                     ") ", pValue, ");");
614         } else if (Column.Type.BIT.equals(type)) {
615             pMethod.addLine(pStmt, ".setBoolean(", pParamNum, ", ((", Boolean JavaDoc.class,
616                     ") ", pValue, ").booleanValue());");
617         } else if (pColumn.isStringColumn()) {
618             pMethod.addLine(pStmt, ".setString(", pParamNum, ", (", String JavaDoc.class,
619                     ") ", pValue, ");");
620         } else if (Column.Type.DATE.equals(type)) {
621             pMethod.addLine(pStmt, ".setDate(", pParamNum, ", (", Date JavaDoc.class,
622                     ") ", pValue, ");");
623         } else if (Column.Type.DOUBLE.equals(type)) {
624             pMethod.addLine(pStmt, ".setDouble(", pParamNum, ", ((", Double JavaDoc.class,
625                     ") ", pValue, ").doubleValue());");
626         } else if (Column.Type.FLOAT.equals(type)) {
627             pMethod.addLine(pStmt, ".setFloat(", pParamNum, ", ((", Float JavaDoc.class,
628                     ") ", pValue, ").floatValue());");
629         } else if (Column.Type.INTEGER.equals(type)) {
630             pMethod.addLine(pStmt, ".setInt(", pParamNum, ", ((", Integer JavaDoc.class,
631                     ") ", pValue, ").intValue());");
632         } else if (Column.Type.SMALLINT.equals(type)) {
633             pMethod.addLine(pStmt, ".setShort(", pParamNum, ", ((", Short JavaDoc.class,
634                     ") ", pValue, ").shortValue());");
635         } else if (Column.Type.TIME.equals(type)) {
636             pMethod.addLine(pStmt, ".setTime(", pParamNum, ", (", Time JavaDoc.class,
637                     ") ", pValue, ");");
638         } else if (Column.Type.TIMESTAMP.equals(type)) {
639             pMethod.addLine(pStmt, ".setTimestamp(", pParamNum, ", (", Timestamp JavaDoc.class,
640                     ") ", pValue, ");");
641         } else if (Column.Type.TINYINT.equals(type)) {
642             pMethod.addLine(pStmt, ".setByte(", pParamNum, ", ((", Byte JavaDoc.class, ") ",
643                     pValue, ").byteValue());");
644         } else {
645             throw new IllegalStateException JavaDoc("Unknown column type: " + type);
646         }
647         
648         pMethod.addEndIf();
649     }
650     
651     /** <p>Generates code for reading all rows matching the given key.</p>
652      */

653     protected void getSelectRowsCode(JavaMethod pMethod,
654                                      TableInfo pTableInfo,
655                                      ColumnSet pColumnSet,
656                                      DirectAccessible pConn,
657                                      DirectAccessible pMap,
658                                      DirectAccessible pValues,
659                                      boolean pReturnValue) {
660         Table table = pTableInfo.getTable();
661         SelectStatement statement = table.getSelectStatement();
662         statement.getWhere().addColumnSetQuery(pColumnSet, statement.getTableReference());
663         String JavaDoc s = table.getSchema().getSQLFactory().newSQLGenerator().getQuery(statement);
664         Object JavaDoc query = JavaSource.getQuoted(s);
665         if (isGeneratingLogging()) {
666             LocalJavaField q = pMethod.newJavaField(String JavaDoc.class);
667             q.addLine(query);
668             query = q;
669             logFinest(pMethod, query, pValues);
670         }
671         
672         LocalJavaField stmt = pMethod.newJavaField(PreparedStatement JavaDoc.class);
673         stmt.addLine(pConn, ".prepareStatement(", JavaSource.getQuoted(s), ");");
674         LocalJavaField isStmtClosed = pMethod.newJavaField(boolean.class);
675         isStmtClosed.addLine("false");
676         pMethod.addTry();
677         
678         int paramNum = 0;
679         for (Iterator JavaDoc iter = pColumnSet.getColumns(); iter.hasNext(); ) {
680             Object JavaDoc v = new Object JavaDoc[]{pValues, "[", Integer.toString(paramNum), "]"};
681             Column column = (Column) iter.next();
682             setPreparedStatementValue(pMethod, column, stmt, Integer.toString(++paramNum), v);
683         }
684         LocalJavaField rs = pMethod.newJavaField(ResultSet JavaDoc.class, "rs");
685         rs.addLine(stmt, ".executeQuery()");
686         LocalJavaField isRsClosed = pMethod.newJavaField(boolean.class);
687         isRsClosed.addLine("false");
688         LocalJavaField result;
689         if (pReturnValue) {
690             result = pMethod.newJavaField(Object JavaDoc[].class, "result");
691             result.addLine("null");
692         } else {
693             result = null;
694         }
695         pMethod.addTry();
696         
697         pMethod.addWhile(rs, ".next()");
698         if (result != null) {
699             pMethod.addIf(result, " != null");
700             pMethod.addThrowNew(IllegalStateException JavaDoc.class,
701                     JavaSource.getQuoted("Expected a single row only."));
702             pMethod.addEndIf();
703         }
704         
705         int resultColumnSize = 0;
706         for (Iterator JavaDoc iter = table.getColumns(); iter.hasNext(); iter.next()) {
707             ++resultColumnSize;
708         }
709         LocalJavaField row = pMethod.newJavaField(Object JavaDoc[].class, "row");
710         row.addLine("new ", Object JavaDoc.class, "[" + resultColumnSize + "];");
711         
712         int resultColumnNum = 0;
713         for (Iterator JavaDoc iter = table.getColumns(); iter.hasNext(); ) {
714             Column column = (Column) iter.next();
715             setResultSetValue(pMethod, column, rs, resultColumnNum++, row);
716         }
717         
718         pMethod.addLine(((result == null) ? "" : "result = "),
719                 getInsertRowMethodName(pTableInfo), "(", pConn, ", ", pMap, ", ",
720                 row, ");");
721         
722         pMethod.addEndWhile();
723         pMethod.addLine(isRsClosed, " = true;");
724         pMethod.addLine(rs, ".close();");
725         
726         pMethod.addFinally();
727         pMethod.addIf("!", isRsClosed);
728         pMethod.addTry();
729         pMethod.addLine(rs, ".close();");
730         pMethod.addCatch(Throwable JavaDoc.class, "pIgnore");
731         pMethod.addEndTry();
732         pMethod.addEndIf();
733         pMethod.addEndTry();
734         
735         pMethod.addLine(isStmtClosed, " = true;");
736         pMethod.addLine(stmt, ".close();");
737         if (result != null) {
738             logExiting(pMethod, result);
739             pMethod.addLine("return ", result, ";");
740         } else {
741             logFinestExiting(pMethod, null);
742         }
743         
744         pMethod.addFinally();
745         pMethod.addIf("!", isStmtClosed);
746         pMethod.addTry();
747         pMethod.addLine(stmt, ".close();");
748         pMethod.addCatch(Throwable JavaDoc.class, "pIgnore");
749         pMethod.addEndTry();
750         pMethod.addEndIf();
751         pMethod.addEndTry();
752         
753     }
754     
755     /** <p>Returns the name of the method for cloning one row from the
756      * given table.</p>
757      */

758     protected String JavaDoc getInsertRowMethodName(TableInfo pTableInfo) {
759         return "clone" + pTableInfo.getPropertyName() + "Row";
760     }
761     
762     /** <p>Creates an instance of the inner class CacheData by reading
763      * the key from the given row.</p>
764      */

765     protected LocalJavaField getCacheDataClassInstance(JavaMethod pMethod,
766                                                        TableInfo pTableInfo,
767                                                        ColumnSet pColumnSet,
768                                                        DirectAccessible pValues) {
769         Table table = pColumnSet.getTable();
770         List JavaDoc params = new ArrayList JavaDoc();
771         for (Iterator JavaDoc iter = pColumnSet.getColumns(); iter.hasNext(); ) {
772             Column primaryKeyColumn = (Column) iter.next();
773             int index = -1;
774             int i = 0;
775             for (Iterator JavaDoc iter2 = table.getColumns(); iter2.hasNext(); ++i) {
776                 Column tableColumn = (Column) iter2.next();
777                 if (tableColumn.equals(primaryKeyColumn)) {
778                     index = i;
779                     break;
780                 }
781             }
782             if (index == -1) {
783                 throw new IllegalStateException JavaDoc("Key column " + primaryKeyColumn.getQName() + " not found.");
784             }
785             if (params.size() > 0) {
786                 params.add(", ");
787             }
788             params.add(new Object JavaDoc[]{pValues, "[" + index + "]"});
789         }
790         
791         JavaQName cacheDataClass = getCacheDataClassName(pMethod.getJavaSource().getQName());
792         LocalJavaField jf = pMethod.newJavaField(cacheDataClass);
793         jf.addLine("new ", cacheDataClass, "(",
794                    JavaSource.getQuoted(pTableInfo.getPropertyName()),
795                    ", new ", Object JavaDoc[].class, "{", params, "})");
796         return jf;
797     }
798
799     /** <p>Updates a row by reading the values from an instance of the inner
800      * class CacheData.</p>
801      */

802     protected void getApplyCacheData(JavaMethod pMethod,
803                                      TableInfo pTableInfo,
804                                      ColumnSet pColumnSet,
805                                      DirectAccessible pRow,
806                                      DirectAccessible pData) {
807         Table table = pTableInfo.getTable();
808         for (Iterator JavaDoc iter = pColumnSet.getColumns(); iter.hasNext(); ) {
809             Column primaryKeyColumn = (Column) iter.next();
810             int index = -1;
811             int i = 0;
812             for (Iterator JavaDoc iter2 = table.getColumns(); iter2.hasNext(); ++i) {
813                 Column tableColumn = (Column) iter2.next();
814                 if (tableColumn.equals(primaryKeyColumn)) {
815                     index = i;
816                     break;
817                 }
818             }
819             if (index == -1) {
820                 throw new IllegalStateException JavaDoc("Key column " + primaryKeyColumn.getQName() + " not found.");
821             }
822             pMethod.addLine(pRow, "[" + index + "] = ", pData, "[" + (i+1) + "];");
823         }
824     }
825     
826     /** <p>Creates a method for cloning one row from the given table.</p>
827      */

828     protected JavaMethod getInsertRowMethod(JavaSource pSource, TableInfo pTableInfo) {
829         Table table = pTableInfo.getTable();
830         JavaMethod jm = pSource.newJavaMethod(getInsertRowMethodName(pTableInfo),
831                 Object JavaDoc[].class, JavaSource.PRIVATE);
832         jm.addThrows(SQLException JavaDoc.class);
833         DirectAccessible connection = jm.addParam(Connection JavaDoc.class, "pConn");
834         DirectAccessible map = jm.addParam(Map JavaDoc.class, "pMap");
835         DirectAccessible values = jm.addParam(Object JavaDoc[].class, "pValue");
836         
837         logFinestEntering(jm, values);
838         
839         LocalJavaField baseKey = null;
840         if (table.getPrimaryKey() != null) {
841             baseKey = getCacheDataClassInstance(jm, pTableInfo,
842                     table.getPrimaryKey(), values);
843             jm.addIf(map, ".containsKey(", baseKey, ")");
844             logFinestExiting(jm, JavaSource.getQuoted("null (Already cloned)"));
845             jm.addLine("return null;");
846             jm.addEndIf();
847         }
848         
849         for (Iterator JavaDoc iter = pTableInfo.getColumnUpdaters(); iter.hasNext(); ) {
850             ((ColumnUpdater) iter.next()).update(jm, pTableInfo, connection, map, values);
851         }
852         
853         jm.addLine(jm.getName(), "(", connection, ", ", values, ");");
854         
855         if (baseKey != null) {
856             LocalJavaField clonedKey = getCacheDataClassInstance(jm, pTableInfo,
857                     table.getPrimaryKey(),
858                     values);
859             jm.addLine(map, ".put(", baseKey, ", ", clonedKey, ");");
860         }
861         
862         
863         // Clone objects referencing this object
864
LocalJavaField referencedValues = null;
865         for (Iterator JavaDoc referencingIter = tablesByOrder.iterator(); referencingIter.hasNext(); ) {
866             TableInfo prevTableInfo = (TableInfo) referencingIter.next();
867             Table prevTable = prevTableInfo.getTable();
868             for (Iterator JavaDoc fkIter = prevTable.getForeignKeys(); fkIter.hasNext(); ) {
869                 ForeignKey fk = (ForeignKey) fkIter.next();
870                 if (fk.getReferencedTable().equals(table)) {
871                     if (referencedValues == null) {
872                         referencedValues = jm.newJavaField(Object JavaDoc[].class);
873                         referencedValues.addLine(baseKey, ".getValues()");
874                     }
875                     getSelectRowsCode(jm, prevTableInfo, fk, connection, map, referencedValues, false);
876                 }
877             }
878         }
879         
880         logFinestExiting(jm, values);
881         jm.addLine("return ", values, ";");
882         
883         return jm;
884     }
885     
886     /** <p>Creates a method for cloning one row from the given table.</p>
887      */

888     protected JavaMethod getInnerInsertRowMethod(JavaSource pSource, TableInfo pTableInfo) {
889         Table table = pTableInfo.getTable();
890         JavaMethod jm = pSource.newJavaMethod(getInsertRowMethodName(pTableInfo),
891                 JavaQNameImpl.VOID, JavaSource.PRIVATE);
892         jm.addThrows(SQLException JavaDoc.class);
893         DirectAccessible connection = jm.addParam(Connection JavaDoc.class, "pConn");
894         DirectAccessible values = jm.addParam(Object JavaDoc[].class, "pValue");
895         
896         logFinestEntering(jm, null);
897         
898         InsertStatement insertStatement = table.getInsertStatement();
899         String JavaDoc s = table.getSchema().getSQLFactory().newSQLGenerator().getQuery(insertStatement);
900         Object JavaDoc query = JavaSource.getQuoted(s);
901         if (isGeneratingLogging()) {
902             LocalJavaField q = jm.newJavaField(String JavaDoc.class);
903             q.addLine(query);
904             query = q;
905             logFinest(jm, query, values);
906         }
907         LocalJavaField stmt = jm.newJavaField(PreparedStatement JavaDoc.class, "stmt");
908         stmt.setFinal(true);
909         stmt.addLine(connection, ".prepareStatement(", query, ");");
910         LocalJavaField isStmtClosed = jm.newJavaField(boolean.class, "isStmtClosed");
911         isStmtClosed.addLine("false");
912         jm.addTry();
913         
914         int paramNum = 0;
915         for (Iterator JavaDoc iter = table.getColumns(); iter.hasNext(); ) {
916             Column column = (Column) iter.next();
917             Object JavaDoc v = new Object JavaDoc[]{ values, "[", Integer.toString(paramNum), "]" };
918             setPreparedStatementValue(jm, column, stmt, Integer.toString(++paramNum), v);
919         }
920         
921         jm.addLine(stmt, ".executeUpdate();");
922         jm.addLine(isStmtClosed, " = true;");
923         jm.addLine(stmt, ".close();");
924         
925         jm.addFinally();
926         jm.addIf("!", isStmtClosed);
927         jm.addTry();
928         jm.addLine(stmt, ".close();");
929         jm.addCatch(Throwable JavaDoc.class, "ignore");
930         jm.addEndTry();
931         jm.addEndIf();
932         jm.addEndTry();
933         
934         logFinestExiting(jm, null);
935         return jm;
936     }
937     
938     
939     /** <p>Actually creates the public "clone" method.</p>
940      */

941     protected JavaMethod getPublicCloneMethod(JavaSource pSource) {
942         TableInfo headTable;
943         {
944             Iterator JavaDoc iter = tablesByOrder.iterator();
945             if (!iter.hasNext()) {
946                 throw new IllegalStateException JavaDoc("No tables have been added.");
947             }
948             headTable = (TableInfo) iter.next();
949         }
950         
951         JavaQName resultType = JavaQNameImpl.getInstance(Object JavaDoc[].class);
952         JavaMethod jm = pSource.newJavaMethod("clone", resultType, JavaSource.PUBLIC);
953         jm.addThrows(SQLException JavaDoc.class);
954         JavaComment jc = jm.newComment();
955         jc.addLine("<p>This method takes as input the key values of a row in the table " +
956                 headTable.getTable().getQName() + ".");
957         jc.addLine("The key values are given by the array <code>pRow</code>:");
958         jc.addLine("<ul>");
959         int i = 0;
960         for (Iterator JavaDoc iter = headTable.getTable().getPrimaryKey().getColumns();
961                 iter.hasNext(); i++) {
962             Column col = (Column) iter.next();
963             jc.addLine(" <li><code>pRow[" + i+ "] = " + col.getQName() + "</code></li>");
964         }
965         jc.addLine("</ul>");
966         jc.addLine("The method updates the rows version number and creates a new row");
967         jc.addLine("with the updated values.</p>");
968         {
969             Iterator JavaDoc iter = tablesByOrder.iterator();
970             iter.next();
971             if (iter.hasNext()) {
972                 jc.addLine("<p>Once the new row is created, the method searches for entries");
973                 jc.addLine("referencing the old row in the following tables:");
974                 jc.addLine("<table>");
975                 do {
976                     TableInfo subTable = (TableInfo) iter.next();
977                     jc.addLine(" <tr><td>" + subTable.getTable().getQName() + "</td></tr>");
978                 } while (iter.hasNext());
979                 jc.addLine("All the referencing entries are cloned as well, with updated");
980                 jc.addLine("references.");
981             }
982         }
983         
984         DirectAccessible conn = jm.addParam(Connection JavaDoc.class, "pConn");
985         DirectAccessible row = jm.addParam(resultType, "pRow");
986         
987         logEntering(jm, new Object JavaDoc[]{"new ", Object JavaDoc[].class, "{", conn, ", ", row, "}"});
988         
989         LocalJavaField map = jm.newJavaField(Map JavaDoc.class, "clonedObjects");
990         map.addLine("new ", HashMap JavaDoc.class, "()");
991         
992         getSelectRowsCode(jm, headTable, headTable.getTable().getPrimaryKey(),
993                 conn, map, row, true);
994         return jm;
995     }
996     
997     /** <p>Creates a method for updating one row in the head table.</p>
998      */

999     public JavaMethod getCloneMethod(JavaSource pSource) {
1000        initLogging(pSource);
1001        
1002        for (Iterator JavaDoc iter = tablesByOrder.iterator(); iter.hasNext(); ) {
1003            TableInfo tableInfo = (TableInfo) iter.next();
1004            
1005            getInsertRowMethod(pSource, tableInfo);
1006            getInnerInsertRowMethod(pSource, tableInfo);
1007        }
1008        getCacheDataClass(pSource);
1009        getPublicCloneMethod(pSource);
1010        
1011        return null;
1012    }
1013}
1014
Popular Tags