View Javadoc

1   package org.apache.torque.engine.database.model;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Hashtable;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import org.apache.commons.collections.map.ListOrderedMap;
29  import org.apache.commons.lang.StringUtils;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import org.apache.torque.engine.EngineException;
35  
36  import org.xml.sax.Attributes;
37  
38  /***
39   * Data about a table used in an application.
40   *
41   * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
42   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
43   * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
44   * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
45   * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
46   * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
47   * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
48   * @version $Id: Table.java 473814 2006-11-11 22:30:30Z tv $
49   */
50  public class Table implements IDMethod
51  {
52      /*** Logging class from commons.logging */
53      private static Log log = LogFactory.getLog(Table.class);
54  
55      //private AttributeListImpl attributes;
56      private List columnList;
57      private List foreignKeys;
58      private List indices;
59      private List unices;
60      private List idMethodParameters;
61      private String name;
62      private String description;
63      private String javaName;
64      private String idMethod;
65      private String javaNamingMethod;
66      private Database tableParent;
67      private List referrers;
68      private List foreignTableNames;
69      private boolean containsForeignPK;
70      private Column inheritanceColumn;
71      private boolean skipSql;
72      private boolean abstractValue;
73      private String alias;
74      private String enterface;
75      private String pkg;
76      private String baseClass;
77      private String basePeer;
78      private Hashtable columnsByName;
79      private Hashtable columnsByJavaName;
80      private boolean needsTransactionInPostgres;
81      private boolean heavyIndexing;
82      private boolean forReferenceOnly;
83      private Map options;
84  
85      /***
86       * Default Constructor
87       */
88      public Table()
89      {
90          this(null);
91      }
92  
93      /***
94       * Constructs a table object with a name
95       *
96       * @param name table name
97       */
98      public Table(String name)
99      {
100         this.name = name;
101         columnList = new ArrayList();
102         foreignKeys = new ArrayList(5);
103         indices = new ArrayList(5);
104         unices = new ArrayList(5);
105         columnsByName = new Hashtable();
106         columnsByJavaName = new Hashtable();
107         options = Collections.synchronizedMap(new ListOrderedMap());
108     }
109 
110     /***
111      * Load the table object from an xml tag.
112      *
113      * @param attrib xml attributes
114      * @param defaultIdMethod defined at db level
115      */
116     public void loadFromXML(Attributes attrib, String defaultIdMethod)
117     {
118         name = attrib.getValue("name");
119         javaName = attrib.getValue("javaName");
120         idMethod = attrib.getValue("idMethod");
121 
122         // retrieves the method for converting from specified name to
123         // a java name.
124         javaNamingMethod = attrib.getValue("javaNamingMethod");
125         if (javaNamingMethod == null)
126         {
127             javaNamingMethod = getDatabase().getDefaultJavaNamingMethod();
128         }
129 
130         if ("null".equals(idMethod))
131         {
132             idMethod = defaultIdMethod;
133         }
134         skipSql = "true".equals(attrib.getValue("skipSql"));
135         // pkg = attrib.getValue("package");
136         abstractValue = "true".equals(attrib.getValue("abstract"));
137         baseClass = attrib.getValue("baseClass");
138         basePeer = attrib.getValue("basePeer");
139         alias = attrib.getValue("alias");
140         heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"))
141                 || (!"false".equals(attrib.getValue("heavyIndexing"))
142                 && getDatabase().isHeavyIndexing());
143         description = attrib.getValue("description");
144         enterface = attrib.getValue("interface");
145     }
146 
147     /***
148      * <p>A hook for the SAX XML parser to call when this table has
149      * been fully loaded from the XML, and all nested elements have
150      * been processed.</p>
151      *
152      * <p>Performs heavy indexing and naming of elements which weren't
153      * provided with a name.</p>
154      */
155     public void doFinalInitialization()
156     {
157         // Heavy indexing must wait until after all columns composing
158         // a table's primary key have been parsed.
159         if (heavyIndexing)
160         {
161             doHeavyIndexing();
162         }
163 
164         // Name any indices which are missing a name using the
165         // appropriate algorithm.
166         doNaming();
167     }
168 
169     /***
170      * <p>Adds extra indices for multi-part primary key columns.</p>
171      *
172      * <p>For databases like MySQL, values in a where clause must
173      * match key part order from the left to right.  So, in the key
174      * definition <code>PRIMARY KEY (FOO_ID, BAR_ID)</code>,
175      * <code>FOO_ID</code> <i>must</i> be the first element used in
176      * the <code>where</code> clause of the SQL query used against
177      * this table for the primary key index to be used.  This feature
178      * could cause problems under MySQL with heavily indexed tables,
179      * as MySQL currently only supports 16 indices per table (i.e. it
180      * might cause too many indices to be created).</p>
181      *
182      * <p>See <a href="http://www.mysql.com/doc/E/X/EXPLAIN.html">the
183      * manual</a> for a better description of why heavy indexing is
184      * useful for quickly searchable database tables.</p>
185      */
186     private void doHeavyIndexing()
187     {
188         if (log.isDebugEnabled())
189         {
190             log.debug("doHeavyIndex() called on table " + name);
191         }
192 
193         List pk = getPrimaryKey();
194         int size = pk.size();
195 
196         try
197         {
198             // We start at an offset of 1 because the entire column
199             // list is generally implicitly indexed by the fact that
200             // it's a primary key.
201             for (int i = 1; i < size; i++)
202             {
203                 addIndex(new Index(this, pk.subList(i, size)));
204             }
205         }
206         catch (EngineException e)
207         {
208             log.error(e, e);
209         }
210     }
211 
212     /***
213      * Names composing objects which haven't yet been named.  This
214      * currently consists of foreign-key and index entities.
215      */
216     private void doNaming()
217     {
218         int i;
219         int size;
220         String name;
221 
222         // Assure names are unique across all databases.
223         try
224         {
225             for (i = 0, size = foreignKeys.size(); i < size; i++)
226             {
227                 ForeignKey fk = (ForeignKey) foreignKeys.get(i);
228                 name = fk.getName();
229                 if (StringUtils.isEmpty(name))
230                 {
231                     name = acquireConstraintName("FK", i + 1);
232                     fk.setName(name);
233                 }
234             }
235 
236             for (i = 0, size = indices.size(); i < size; i++)
237             {
238                 Index index = (Index) indices.get(i);
239                 name = index.getName();
240                 if (StringUtils.isEmpty(name))
241                 {
242                     name = acquireConstraintName("I", i + 1);
243                     index.setName(name);
244                 }
245             }
246 
247             for (i = 0, size = unices.size(); i < size; i++)
248             {
249                 Unique unique = (Unique) unices.get(i);
250                 name = unique.getName();
251                 if (StringUtils.isEmpty(name))
252                 {
253                     name = acquireConstraintName("U", i + 1);
254                     unique.setName(name);
255                 }
256             }
257         }
258         catch (EngineException nameAlreadyInUse)
259         {
260             log.error(nameAlreadyInUse, nameAlreadyInUse);
261         }
262     }
263 
264     /***
265      * Macro to a constraint name.
266      *
267      * @param nameType constraint type
268      * @param nbr unique number for this constraint type
269      * @return unique name for constraint
270      * @throws EngineException
271      */
272     private final String acquireConstraintName(String nameType, int nbr)
273             throws EngineException
274     {
275         List inputs = new ArrayList(4);
276         inputs.add(getDatabase());
277         inputs.add(getName());
278         inputs.add(nameType);
279         inputs.add(new Integer(nbr));
280         return NameFactory.generateName(NameFactory.CONSTRAINT_GENERATOR,
281                                         inputs);
282     }
283 
284     /***
285      * Gets the value of base class for classes produced from this table.
286      *
287      * @return The base class for classes produced from this table.
288      */
289     public String getBaseClass()
290     {
291         if (isAlias() && baseClass == null)
292         {
293             return alias;
294         }
295         else if (baseClass == null)
296         {
297             return getDatabase().getBaseClass();
298         }
299         else
300         {
301             return baseClass;
302         }
303     }
304 
305     /***
306      * Set the value of baseClass.
307      * @param v  Value to assign to baseClass.
308      */
309     public void setBaseClass(String v)
310     {
311         this.baseClass = v;
312     }
313 
314     /***
315      * Get the value of basePeer.
316      * @return value of basePeer.
317      */
318     public String getBasePeer()
319     {
320         if (isAlias() && basePeer == null)
321         {
322             return alias + "Peer";
323         }
324         else if (basePeer == null)
325         {
326             return getDatabase().getBasePeer();
327         }
328         else
329         {
330             return basePeer;
331         }
332     }
333 
334     /***
335      * Set the value of basePeer.
336      * @param v  Value to assign to basePeer.
337      */
338     public void setBasePeer(String v)
339     {
340         this.basePeer = v;
341     }
342 
343     /***
344      * A utility function to create a new column from attrib and add it to this
345      * table.
346      *
347      * @param attrib xml attributes for the column to add
348      * @return the added column
349      */
350     public Column addColumn(Attributes attrib)
351     {
352         Column col = new Column();
353         col.setTable(this);
354         col.setCorrectGetters(false);
355         col.loadFromXML(attrib);
356         addColumn(col);
357         return col;
358     }
359 
360     /***
361      * Adds a new column to the column list and set the
362      * parent table of the column to the current table
363      *
364      * @param col the column to add
365      */
366     public void addColumn(Column col)
367     {
368         col.setTable (this);
369         if (col.isInheritance())
370         {
371             inheritanceColumn = col;
372         }
373         columnList.add(col);
374         columnsByName.put(col.getName(), col);
375         columnsByJavaName.put(col.getJavaName(), col);
376         col.setPosition(columnList.size());
377         needsTransactionInPostgres |= col.requiresTransactionInPostgres();
378     }
379 
380     /***
381      * A utility function to create a new foreign key
382      * from attrib and add it to this table.
383      *
384      * @param attrib the xml attributes
385      * @return the created ForeignKey
386      */
387     public ForeignKey addForeignKey(Attributes attrib)
388     {
389         ForeignKey fk = new ForeignKey();
390         fk.loadFromXML(attrib);
391         addForeignKey(fk);
392         return fk;
393     }
394 
395     /***
396      * Gets the column that subclasses of the class representing this
397      * table can be produced from.
398      */
399     public Column getChildrenColumn()
400     {
401         return inheritanceColumn;
402     }
403 
404     /***
405      * Get the objects that can be created from this table.
406      */
407     public List getChildrenNames()
408     {
409         if (inheritanceColumn == null
410                 || !inheritanceColumn.isEnumeratedClasses())
411         {
412             return null;
413         }
414         List children = inheritanceColumn.getChildren();
415         List names = new ArrayList(children.size());
416         for (int i = 0; i < children.size(); i++)
417         {
418             names.add(((Inheritance) children.get(i)).getClassName());
419         }
420         return names;
421     }
422 
423     /***
424      * Adds the foreign key from another table that refers to this table.
425      *
426      * @param fk A foreign key refering to this table
427      */
428     public void addReferrer(ForeignKey fk)
429     {
430         if (referrers == null)
431         {
432             referrers = new ArrayList(5);
433         }
434         referrers.add(fk);
435     }
436 
437     /***
438      * Get list of references to this table.
439      *
440      * @return A list of references to this table
441      */
442     public List getReferrers()
443     {
444         return referrers;
445     }
446 
447     /***
448      * Set whether this table contains a foreign PK
449      *
450      * @param b
451      */
452     public void setContainsForeignPK(boolean b)
453     {
454         containsForeignPK = b;
455     }
456 
457     /***
458      * Determine if this table contains a foreign PK
459      */
460     public boolean getContainsForeignPK()
461     {
462         return containsForeignPK;
463     }
464 
465     /***
466      * A list of tables referenced by foreign keys in this table
467      *
468      * @return A list of tables
469      */
470     public List getForeignTableNames()
471     {
472         if (foreignTableNames == null)
473         {
474             foreignTableNames = new ArrayList(1);
475         }
476         return foreignTableNames;
477     }
478 
479     /***
480      * Adds a new FK to the FK list and set the
481      * parent table of the column to the current table
482      *
483      * @param fk A foreign key
484      */
485     public void addForeignKey(ForeignKey fk)
486     {
487         fk.setTable (this);
488         foreignKeys.add(fk);
489 
490         if (foreignTableNames == null)
491         {
492             foreignTableNames = new ArrayList(5);
493         }
494         if (!foreignTableNames.contains(fk.getForeignTableName()))
495         {
496             foreignTableNames.add(fk.getForeignTableName());
497         }
498     }
499 
500     /***
501      * Return true if the column requires a transaction in Postgres
502      */
503     public boolean requiresTransactionInPostgres()
504     {
505         return needsTransactionInPostgres;
506     }
507 
508     /***
509      * A utility function to create a new id method parameter
510      * from attrib and add it to this table.
511      */
512     public IdMethodParameter addIdMethodParameter(Attributes attrib)
513     {
514         IdMethodParameter imp = new IdMethodParameter();
515         imp.loadFromXML(attrib);
516         addIdMethodParameter(imp);
517         return imp;
518     }
519 
520 
521     /***
522      * Adds a new ID method parameter to the list and sets the parent
523      * table of the column associated with the supplied parameter to this table.
524      *
525      * @param imp The column to add as an ID method parameter.
526      */
527     public void addIdMethodParameter(IdMethodParameter imp)
528     {
529         imp.setTable(this);
530         if (idMethodParameters == null)
531         {
532             idMethodParameters = new ArrayList(2);
533         }
534         idMethodParameters.add(imp);
535     }
536 
537     /***
538      * Adds a new index to the index list and set the
539      * parent table of the column to the current table
540      */
541     public void addIndex(Index index)
542     {
543         index.setTable (this);
544         indices.add(index);
545     }
546 
547     /***
548      * A utility function to create a new index
549      * from attrib and add it to this table.
550      */
551     public Index addIndex(Attributes attrib)
552     {
553         Index index = new Index();
554         index.loadFromXML(attrib);
555         addIndex(index);
556         return index;
557     }
558 
559     /***
560      * Adds a new Unique to the Unique list and set the
561      * parent table of the column to the current table
562      */
563     public void addUnique(Unique unique)
564     {
565         unique.setTable(this);
566         unices.add(unique);
567     }
568 
569     /***
570      * A utility function to create a new Unique
571      * from attrib and add it to this table.
572      *
573      * @param attrib the xml attributes
574      */
575     public Unique addUnique(Attributes attrib)
576     {
577         Unique unique = new Unique();
578         unique.loadFromXML(attrib);
579         addUnique(unique);
580         return unique;
581     }
582 
583     /***
584      * Get the name of the Table
585      */
586     public String getName()
587     {
588         return name;
589     }
590 
591     /***
592      * Set the name of the Table
593      */
594     public void setName(String newName)
595     {
596         name = newName;
597     }
598 
599     /***
600      * Get the description for the Table
601      */
602     public String getDescription()
603     {
604         return description;
605     }
606 
607     /***
608      * Set the description for the Table
609      *
610      * @param newDescription description for the Table
611      */
612     public void setDescription(String newDescription)
613     {
614         description = newDescription;
615     }
616 
617     /***
618      * Get name to use in Java sources
619      */
620     public String getJavaName()
621     {
622         if (javaName == null)
623         {
624             List inputs = new ArrayList(2);
625             inputs.add(name);
626             inputs.add(javaNamingMethod);
627             try
628             {
629                 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR,
630                                                     inputs);
631             }
632             catch (EngineException e)
633             {
634                 log.error(e, e);
635             }
636         }
637         return javaName;
638     }
639 
640     /***
641      * Set name to use in Java sources
642      */
643     public void setJavaName(String javaName)
644     {
645         this.javaName = javaName;
646     }
647 
648     /***
649      * Get the method for generating pk's
650      */
651     public String getIdMethod()
652     {
653         if (idMethod == null)
654         {
655             return IDMethod.NO_ID_METHOD;
656         }
657         else
658         {
659             return idMethod;
660         }
661     }
662 
663     /***
664      * Set the method for generating pk's
665      */
666     public void setIdMethod(String idMethod)
667     {
668         this.idMethod = idMethod;
669     }
670 
671     /***
672      * Skip generating sql for this table (in the event it should
673      * not be created from scratch).
674      * @return value of skipSql.
675      */
676     public boolean isSkipSql()
677     {
678         return (skipSql || isAlias() || isForReferenceOnly());
679     }
680 
681     /***
682      * Set whether this table should have its creation sql generated.
683      * @param v  Value to assign to skipSql.
684      */
685     public void setSkipSql(boolean  v)
686     {
687         this.skipSql = v;
688     }
689 
690     /***
691      * JavaName of om object this entry references.
692      * @return value of external.
693      */
694     public String getAlias()
695     {
696         return alias;
697     }
698 
699     /***
700      * Is this table specified in the schema or is there just
701      * a foreign key reference to it.
702      * @return value of external.
703      */
704     public boolean isAlias()
705     {
706         return (alias != null);
707     }
708 
709     /***
710      * Set whether this table specified in the schema or is there just
711      * a foreign key reference to it.
712      * @param v  Value to assign to alias.
713      */
714     public void setAlias(String v)
715     {
716         this.alias = v;
717     }
718 
719 
720     /***
721      * Interface which objects for this table will implement
722      * @return value of interface.
723      */
724     public String getInterface()
725     {
726         return enterface;
727     }
728 
729     /***
730      * Interface which objects for this table will implement
731      * @param v  Value to assign to interface.
732      */
733     public void setInterface(String  v)
734     {
735         this.enterface = v;
736     }
737 
738     /***
739      * When a table is abstract, it marks the business object class that is
740      * generated as being abstract. If you have a table called "FOO", then the
741      * Foo BO will be <code>public abstract class Foo</code>
742      * This helps support class hierarchies
743      *
744      * @return value of abstractValue.
745      */
746     public boolean isAbstract()
747     {
748         return abstractValue;
749     }
750 
751     /***
752      * When a table is abstract, it marks the business object
753      * class that is generated as being abstract. If you have a
754      * table called "FOO", then the Foo BO will be
755      * <code>public abstract class Foo</code>
756      * This helps support class hierarchies
757      *
758      * @param v  Value to assign to abstractValue.
759      */
760     public void setAbstract(boolean  v)
761     {
762         this.abstractValue = v;
763     }
764 
765     /***
766      * Get the value of package.
767      *
768      * @return value of package.
769      */
770     public String getPackage()
771     {
772         if (pkg != null)
773         {
774             return pkg;
775         }
776         else
777         {
778             return this.getDatabase().getPackage();
779         }
780     }
781 
782     /***
783      * Set the value of package.
784      *
785      * @param v  Value to assign to package.
786      */
787     public void setPackage(String v)
788     {
789         this.pkg = v;
790     }
791 
792     /***
793      * Returns a List containing all the columns in the table
794      *
795      * @return a List containing all the columns
796      */
797     public List getColumns()
798     {
799         return columnList;
800     }
801 
802     /***
803      * Utility method to get the number of columns in this table
804      */
805     public int getNumColumns()
806     {
807         return columnList.size();
808     }
809 
810     /***
811      * Returns a List containing all the FKs in the table
812      *
813      * @return a List containing all the FKs
814      */
815     public List getForeignKeys()
816     {
817         return foreignKeys;
818     }
819 
820     /***
821      * Returns a Collection of parameters relevant for the chosen
822      * id generation method.
823      */
824     public List getIdMethodParameters()
825     {
826         return idMethodParameters;
827     }
828 
829     /***
830      * A name to use for creating a sequence if one is not specified.
831      *
832      * @return name of the sequence
833      */
834     public String getSequenceName()
835     {
836         String result = null;
837         if (getIdMethod().equals(NATIVE))
838         {
839             List idMethodParams = getIdMethodParameters();
840             if (idMethodParams == null)
841             {
842                 result = getName() + "_SEQ";
843             }
844             else
845             {
846                 result = ((IdMethodParameter) idMethodParams.get(0)).getValue();
847             }
848         }
849         return result;
850     }
851 
852     /***
853      * Returns a List containing all the indices in the table
854      *
855      * @return A List containing all the indices
856      */
857     public List getIndices()
858     {
859         return indices;
860     }
861 
862     /***
863      * Returns a List containing all the UKs in the table
864      *
865      * @return A List containing all the UKs
866      */
867     public List getUnices()
868     {
869         return unices;
870     }
871 
872     /***
873      * Returns a specified column.
874      *
875      * @param name name of the column
876      * @return Return a Column object or null if it does not exist.
877      */
878     public Column getColumn(String name)
879     {
880         return (Column) columnsByName.get(name);
881     }
882 
883     /***
884      * Returns a specified column.
885      *
886      * @param javaName java name of the column
887      * @return Return a Column object or null if it does not exist.
888      */
889     public Column getColumnByJavaName(String javaName)
890     {
891         return (Column) columnsByJavaName.get(javaName);
892     }
893 
894     /***
895      * Return the first foreign key that includes col in it's list
896      * of local columns.  Eg. Foreign key (a,b,c) refrences tbl(x,y,z)
897      * will be returned of col is either a,b or c.
898      *
899      * @param col column name included in the key
900      * @return Return a Column object or null if it does not exist.
901      */
902     public ForeignKey getForeignKey(String col)
903     {
904         ForeignKey firstFK = null;
905         for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
906         {
907             ForeignKey key = (ForeignKey) iter.next();
908             if (key.getLocalColumns().contains(col))
909             {
910                 if (firstFK == null)
911                 {
912                     firstFK = key;
913                 }
914                 else
915                 {
916                     //System.out.println(col+" is in multiple FKs.  This is not"
917                     //                   + " being handled properly.");
918                     //throw new IllegalStateException("Cannot call method if " +
919                     //    "column is referenced multiple times");
920                 }
921             }
922         }
923         return firstFK;
924     }
925 
926     /***
927      * Returns true if the table contains a specified column
928      *
929      * @param col the column
930      * @return true if the table contains the column
931      */
932     public boolean containsColumn(Column col)
933     {
934         return columnList.contains(col);
935     }
936 
937     /***
938      * Returns true if the table contains a specified column
939      *
940      * @param name name of the column
941      * @return true if the table contains the column
942      */
943     public boolean containsColumn(String name)
944     {
945         return (getColumn(name) != null);
946     }
947 
948     /***
949      * Set the parent of the table
950      *
951      * @param parent the parant database
952      */
953     public void setDatabase(Database parent)
954     {
955         tableParent = parent;
956     }
957 
958     /***
959      * Get the parent of the table
960      *
961      * @return the parant database
962      */
963     public Database getDatabase()
964     {
965         return tableParent;
966     }
967 
968     /***
969      * Flag to determine if code/sql gets created for this table.
970      * Table will be skipped, if return true.
971      * @return value of forReferenceOnly.
972      */
973     public boolean isForReferenceOnly()
974     {
975         return forReferenceOnly;
976     }
977 
978     /***
979      * Flag to determine if code/sql gets created for this table.
980      * Table will be skipped, if set to true.
981      * @param v  Value to assign to forReferenceOnly.
982      */
983     public void setForReferenceOnly(boolean  v)
984     {
985         this.forReferenceOnly = v;
986     }
987 
988     /***
989      * Returns a XML representation of this table.
990      *
991      * @return XML representation of this table
992      */
993     public String toString()
994     {
995         StringBuffer result = new StringBuffer();
996 
997         result.append ("<table name=\"")
998               .append(name)
999               .append('\"');
1000 
1001         if (javaName != null)
1002         {
1003             result.append(" javaName=\"")
1004                   .append(javaName)
1005                   .append('\"');
1006         }
1007 
1008         if (idMethod != null)
1009         {
1010             result.append(" idMethod=\"")
1011                   .append(idMethod)
1012                   .append('\"');
1013         }
1014 
1015         if (skipSql)
1016         {
1017             result.append(" skipSql=\"")
1018                   .append(new Boolean(skipSql))
1019                   .append('\"');
1020         }
1021 
1022         if (abstractValue)
1023         {
1024             result.append(" abstract=\"")
1025                   .append(new Boolean(abstractValue))
1026                   .append('\"');
1027         }
1028 
1029         if (baseClass != null)
1030         {
1031             result.append(" baseClass=\"")
1032                   .append(baseClass)
1033                   .append('\"');
1034         }
1035 
1036         if (basePeer != null)
1037         {
1038             result.append(" basePeer=\"")
1039                   .append(basePeer)
1040                   .append('\"');
1041         }
1042 
1043         result.append(">\n");
1044 
1045         if (columnList != null)
1046         {
1047             for (Iterator iter = columnList.iterator(); iter.hasNext();)
1048             {
1049                 result.append(iter.next());
1050             }
1051         }
1052 
1053         if (foreignKeys != null)
1054         {
1055             for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
1056             {
1057                 result.append(iter.next());
1058             }
1059         }
1060 
1061         if (idMethodParameters != null)
1062         {
1063             Iterator iter = idMethodParameters.iterator();
1064             while (iter.hasNext())
1065             {
1066                 result.append(iter.next());
1067             }
1068         }
1069 
1070         result.append ("</table>\n");
1071 
1072         return result.toString();
1073     }
1074 
1075     /***
1076      * Returns the collection of Columns which make up the single primary
1077      * key for this table.
1078      *
1079      * @return A list of the primary key parts.
1080      */
1081     public List getPrimaryKey()
1082     {
1083         List pk = new ArrayList(columnList.size());
1084 
1085         Iterator iter = columnList.iterator();
1086         while (iter.hasNext())
1087         {
1088             Column col = (Column) iter.next();
1089             if (col.isPrimaryKey())
1090             {
1091                 pk.add(col);
1092             }
1093         }
1094         return pk;
1095     }
1096 
1097     /***
1098      * Determine whether this table has a primary key.
1099      *
1100      * @return Whether this table has any primary key parts.
1101      */
1102     public boolean hasPrimaryKey()
1103     {
1104         return (getPrimaryKey().size() > 0);
1105     }
1106 
1107     /***
1108      * Returns all parts of the primary key, separated by commas.
1109      *
1110      * @return A CSV list of primary key parts.
1111      */
1112     public String printPrimaryKey()
1113     {
1114         return printList(columnList);
1115     }
1116 
1117     /***
1118      * Returns the elements of the list, separated by commas.
1119      *
1120      * @param list a list of Columns
1121      * @return A CSV list.
1122      */
1123     private String printList(List list)
1124     {
1125         StringBuffer result = new StringBuffer();
1126         boolean comma = false;
1127         for (Iterator iter = list.iterator(); iter.hasNext();)
1128         {
1129             Column col = (Column) iter.next();
1130             if (col.isPrimaryKey())
1131             {
1132                 if (comma)
1133                 {
1134                     result.append(',');
1135                 }
1136                 else
1137                 {
1138                     comma = true;
1139                 }
1140                 result.append(col.getName());
1141             }
1142         }
1143         return result.toString();
1144     }
1145 
1146     /***
1147      * Force all columns to set the correctGetters property.
1148      *
1149      * @param value The new value of the correctGetters property.
1150      * @since 3.2
1151      */
1152     public void setCorrectGetters(Boolean value)
1153     {
1154         boolean correctGetters = value != null && value.booleanValue();
1155         for (Iterator it = columnList.iterator(); it.hasNext();)
1156         {
1157             Column col = (Column) it.next();
1158             col.setCorrectGetters(correctGetters);
1159         }
1160     }
1161 
1162     /***
1163      * Add an XML Specified option key/value pair to this element's option set.
1164      *
1165      * @param key the key of the option.
1166      * @param value the value of the option.
1167      */
1168     public void addOption(String key, String value)
1169     {
1170         options.put(key, value);
1171     }
1172 
1173     /***
1174      * Get the value that was associated with this key in an XML option
1175      * element.
1176      *
1177      * @param key the key of the option.
1178      * @return The value for the key or a null.
1179      */
1180     public String getOption(String key)
1181     {
1182         return (String) options.get(key);
1183     }
1184 
1185     /***
1186      * Gets the full ordered hashtable array of items specified by XML option
1187      * statements under this element.<p>
1188      *
1189      * Note, this is not thread save but since it's only used for
1190      * generation which is single threaded, there should be minimum
1191      * danger using this in Velocity.
1192      *
1193      * @return An Map of all options. Will not be null but may be empty.
1194      */
1195     public Map getOptions()
1196     {
1197         return options;
1198     }
1199 }