View Javadoc

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