View Javadoc

1   /*
2    * $Source: /usr/cvsroot/melati/maven-dsd-plugin/src/main/java/org/melati/poem/prepro/TableDef.java,v $
3    * $Revision: 1.77 $
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc AT paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem.prepro;
47  
48  import java.util.Enumeration;
49  import java.util.Vector;
50  import java.util.Hashtable;
51  import java.util.Arrays;
52  import java.io.StreamTokenizer;
53  import java.io.Writer;
54  import java.io.IOException;
55  
56  
57  /**
58   * A Table Definition holding information from a DSD.
59   * 
60   * 
61   * The SQL table naming convention is enforced here:
62   * the name given in the DSD is forced to lowercase. 
63   * 
64   *  This could be changed to enable mixed case names in 
65   *  jdbc names by simply not forcing to lowercase, 
66   *  but the JavaName has first letter capitalised. 
67   *  
68   *  The way around this is to have a separate jdbc name, 
69   *  but this would be a lot of redundancy for a small use case.
70   *  
71   *  The use case is actually broken: MySQL allows significant 
72   *  table name case, but only on operating systems where 
73   *  file name case is significant, and the manual advises against.
74   *  
75   *  The solution is to add the following to /etc/mysqld/my.cnf
76   *  
77   *  [mysqld]
78   *  lower_case_table_names=1  
79   * 
80   */
81  public class TableDef {
82  
83    DSD dsd;
84    /** Mixed case name. */
85    final String nameFromDsd;
86    final String capitalisedName;
87    /** Lowercase name. */
88    final String name;
89    String displayName;
90    String description;
91    String category;
92    String superclass;
93    int displayOrder;
94    boolean seqCached;
95    int cacheSize = CacheSizeTableQualifier.DEFAULT;
96    private Vector<FieldDef> fields = new Vector<FieldDef>();
97    boolean isAbstract;
98    boolean definesColumns;
99    TableNamingInfo tableNamingInfo = null;
100 
101   int nextFieldDisplayOrder = 0;
102   // Note we have to store the imports and process them at
103   // the end to avoid referring to a table that has yet to be processed.
104   // There is a similar problem with ReferenceFieldDef, really we need two passes
105   private final Hashtable<String, String> imports = new Hashtable<String, String>();
106   private final Vector<String> tableBaseImports = new Vector<String>();
107   private final Vector<String> persistentBaseImports = new Vector<String>();
108 
109   /**
110    * Constructor.
111    *
112    * @param dsd
113    *        the {@link DSD} this is a member of
114    * @param tokens
115    *        a stream of tokens
116    * @param displayOrder
117    *        the ordering within the DSD
118    * @param isAbstract
119    *        whether this is an abstract table
120    * @param nameStore
121    *        where to put our names
122    * @throws ParsingDSDException
123    *         if an unexpected token is encountered
124    * @throws IllegalityException
125    *         if a semantic incoherence is detected
126    * @throws IOException
127    *         if a problem with the file system is encountered
128    */
129   public TableDef(DSD dsd, StreamTokenizer tokens, int displayOrder,
130                   boolean isAbstract, TableNamingStore nameStore)
131       throws ParsingDSDException,
132       IOException,
133       IllegalityException {
134     this.dsd = dsd;
135     this.displayOrder = displayOrder;
136     this.isAbstract = isAbstract;
137     if (tokens.ttype != StreamTokenizer.TT_WORD)
138       throw new ParsingDSDException("<table name>", tokens);
139     nameFromDsd = tokens.sval;
140     name = nameFromDsd.toLowerCase();
141     capitalisedName = nameFromDsd.substring(0,1).toUpperCase() + nameFromDsd.substring(1);
142 
143     if (tokens.nextToken() == StreamTokenizer.TT_WORD) {
144       if (!tokens.sval.equals("extends"))
145         throw new ParsingDSDException("{", tokens);
146       tokens.wordChars('.', '.');
147       try {
148         if (tokens.nextToken() != StreamTokenizer.TT_WORD)
149           throw new ParsingDSDException("<class name>", tokens);
150       } finally {
151         tokens.ordinaryChar('.');
152       }
153       superclass = tokens.sval;
154     } else
155       tokens.pushBack();
156 
157     tableNamingInfo = nameStore.add(dsd, dsd.packageName, nameFromDsd, superclass);
158 
159     while (tokens.nextToken() == '(') {
160       tokens.nextToken();
161       TableQualifier.from(tokens).apply(this);
162       DSD.expect(tokens, ')');
163     }
164 
165     DSD.expect(tokens, '{');
166     while (tokens.nextToken() != '}')
167       fields.addElement(FieldDef.from(this, tokens, nextFieldDisplayOrder++));
168 
169     tokens.nextToken();
170 
171   }
172 
173   /**
174    * @param importName name of import
175    * @param destination "persistent", "table" or "both"
176    */
177   void addImport(String importName, String destination) {
178     if (!destination.equals("table") && !destination.equals("persistent")
179         && !destination.equals("both"))
180       throw new RuntimeException(
181                                  "Destination other than 'table', 'persistent' or 'both' used:"
182                                      + destination);
183 
184     String existing = null;
185     existing = imports.put(importName, destination);
186     if (existing != null && existing != destination)
187       imports.put(importName, "both");
188   }
189 
190   private final TableDef this_ = this;
191 
192   /**
193    * @param w
194    *        DatabaseBase
195    * @throws IOException
196    *         if a problem with the file system is encountered
197    */
198   public void generateTableDeclarationJava(Writer w)
199       throws IOException {
200     if (!isAbstract)
201       w.write("  private " + tableNamingInfo.tableMainClassRootReturnClass() +
202           "<" + tableNamingInfo.mainClassRootReturnClass() + ">" +
203           " tab_" + name + " = null;\n");
204   }
205 
206   /**
207    * @param w
208    *        DatabaseBase
209    * @throws IOException
210    *         if a problem with the file system is encountered
211    */
212   public void generateTableDefinitionJava(Writer w)
213       throws IOException {
214     if (!isAbstract)
215       w.write("    redefineTable(tab_" + name + " = " + "new "
216           + tableNamingInfo.tableMainClassUnambiguous() 
217           + ( 
218               (tableNamingInfo.tableMainClassRootReturnClass().equals(tableNamingInfo.tableMainClassUnambiguous()) 
219                   ?
220                   "<"+ tableNamingInfo.mainClassUnambiguous() +">" 
221                   : "")
222             )
223           + "(this, \"" + nameFromDsd + "\", "
224           + "DefinitionSource.dsd));\n");
225   }
226 
227   /**
228    * @param w
229    *        DatabaseBase
230    * @throws IOException
231    *         if a problem with the file system is encountered
232    */
233   public void generateTableAccessorJava(Writer w)
234       throws IOException {
235     if (isAbstract) 
236       return;
237 
238     generateTableAccessorDeclaration(w, false);
239     
240     w.write(" {\n" + "    return ");
241     // This cast is not actually required as ours is a subclass anyway
242     // but with generics we need to :(
243     if (!tableNamingInfo.tableMainClassRootReturnClass().equals(tableNamingInfo.tableMainClassUnambiguous()))
244       w.write("(" + tableNamingInfo.tableMainClassRootReturnClass() + ")");
245     w.write("tab_" + name + ";\n  }\n");
246   }
247 
248 
249   /**
250    * @param w
251    *        DatabaseTablesBase
252    * @throws IOException
253    *         if a problem with the file system is encountered
254    */
255   public void generateTableAccessorDefnJava(Writer w)
256       throws IOException {
257     if (isAbstract) 
258       return;
259 
260     generateTableAccessorDeclaration(w, true);    
261     w.write(";\n");
262   }
263 
264   private void generateTableAccessorDeclaration(Writer w, boolean inInterface) throws IOException {
265     w.write("\n /**\n"
266         + "  * Retrieves the " + tableNamingInfo.tableMainClassShortName() + " table.\n"
267         + "  *\n"
268         + "  * see " + "org.melati.poem.prepro.TableDef"
269         + "#generateTableAccessorJava \n"
270         + "  * @return the " + tableNamingInfo.tableMainClassRootReturnClass() + " from this database\n" + "  */\n");
271     if (!inInterface)
272      if (!tableNamingInfo.tableMainClassRootReturnClass().equals(tableNamingInfo.tableMainClassUnambiguous()))
273         w.write("  @SuppressWarnings({ \"rawtypes\", \"unchecked\" })\n");
274     w.write("  public " + tableNamingInfo.tableMainClassRootReturnClass() + "<" + tableNamingInfo.mainClassRootReturnClass() + "> get"
275         + tableNamingInfo.tableMainClassShortName() + "()");
276   }
277 
278   /**
279    * @param w
280    *        Persistent Base writer
281    * @throws IOException
282    *         if a problem with the file system is encountered
283    */
284   public void generatePersistentBaseJava(Writer w)
285       throws IOException {
286     for (Enumeration<String> e = persistentBaseImports.elements(); e.hasMoreElements();) {
287       String packageName = e.nextElement();
288       TableNamingInfo tni = dsd.tableNamingStore.tableInfoByTableOrPersistentFQName.get(packageName);
289       //if (tni != null) w.write("// tni " + tni.objectFQName + ";\n");
290       if (tableNamingInfo.extended != null) { 
291         if (tni!=null)
292           if (tni.equals(tableNamingInfo) || tni.equals(tableNamingInfo.extended))
293             w.write("// import " + packageName + ";\n");
294           else 
295             w.write("import " + packageName + ";\n");                 
296         else 
297           w.write("import " + packageName + ";\n");                 
298       } else
299         w.write("import " + packageName + ";\n");
300     }
301     w.write("\n");
302 
303     // if we subclass a table with the same name we need to cast the table to
304     // have the same return type as the root superclass
305     String requiredReturnClass = tableNamingInfo.tableMainClassRootReturnClass();
306 
307     w.write("\n" + "/**\n"
308         + " * Melati POEM generated abstract base class for a "
309         + "<code>Persistent</code> \n" + 
310         " * <code>" + nameFromDsd + "</code> Object.\n" + " *\n" + 
311         " * see org.melati.poem.prepro.TableDef#generatePersistentBaseJava \n" + 
312         " */\n");
313     w.write("public abstract class " + tableNamingInfo.baseClassUnambiguous()
314         + " extends " + tableNamingInfo.superclassMainUnambiguous() + " {\n" + "\n");
315 
316     w.write("\n /**\n" + 
317             "  * Retrieves the Database object.\n" + "  * \n" + 
318             "  * see org.melati.poem.prepro.TableDef#generatePersistentBaseJava \n" + 
319             "  * @return the database\n" + "  */\n");
320     w.write("  public " + dsd.databaseTablesClassName + " get"
321         + dsd.databaseTablesClassName + "() {\n" + "    return ("
322         + dsd.databaseTablesClassName + ")getDatabase();\n" + "  }\n" + "\n");
323 
324     w.write("\n /**\n" + "  * Retrieves the  <code>" + tableNamingInfo.tableMainClassShortName() + "</code> table \n"
325         + "  * which this <code>Persistent</code> is from.\n" + "  * \n"
326         + "  * see org.melati.poem.prepro.TableDef"
327         + "#generatePersistentBaseJava \n" 
328         + "  * @return the " + requiredReturnClass + "\n" 
329         + "  */\n");
330     w.write("  @SuppressWarnings(\"unchecked\")\n");
331     w.write("  public " + requiredReturnClass + "<"+tableNamingInfo.mainClassRootReturnClass()+"> "
332         + tableNamingInfo.tableAccessorMethod() + "() {\n" + "    return ("
333         + requiredReturnClass + "<"+tableNamingInfo.mainClassRootReturnClass()+">)getTable();\n" + "  }\n\n");
334       
335     if (!fields.elements().hasMoreElements()) {
336       w.write("  // There are no Fields in this table, only in its ancestors \n");
337     } else {
338       w.write("  @SuppressWarnings(\"unchecked\")\n");
339       w.write("  private " + tableNamingInfo.tableMainClassUnambiguous() +"<"+tableNamingInfo.mainClassUnambiguous()+"> _"
340           + tableNamingInfo.tableAccessorMethod() + "() {\n" + "    return ("
341           + tableNamingInfo.tableMainClassUnambiguous() + "<"+tableNamingInfo.mainClassUnambiguous()+">)getTable();\n" + "  }\n\n");
342 
343       w.write("  // Fields in this table \n");
344       for (Enumeration<FieldDef> f = fields.elements(); f.hasMoreElements();) {
345         FieldDef fd = f.nextElement();
346         w.write(" /**\n");
347         w.write(DSD.javadocFormat(2, 1,
348             ((fd.displayName != null) ? fd.displayName : fd.name)
349                 + ((fd.description != null) ? " - " + fd.description : "")));
350         w.write("  */\n");
351         w.write("  protected ");
352         fd.generateJavaDeclaration(w);
353 
354         w.write(";\n");
355       }
356 
357       for (Enumeration<FieldDef> f = fields.elements(); f.hasMoreElements();) {
358         FieldDef field = f.nextElement();
359         w.write('\n');
360         field.generateBaseMethods(w);
361         w.write('\n');
362         field.generateFieldCreator(w);
363       }
364     }
365 
366     // for references to this table in tableDefs
367     //  write getReferencesByColumname
368     for (TableDef t : dsd.tablesInDatabase) { 
369       if (!t.isAbstract && t.superclass == null) { //TODO handle abstract and extended classes
370         for (FieldDef f : t.fields) { 
371           if (f instanceof ReferenceFieldDef) { 
372             ReferenceFieldDef rfd = (ReferenceFieldDef) f;
373             if (rfd.getTargetTableNamingInfo() != null && rfd.getTargetTableNamingInfo().mainClassFQName().equals(tableNamingInfo.mainClassFQName())) {
374               w.write('\n');          
375               w.write("  private CachedSelection<" + rfd.shortestUnambiguousClassname +"> "+rfd.name+rfd.shortestUnambiguousClassname + "s = null;\n");
376               w.write("  /** References to this "+tableNamingInfo.mainClassShortName()+" in the " + rfd.shortestUnambiguousClassname+" table via its "+ rfd.name+" field.*/\n");
377               w.write("  @SuppressWarnings(\"unchecked\")\n");
378               w.write("  public Enumeration<" + rfd.shortestUnambiguousClassname +"> get" + StringUtils.capitalised(rfd.name)+rfd.shortestUnambiguousClassname + "s() {\n");
379               w.write("    if (getTroid() == null)\n");
380               w.write("      return new EmptyEnumeration<" + rfd.shortestUnambiguousClassname +">();\n");
381               w.write("    else {\n");
382               w.write("      if (" + rfd.name+rfd.shortestUnambiguousClassname + "s == null)\n");
383               w.write("        " + rfd.name+rfd.shortestUnambiguousClassname + "s =\n");
384               w.write("          get" + dsd.databaseTablesClassName + "().get"+rfd.shortestUnambiguousClassname+"Table().get"+StringUtils.capitalised(rfd.name)+"Column().cachedSelectionWhereEq(getTroid());\n");
385               w.write("      return " + rfd.name+rfd.shortestUnambiguousClassname + "s.objects();\n");
386               w.write("    }\n");
387               w.write("  }\n");
388               w.write("\n");
389               w.write("\n");
390               w.write("  /** References to this "+tableNamingInfo.mainClassShortName()+" in the " + rfd.shortestUnambiguousClassname+" table via its "+ rfd.name+" field, as a List.*/\n");
391               w.write("  public List<" + StringUtils.capitalised(rfd.shortestUnambiguousClassname) +"> get" + StringUtils.capitalised(rfd.name)+rfd.shortestUnambiguousClassname + "List() {\n");
392               w.write("    return Collections.list(get" + StringUtils.capitalised(rfd.name)+rfd.shortestUnambiguousClassname + "s());\n");
393               w.write("  }\n");
394               w.write("\n");
395               w.write("\n");
396             }
397           }
398         }
399       }
400     }
401     w.write('\n');
402     
403     w.write("}\n");
404   }
405 
406   /**
407    * @param w
408    *        Persistent writer
409    * @throws IOException
410    *         if a problem with the file system is encountered
411    */
412   public void generatePersistentJava(Writer w)
413       throws IOException {
414 
415     w.write("import " + dsd.packageName + ".generated."
416         + tableNamingInfo.baseClassShortName() + ";\n");
417     w.write("\n/**\n"
418         + " * Melati POEM generated, programmer modifiable stub \n"
419         + " * for a <code>Persistent</code> <code>"
420         + tableNamingInfo.mainClassShortName() + "</code> object.\n");
421     w.write(" * \n"
422         + (description != null ? " * <p> \n"
423             + " * Description: \n"
424             + DSD.javadocFormat(1, 3, (description + ((description
425                 .lastIndexOf(".") != description.length() - 1) ? "." : "")))
426             + " * </p>\n" : ""));
427     w.write(fieldSummaryTable());
428     w.write(" * \n" + " * see org.melati.poem.prepro.TableDef"
429         + "#generatePersistentJava \n" + " */\n");
430     w.write("public class " + tableNamingInfo.mainClassShortName() + " extends "
431         + tableNamingInfo.baseClassShortName() + " {\n");
432 
433     w.write("\n /**\n"
434             + "  * Constructor \n"
435             + "  * for a <code>Persistent</code> <code>"
436             + tableNamingInfo.mainClassShortName()
437             + "</code> object.\n"
438             + (description != null ? ("  * <p>\n"
439                 + "  * Description: \n"
440                 + DSD
441                     .javadocFormat(2, 3, (description + ((description
442                         .lastIndexOf(".") != description.length() - 1) ? "."
443                         : ""))) + "  * </p>\n") : "") + "  * \n"
444             + "  * see org.melati.poem.prepro.TableDef"
445             + "#generatePersistentJava \n" + "  */\n");
446 
447     w.write("  public " + tableNamingInfo.mainClassShortName() + "() { \n"
448             + "    super();\n"
449             + "}\n" + "\n"
450             + "  // programmer's domain-specific code here\n" + "}\n");
451   }
452 
453   /**
454    * @param w
455    *        TableBase
456    * @throws IOException
457    *         if a problem with the file system is encountered
458    */
459   public void generateTableBaseJava(Writer w)
460       throws IOException {
461 
462     for (Enumeration<String> e = tableBaseImports.elements(); e.hasMoreElements();) {
463       String packageName = e.nextElement();
464       if (ambiguous(packageName)) 
465         w.write("// FIXME extended table \nimport " + packageName + ";\n");
466       else
467         w.write("import " + packageName + ";\n");
468     }
469 
470     w.write("\n");
471     w.write("\n" 
472         + "/**\n" 
473         + " * Melati POEM generated base class for " + "<code>Table</code> <code>" + nameFromDsd + "</code>.\n");
474     w.write(" *\n" 
475         + " * see org.melati.poem.prepro.TableDef"
476         + "#generateTableBaseJava \n" + " */\n\n");
477     w.write("public class " + tableNamingInfo.tableBaseClassShortName() + "<T extends "+tableNamingInfo.mainClassShortName()+"> extends "
478         + tableNamingInfo.superclassTableShortName() + "<T> {\n" 
479         + "\n");
480 
481     for (Enumeration<FieldDef> f = fields.elements(); f.hasMoreElements();) {
482       w.write("  private ");
483       (f.nextElement()).generateColDecl(w);
484       w.write(" = null;\n");
485     }
486 
487     w.write("\n /**\n" + "  * Constructor. \n" 
488         + "  * \n" 
489         + "  * see org.melati.poem.prepro.TableDef" + "#generateTableBaseJava \n"
490         + "  * @param database          the POEM database we are using\n"
491         + "  * @param name              the name of this <code>Table</code>\n"
492         + "  * @param definitionSource  which definition is being used\n"
493         + "  * @throws PoemException    if anything goes wrong\n" + "  */\n");
494     w.write("\n" + "  public " + tableNamingInfo.tableBaseClassShortName() + "(\n"
495         + "      Database database, String name,\n"
496         + "      DefinitionSource definitionSource)"
497         + " throws PoemException {\n"
498         + "    super(database, name, definitionSource);\n" + "  }\n" + "\n");
499 
500     
501     //w.write("\n /**\n" + "  * Constructor.\n" 
502     //    + "  *\n" 
503     //    + "  * see org.melati.poem.prepro.TableDef" + "#generateTableBaseJava \n"
504     //    + "  * @param database          the POEM database we are using\n"
505     //    + "  * @param name              the name of this <code>Table</code>\n"
506     //    + "  * @throws PoemException    if anything goes wrong\n" 
507     //    + "  */\n");
508     //w.write("  public " + naming.tableBaseClassShortName() + "(\n"
509     //    + "      Database database, String name)" 
510     //    + " throws PoemException {\n"
511     //    + "    this(database, name, DefinitionSource.dsd);\n" + "  }\n" 
512     //    + "\n");
513     
514     w.write("\n /**\n" 
515         + "  * Get the database tables.\n" + "  *\n"
516         + "  * see org.melati.poem.prepro.TableDef#generateTableBaseJava \n" 
517         + "  * @return the database tables\n"
518         + "  */\n");
519     w.write("  public " + dsd.databaseTablesClassName + " get"+ dsd.databaseTablesClassName + "() {\n" + 
520         "    return (" + dsd.databaseTablesClassName + ")getDatabase();\n" + 
521         "  }\n" + 
522         "\n"); 
523     Enumeration<FieldDef> fs = fields.elements(); 
524     w.write(
525         "  public void init() throws PoemException {\n" + 
526         "    super.init();\n");
527 
528     for (; fs.hasMoreElements();) {
529       (fs.nextElement()).generateColDefinition(w);
530       if (fs.hasMoreElements())
531         w.write('\n');
532     }
533 
534     w.write("  }\n" + "\n");
535 
536     for (Enumeration<FieldDef> f = fields.elements(); f.hasMoreElements();) {
537       (f.nextElement()).generateColAccessor(w);
538       w.write('\n');
539     }
540 
541     // if we subclass a table with the same name we need to cast the table to
542     // have the same return type as the root superclass
543     String requiredReturnClass = tableNamingInfo.mainClassRootReturnClass();
544 
545     w.write("\n /**\n" + "  * Retrieve the <code>"
546         + tableNamingInfo.mainClassShortName() + "</code> as a <code>"
547         + requiredReturnClass + "</code>.\n" 
548         + "  *\n" 
549         + "  * see org.melati.poem.prepro.TableDef" + "#generateTableBaseJava \n"
550         + "  * @param troid a Table Row Oject ID\n"
551         + "  * @return the <code>Persistent</code> identified "
552         + "by the <code>troid</code>\n" + "  */\n");
553     w.write("  public " + requiredReturnClass + " get"
554         + tableNamingInfo.mainClassShortName() + "Object(" + "Integer troid) {\n"
555         + "    return (" + requiredReturnClass + ")getObject(troid);\n"
556         + "  }\n" + "\n");
557 
558     w.write("\n /**\n" + "  * Retrieve the <code>"
559         + tableNamingInfo.mainClassShortName() + "</code> \n" 
560         + "  * as a <code>" + requiredReturnClass + "</code>.\n" 
561         + "  *\n" 
562         + "  * see org.melati.poem.prepro.TableDef" + "#generateTableBaseJava \n"
563         + "  * @param troid a Table Row Object ID\n"
564         + "  * @return the <code>Persistent</code> identified " + "  */\n");
565     w.write("  public " + requiredReturnClass + " get"
566         + tableNamingInfo.mainClassShortName() + "Object(" + "int troid) {\n"
567         + "    return (" + requiredReturnClass + ")getObject(troid);\n"
568         + "  }\n");
569 
570     if (!isAbstract)
571       w.write("\n" + "  protected JdbcPersistent _newPersistent() {\n"
572           + "    return new " + tableNamingInfo.mainClassUnambiguous() + "();\n" + "  }"
573           + "\n");
574     
575     if (displayName != null)
576       w.write("  public String defaultDisplayName() {\n" + "    return "
577           + StringUtils.quoted(displayName, '"') + ";\n" + "  }\n" + "\n");
578 
579     if (description != null)
580       w.write("  public String defaultDescription() {\n" + "    return "
581           + StringUtils.quoted(description, '"') + ";\n" + "  }\n" + "\n");
582 
583     if (seqCached)
584       w.write("  public boolean defaultRememberAllTroids() {\n"
585           + "    return true;\n" + "  }\n" + "\n");
586 
587     if (cacheSize != CacheSizeTableQualifier.DEFAULT)
588       w.write("  public Integer defaultCacheLimit() {\n"
589           + "    return new Integer("
590           + (cacheSize == CacheSizeTableQualifier.UNLIMITED ? "999999999" : ""
591               + cacheSize) + ");\n" + "  }\n" + "\n");
592 
593     if (category != null)
594       w.write("  public String defaultCategory() {\n" + "    return "
595           + StringUtils.quoted(category, '"') + ";\n" + "  }\n" + "\n");
596 
597     w.write("  public int defaultDisplayOrder() {\n" + "    return "
598         + displayOrder + ";\n" + "  }\n");
599 
600     w.write("}\n");
601   }
602 
603   private boolean ambiguous(String packageName) {
604     TableNamingInfo tni = dsd.tableNamingStore.tableInfoByTableOrPersistentFQName.get(packageName);
605     if (tni == null)
606       return false;
607     else if(tni.hidden || tni.hidesOther)
608       return true;
609     return false;
610   }
611 
612   /**
613    * @param w
614    *        Table
615    * @throws IOException
616    *         if a problem with the file system is encountered
617    */
618   public void generateTableJava(Writer w)
619       throws IOException {
620 
621     w.write("import " + tableNamingInfo.tableBaseClassFQName() + ";\n");
622     w.write("import org.melati.poem.DefinitionSource;\n");
623     w.write("import org.melati.poem.Database;\n");
624     w.write("import org.melati.poem.PoemException;\n");
625 
626     w.write("\n/**\n"
627         + " * Melati POEM generated, programmer modifiable stub \n"
628         + " * for a <code>"
629         + tableNamingInfo.tableMainClassShortName()
630         + "</code> object.\n"
631         + (description != null ? " * <p>\n"
632             + " * Description: \n"
633             + DSD.javadocFormat(1, 3, (description + ((description
634                 .lastIndexOf(".") != description.length() - 1) ? "." : "")))
635             + " * </p>\n" : "") + " *\n");
636     w.write(fieldSummaryTable());
637     w.write(" * \n" 
638         + " * see  org.melati.poem.prepro.TableDef" + "#generateTableJava \n" + " */\n");
639     w.write("public class " + tableNamingInfo.tableMainClassShortName() + "<T extends "+tableNamingInfo.mainClassShortName()+"> extends "
640         + tableNamingInfo.tableBaseClassShortName() + "<"+tableNamingInfo.mainClassShortName()+"> {\n");
641     
642     Object o = new Object() {
643       public String toString() {
644         return "\n /**\n"
645             + "  * Constructor.\n"
646             + "  * \n"
647             + "  * see org.melati.poem.prepro.TableDef" + "#generateTableJava \n"
648             + "  * @param database          the POEM database we are using\n"
649             + "  * @param name              the name of this <code>Table</code>\n"
650             + "  * @param definitionSource  which definition is being used\n"
651             + "  * @throws PoemException    if anything goes wrong\n"
652             + "  */\n";
653       }
654     };
655     w.write(o.toString());
656     w.write("  public " + tableNamingInfo.tableMainClassShortName() + "(\n"
657         + "      Database database, String name,\n"
658         + "      DefinitionSource definitionSource)"
659         + " throws PoemException {\n"
660         + "    super(database, name, definitionSource);\n" + "  }\n" + "\n"
661         + "  // programmer's domain-specific code here\n" + "}\n");
662   }
663 
664   /**
665    * Generate the 4 files.
666    *
667    * @throws IOException
668    *         if a problem with the file system is encountered
669    * @throws IllegalityException
670    *         if a semantic incoherence is detected
671    */
672   public void generateJava()
673       throws IOException, IllegalityException {
674 
675     boolean hasDisplayLevel = false;
676     boolean hasSearchability = false;
677     
678     boolean needSelectionImports = false;
679     for (TableDef t : dsd.tablesInDatabase) { 
680       if (!t.isAbstract && t.superclass == null)
681       for (FieldDef f : t.fields) { 
682         if (f instanceof ReferenceFieldDef) { 
683           ReferenceFieldDef rfd = (ReferenceFieldDef) f;
684           if (!(rfd.getTargetTableNamingInfo() == null)  && 
685               rfd.getTargetTableNamingInfo().mainClassFQName().equals(tableNamingInfo.mainClassFQName())) {
686             needSelectionImports = true;
687             addImport(rfd.table.tableNamingInfo.mainClassFQName(), "persistent");
688           }
689         }
690       }
691     }
692     if (needSelectionImports) {
693       addImport("org.melati.poem.CachedSelection", "persistent");
694       addImport("org.melati.poem.util.EmptyEnumeration","persistent");
695       addImport("java.util.Enumeration","persistent");
696       addImport("java.util.List","persistent");
697       addImport("java.util.Collections","persistent");
698     }
699     
700     int fieldCount = 0;
701     for (Enumeration<FieldDef> e = fields.elements(); e.hasMoreElements();) {
702       fieldCount++;
703       FieldDef f = e.nextElement();
704       if (f.displayLevel != null)
705         hasDisplayLevel = true;
706       if (f.searchability != null)
707         hasSearchability = true;
708     }
709     if (fieldCount == 0 && !isAbstract && tableNamingInfo.superclass == null)
710       throw new NonAbstractEmptyTableException(name);
711 
712     if (!isAbstract)
713       addImport("org.melati.poem.JdbcPersistent", "table");
714     if (hasDisplayLevel)
715       addImport("org.melati.poem.DisplayLevel", "table");
716     if (hasSearchability)
717       addImport("org.melati.poem.Searchability", "table");
718     addImport(tableNamingInfo.objectFQName, "table");
719     if (definesColumns) {
720       addImport("org.melati.poem.Column", "both");
721       addImport("org.melati.poem.Field", "both");
722     }
723     if (tableNamingInfo.superclassMainUnambiguous().equals("JdbcPersistent")) {
724       addImport("org.melati.poem.JdbcPersistent", "persistent");
725     } else {
726       addImport(tableNamingInfo.superclassMainFQName(), "persistent");
727     }
728 
729     // we may not have any fields in an overridden class
730     // but we need the import for getTable
731     addImport(tableNamingInfo.tableMainClassFQName(), "persistent");
732     addImport(dsd.packageName + "." + dsd.databaseTablesClassName, "persistent");
733 
734     addImport("org.melati.poem.Database", "table");
735     addImport("org.melati.poem.DefinitionSource", "table");
736     addImport("org.melati.poem.PoemException", "table");
737 
738     if (!isAbstract)
739       addImport("org.melati.poem.Persistent", "table");
740 
741     if (tableNamingInfo.superclassTableUnambiguous().equals("Table")) {
742       addImport("org.melati.poem.Table", "table");
743     } else {
744       addImport(tableNamingInfo.superclassTableFQName(), "table");
745     }
746     addImport(dsd.packageName + "." + dsd.databaseTablesClassName, "table");
747     addImport(tableNamingInfo.mainClassFQName(), "persistent");
748 
749     // Sort out the imports
750     for (Enumeration<String> i = imports.keys(); i.hasMoreElements();) {
751       String fqKey;
752       String key = i.nextElement();
753       if (key.indexOf(".") == -1) {
754         TableNamingInfo targetTable = (TableNamingInfo) dsd.tableNamingStore.tableInfoByPersistentShortName
755             .get(key);
756         if (targetTable == null)
757           throw new RuntimeException("No TableNamingInfo for " + key + 
758                   ". This is probably a typo either in the table definition name or in a reference field.");
759         fqKey = targetTable.objectFQName;
760         String destination = imports.get(key);
761         imports.remove(key);
762         addImport(fqKey, destination);
763       }
764     }
765     for (Enumeration<String> i = imports.keys(); i.hasMoreElements();) {
766       String fqKey;
767       String key = i.nextElement();
768 
769       if (key.indexOf(".") == -1) {
770         TableNamingInfo targetTable = 
771             (TableNamingInfo)dsd.tableNamingStore.tableInfoByPersistentShortName.get(key);
772         fqKey = targetTable.objectFQName;
773       } else {
774         fqKey = key;
775       }
776       String destination = imports.get(key);
777       if (destination == "table") {
778         tableBaseImports.addElement(fqKey);
779       } else if (destination == "persistent") {
780         persistentBaseImports.addElement(fqKey);
781       } else {
782         tableBaseImports.addElement(fqKey);
783         persistentBaseImports.addElement(fqKey);
784       }
785     }
786     Object[] t = tableBaseImports.toArray();
787     Object[] p = persistentBaseImports.toArray();
788     Arrays.sort(t);
789     Arrays.sort(p);
790     tableBaseImports.removeAllElements();
791     persistentBaseImports.removeAllElements();
792     for (int i = 0; i < t.length; i++)
793       tableBaseImports.addElement((String)t[i]);
794     for (int i = 0; i < p.length; i++)
795       persistentBaseImports.addElement((String)p[i]);
796 
797     dsd.createJava(tableNamingInfo.baseClassShortName(), new Generator() {
798       public void process(Writer w)
799           throws IOException {
800         this_.generatePersistentBaseJava(w);
801       }
802     }, true);
803 
804     dsd.createJava(tableNamingInfo.mainClassShortName(), new Generator() {
805       public void process(Writer w)
806           throws IOException {
807         this_.generatePersistentJava(w);
808       }
809     }, false);
810 
811     dsd.createJava(tableNamingInfo.tableBaseClassShortName(), new Generator() {
812       public void process(Writer w)
813           throws IOException {
814         this_.generateTableBaseJava(w);
815       }
816     }, true);
817 
818     dsd.createJava(tableNamingInfo.tableMainClassShortName(), new Generator() {
819       public void process(Writer w)
820           throws IOException {
821         this_.generateTableJava(w);
822       }
823     }, false);
824   }
825 
826   String fieldSummaryTable() {
827     StringBuffer table = new StringBuffer();
828     table.append(" * \n" + " * <table> \n" + " * <tr><th colspan='3'>\n"
829         + " * Field summary for SQL table <code>" + nameFromDsd + "</code>\n"
830         + " * </th></tr>\n"
831         + " * <tr><th>Name</th><th>Type</th><th>Description</th></tr>\n");
832     for (Enumeration<FieldDef> f = fields.elements(); f.hasMoreElements();) {
833       FieldDef fd = f.nextElement();
834       table.append(DSD.javadocFormat(1, 1, "<tr><td> " + fd.name
835           + " </td><td> " + fd.typeShortName + " </td><td> "
836           + ((fd.description != null) ? fd.description : "&nbsp;")
837           + " </td></tr>"));
838     }
839     table.append(" * </table> \n");
840     return table.toString();
841   }
842 }