View Javadoc

1   package org.apache.torque.engine.database.model;
2   
3   /*
4    * Copyright 2001-2004 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.Date;
21  import java.util.Hashtable;
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   * A Class for holding data about a column 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:jon@latchkey.com">Jon S. Stevens</a>
39   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
40   * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
41   * @version $Id: Column.java,v 1.5.2.5 2004/09/11 13:57:33 seade Exp $
42   */
43  public class Column
44  {
45      /*** Logging class from commons.logging */
46      private static Log log = LogFactory.getLog(Column.class);
47      private String name;
48      private String description;
49      private String javaName = null;
50      private String javaNamingMethod;
51      private boolean isNotNull = false;
52      private String size;
53      /*** type as defined in schema.xml */
54      private String torqueType;
55      private String javaType;
56      private Object columnType;
57      private Table parentTable;
58      private int position;
59      private boolean isPrimaryKey = false;
60      private boolean isUnique = false;
61      private boolean isAutoIncrement = false;
62      private String defaultValue;
63      private List referrers;
64      // only one type is supported currently, which assumes the
65      // column either contains the classnames or a key to
66      // classnames specified in the schema.  Others may be
67      // supported later.
68      private String inheritanceType;
69      private boolean isInheritance;
70      private boolean isEnumeratedClasses;
71      private List inheritanceList;
72      private boolean needsTransactionInPostgres;
73  
74      /*** class name to do input validation on this column */
75      private String inputValidator = null;
76  
77      /***
78       * Creates a new instance with a <code>null</code> name.
79       */
80      public Column()
81      {
82          this(null);
83      }
84  
85      /***
86       * Creates a new column and set the name
87       *
88       * @param name column name
89       */
90      public Column(String name)
91      {
92          this.name = name;
93      }
94  
95      /***
96       * Return a comma delimited string listing the specified columns.
97       *
98       * @param columns Either a list of <code>Column</code> objects, or
99       * a list of <code>String</code> objects with column names.
100      */
101     public static String makeList(List columns)
102     {
103         Object obj = columns.get(0);
104         boolean isColumnList = (obj instanceof Column);
105         if (isColumnList)
106         {
107             obj = ((Column) obj).getName();
108         }
109         StringBuffer buf = new StringBuffer((String) obj);
110         for (int i = 1; i < columns.size(); i++)
111         {
112             obj = columns.get(i);
113             if (isColumnList)
114             {
115                 obj = ((Column) obj).getName();
116             }
117             buf.append(", ").append(obj);
118         }
119         return buf.toString();
120     }
121 
122     /***
123      * Imports a column from an XML specification
124      */
125     public void loadFromXML(Attributes attrib)
126     {
127         //Name
128         name = attrib.getValue("name");
129 
130         javaName = attrib.getValue("javaName");
131         javaType = attrib.getValue("javaType");
132         if (javaType != null && javaType.length() == 0)
133         {
134             javaType = null;
135         }
136 
137         // retrieves the method for converting from specified name to
138         // a java name.
139         javaNamingMethod = attrib.getValue("javaNamingMethod");
140         if (javaNamingMethod == null)
141         {
142             javaNamingMethod
143                     = parentTable.getDatabase().getDefaultJavaNamingMethod();
144         }
145 
146         //Primary Key
147         String primaryKey = attrib.getValue("primaryKey");
148         //Avoid NullPointerExceptions on string comparisons.
149         isPrimaryKey = ("true".equals (primaryKey));
150 
151         // If this column is a primary key then it can't be null.
152         if ("true".equals (primaryKey))
153         {
154             isNotNull = true;
155         }
156 
157         // HELP: Should primary key, index, and/or idMethod="native"
158         // affect isNotNull?  If not, please document why here.
159         String notNull = attrib.getValue("required");
160         isNotNull = (notNull != null && "true".equals(notNull));
161 
162         //AutoIncrement/Sequences
163         String autoIncrement = attrib.getValue("autoIncrement");
164         isAutoIncrement = ("true".equals(autoIncrement));
165 
166         //Default column value.
167         defaultValue = attrib.getValue("default");
168 
169         size = attrib.getValue("size");
170 
171         setType(attrib.getValue("type"));
172 
173         inheritanceType = attrib.getValue("inheritance");
174         isInheritance = (inheritanceType != null
175                 && !inheritanceType.equals("false"));
176 
177         this.inputValidator = attrib.getValue("inputValidator");
178         description = attrib.getValue("description");
179     }
180 
181     /***
182      * Returns table.column
183      */
184     public String getFullyQualifiedName()
185     {
186         return (parentTable.getName() + '.' + name);
187     }
188 
189     /***
190      * Get the name of the column
191      */
192     public String getName()
193     {
194         return name;
195     }
196 
197     /***
198      * Set the name of the column
199      */
200     public void setName(String newName)
201     {
202         name = newName;
203     }
204 
205     /***
206      * Get the description for the Table
207      */
208     public String getDescription()
209     {
210         return description;
211     }
212 
213     /***
214      * Set the description for the Table
215      *
216      * @param newDescription description for the Table
217      */
218     public void setDescription(String newDescription)
219     {
220         description = newDescription;
221     }
222 
223     /***
224      * Get name to use in Java sources to build method names.
225      * 
226      * @return the capitalised javaName
227      */
228     public String getJavaName()
229     {
230         if (javaName == null)
231         {
232             List inputs = new ArrayList(2);
233             inputs.add(name);
234             inputs.add(javaNamingMethod);
235             try
236             {
237                 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR,
238                                                     inputs);
239             }
240             catch (EngineException e)
241             {
242                 log.error(e, e);
243             }
244         }
245         return StringUtils.capitalise(javaName);
246     }
247     
248     /***
249      * Get variable name to use in Java sources (= uncapitalised java name)
250      */    
251     public String getUncapitalisedJavaName()
252     {
253         return StringUtils.uncapitalise(getJavaName());
254     }
255 
256     /***
257      * Set name to use in Java sources
258      */
259     public void setJavaName(String javaName)
260     {
261         this.javaName = javaName;
262     }
263 
264     /***
265      * Get type to use in Java sources
266      */
267     public String getJavaType()
268     {
269         return javaType;
270     }
271 
272     /***
273      * Get the location of this column within the table (one-based).
274      * @return value of position.
275      */
276     public int getPosition()
277     {
278         return position;
279     }
280 
281     /***
282      * Get the location of this column within the table (one-based).
283      * @param v  Value to assign to position.
284      */
285     public void setPosition(int  v)
286     {
287         this.position = v;
288     }
289 
290     /***
291      * Set the parent Table of the column
292      */
293     public void setTable(Table parent)
294     {
295         parentTable = parent;
296     }
297 
298     /***
299      * Get the parent Table of the column
300      */
301     public Table getTable()
302     {
303         return parentTable;
304     }
305 
306     /***
307      * Returns the Name of the table the column is in
308      */
309     public String getTableName()
310     {
311         return parentTable.getName();
312     }
313 
314     /***
315      * A utility function to create a new column
316      * from attrib and add it to this table.
317      */
318     public Inheritance addInheritance(Attributes attrib)
319     {
320         Inheritance inh = new Inheritance();
321         inh.loadFromXML (attrib);
322         addInheritance(inh);
323 
324         return inh;
325     }
326 
327     /***
328      * Adds a new inheritance definition to the inheritance list and set the
329      * parent column of the inheritance to the current column
330      */
331     public void addInheritance(Inheritance inh)
332     {
333         inh.setColumn(this);
334         if (inheritanceList == null)
335         {
336             inheritanceList = new ArrayList();
337             isEnumeratedClasses = true;
338         }
339         inheritanceList.add(inh);
340     }
341 
342     /***
343      * Get the inheritance definitions.
344      */
345     public List getChildren()
346     {
347         return inheritanceList;
348     }
349 
350     /***
351      * Determine if this column is a normal property or specifies a
352      * the classes that are represented in the table containing this column.
353      */
354     public boolean isInheritance()
355     {
356         return isInheritance;
357     }
358 
359     /***
360      * Determine if possible classes have been enumerated in the xml file.
361      */
362     public boolean isEnumeratedClasses()
363     {
364         return isEnumeratedClasses;
365     }
366 
367     /***
368      * Return the isNotNull property of the column
369      */
370     public boolean isNotNull()
371     {
372         return isNotNull;
373     }
374 
375     /***
376      * Set the isNotNull property of the column
377      */
378     public void setNotNull(boolean status)
379     {
380         isNotNull = status;
381     }
382 
383     /***
384      * Set if the column is a primary key or not
385      */
386     public void setPrimaryKey(boolean pk)
387     {
388         isPrimaryKey = pk;
389     }
390 
391     /***
392      * Return true if the column is a primary key
393      */
394     public boolean isPrimaryKey()
395     {
396         return isPrimaryKey;
397     }
398 
399     /***
400      * Set true if the column is UNIQUE
401      */
402     public void setUnique (boolean u)
403     {
404         isUnique = u;
405     }
406 
407     /***
408      * Get the UNIQUE property
409      */
410     public boolean isUnique()
411     {
412         return isUnique;
413     }
414 
415     /***
416      * Return true if the column requires a transaction in Postgres
417      */
418     public boolean requiresTransactionInPostgres()
419     {
420         return needsTransactionInPostgres;
421     }
422 
423     /***
424      * Utility method to determine if this column is a foreign key.
425      */
426     public boolean isForeignKey()
427     {
428         return (getForeignKey() != null);
429     }
430 
431     /***
432      * Determine if this column is a foreign key that refers to the
433      * same table as another foreign key column in this table.
434      */
435     public boolean isMultipleFK()
436     {
437         ForeignKey fk = getForeignKey();
438         if (fk != null)
439         {
440             ForeignKey[] fks = parentTable.getForeignKeys();
441             for (int i = 0; i < fks.length; i++)
442             {
443                 if (fks[i].getForeignTableName()
444                         .equals(fk.getForeignTableName())
445                         && !fks[i].getLocalColumns().contains(this.name))
446                 {
447                     return true;
448                 }
449             }
450         }
451 
452         // No multiple foreign keys.
453         return false;
454     }
455 
456     /***
457      * get the foreign key object for this column
458      * if it is a foreign key or part of a foreign key
459      */
460     public ForeignKey getForeignKey()
461     {
462         return parentTable.getForeignKey (this.name);
463     }
464 
465     /***
466      * Utility method to get the related table of this column if it is a foreign
467      * key or part of a foreign key
468      */
469     public String getRelatedTableName()
470     {
471         ForeignKey fk = getForeignKey();
472         return (fk == null ? null : fk.getForeignTableName());
473     }
474 
475 
476     /***
477      * Utility method to get the related column of this local column if this
478      * column is a foreign key or part of a foreign key.
479      */
480     public String getRelatedColumnName()
481     {
482         ForeignKey fk = getForeignKey();
483         if (fk == null)
484         {
485             return null;
486         }
487         else
488         {
489             return fk.getLocalForeignMapping().get(this.name).toString();
490         }
491     }
492 
493     /***
494      * Adds the foreign key from another table that refers to this column.
495      */
496     public void addReferrer(ForeignKey fk)
497     {
498         if (referrers == null)
499         {
500             referrers = new ArrayList(5);
501         }
502         referrers.add(fk);
503     }
504 
505     /***
506      * Get list of references to this column.
507      */
508     public List getReferrers()
509     {
510         if (referrers == null)
511         {
512             referrers = new ArrayList(5);
513         }
514         return referrers;
515     }
516 
517     /***
518      * Returns the colunm type
519      */
520     public void setType(String torqueType)
521     {
522         this.torqueType = torqueType;
523         if (torqueType.equals("VARBINARY") || torqueType.equals("BLOB"))
524         {
525             needsTransactionInPostgres = true;
526         }
527     }
528 
529     /***
530      * Returns the column jdbc type as an object
531      */
532     public Object getType()
533     {
534         return TypeMap.getJdbcType(torqueType);
535     }
536 
537     /***
538      * Returns the column type as given in the schema as an object
539      */
540     public Object getTorqueType()
541     {
542         return torqueType;
543     }
544 
545     /***
546      * Utility method to see if the column is a string
547      */
548     public boolean isString()
549     {
550         return (columnType instanceof String);
551     }
552 
553     /***
554      * Utility method to return the value as an element to be usable
555      * in an SQL insert statement. This is used from the SQL loader task
556      */
557     public boolean needEscapedValue()
558     {
559         return (torqueType != null) &&
560                 ( torqueType.equals("VARCHAR")
561                         || torqueType.equals("LONGVARCHAR")
562                         || torqueType.equals("DATE")
563                         || torqueType.equals("DATETIME")
564                         || torqueType.equals("TIMESTAMP")
565                         || torqueType.equals("TIME")
566                         || torqueType.equals("CHAR")
567                         || torqueType.equals("CLOB"));
568     }
569 
570     /***
571      * String representation of the column. This is an xml representation.
572      *
573      * @return string representation in xml
574      */
575     public String toString()
576     {
577         StringBuffer result = new StringBuffer();
578         result.append("    <column name=\"").append(name).append('"');
579 
580         if (javaName != null)
581         {
582             result.append(" javaName=\"").append(javaName).append('"');
583         }
584 
585         if (isPrimaryKey)
586         {
587             result.append(" primaryKey=\"").append(isPrimaryKey).append('"');
588         }
589 
590         if (isNotNull)
591         {
592             result.append(" required=\"true\"");
593         }
594         else
595         {
596             result.append(" required=\"false\"");
597         }
598 
599         result.append(" type=\"").append (torqueType).append('"');
600 
601         if (size != null)
602         {
603             result.append(" size=\"").append(size).append('"');
604         }
605 
606         if (defaultValue != null)
607         {
608             result.append(" default=\"").append(defaultValue).append('"');
609         }
610 
611         if (isInheritance())
612         {
613             result.append(" inheritance=\"").append(inheritanceType)
614                 .append('"');
615         }
616 
617         // Close the column.
618         result.append(" />\n");
619 
620         return result.toString();
621     }
622 
623     /***
624      * Returns the size of the column
625      */
626     public String getSize()
627     {
628         return size;
629     }
630 
631     /***
632      * Set the size of the column
633      */
634     public void setSize(String newSize)
635     {
636         size = newSize;
637     }
638 
639     /***
640      * Return the size in brackets for use in an sql
641      * schema if the type is String.  Otherwise return an empty string
642      */
643     public String printSize()
644     {
645         return (size == null ? "" : '(' + size + ')');
646     }
647 
648     /***
649      * Return a string that will give this column a default value.
650      * <p>
651      * TODO: Properly SQL-escape text values.
652      */
653      public String getDefaultSetting()
654      {
655          StringBuffer dflt = new StringBuffer(0);
656          if (defaultValue != null)
657          {
658              dflt.append("default ");
659              if (TypeMap.isTextType(torqueType))
660              {
661                  // TODO: Properly SQL-escape the text.
662                  dflt.append('\'').append(defaultValue).append('\'');
663              }
664              else
665              {
666                  dflt.append(defaultValue);
667              }
668          }
669          return dflt.toString();
670      }
671 
672     /***
673      * Set a string that will give this column a default value.
674      */
675     public void setDefaultValue(String def)
676     {
677         defaultValue = def;
678     }
679 
680     /***
681      * Get a string that will give this column a default value.
682      */
683     public String getDefaultValue()
684     {
685         return defaultValue;
686     }
687 
688     /***
689      * Returns the class name to do input validation
690      */
691     public String getInputValidator()
692     {
693        return this.inputValidator;
694     }
695 
696     /***
697      * Return auto increment/sequence string for the target database. We need to
698      * pass in the props for the target database!
699      */
700     public boolean isAutoIncrement()
701     {
702         return isAutoIncrement;
703     }
704 
705     /***
706      * Set the auto increment value.
707      * Use isAutoIncrement() to find out if it is set or not.
708      */
709     public void setAutoIncrement(boolean value)
710     {
711         isAutoIncrement = value;
712     }
713 
714     /***
715      * Set the column type from a string property
716      * (normally a string from an sql input file)
717      */
718     public void setTypeFromString (String typeName, String size)
719     {
720         String tn = typeName.toUpperCase();
721         setType(tn);
722 
723         if (size != null)
724         {
725             this.size = size;
726         }
727 
728         if (tn.indexOf ("CHAR") != -1)
729         {
730             torqueType = "VARCHAR";
731             columnType = "";
732         }
733         else if (tn.indexOf ("INT") != -1)
734         {
735             torqueType = "INTEGER";
736             columnType = new Integer (0);
737         }
738         else if (tn.indexOf ("FLOAT") != -1)
739         {
740             torqueType = "FLOAT";
741             columnType = new Float (0);
742         }
743         else if (tn.indexOf ("DATE") != -1)
744         {
745             torqueType = "DATE";
746             columnType = new Date();
747         }
748         else if (tn.indexOf ("TIME") != -1)
749         {
750             torqueType = "TIMESTAMP";
751             columnType = new Date();
752         }
753         else if (tn.indexOf ("BINARY") != -1)
754         {
755             torqueType = "LONGVARBINARY";
756             columnType = new Hashtable();
757         }
758         else
759         {
760             torqueType = "VARCHAR";
761             columnType = "";
762         }
763     }
764 
765     /***
766      * Return a string representation of the
767      * Java object which corresponds to the JDBC
768      * type of this column. Use in the generation
769      * of MapBuilders.
770      */
771     public String getJavaObject()
772     {
773         return TypeMap.getJavaObject(torqueType);
774     }
775 
776     /***
777      * Return a string representation of the primitive java type which
778      * corresponds to the JDBC type of this column.
779      *
780      * @return string representation of the primitive java type
781      */
782     public String getJavaPrimitive()
783     {
784         return TypeMap.getJavaNative(torqueType);
785     }
786 
787     /***
788      * Return a string representation of the native java type which corresponds
789      * to the JDBC type of this column. Use in the generation of Base objects.
790      * This method is used by torque, so it returns Key types for primaryKey and
791      * foreignKey columns
792      *
793      * @return java datatype used by torque
794      */
795     public String getJavaNative()
796     {
797         String jtype = TypeMap.getJavaNativeObject(torqueType);
798         if (isUsePrimitive())
799         {
800             jtype = TypeMap.getJavaNative(torqueType);
801         }
802 
803         return jtype;
804     }
805 
806     /***
807      * Return Village asX() method which corresponds to the JDBC type
808      * which represents this column.
809      */
810     public String getVillageMethod()
811     {
812         String vmethod = TypeMap.getVillageObjectMethod(torqueType);
813         if (isUsePrimitive())
814         {
815             vmethod = TypeMap.getVillageMethod(torqueType);
816         }
817 
818         return vmethod;
819     }
820 
821     /***
822      * Return ParameterParser getX() method which
823      * corresponds to the JDBC type which represents this column.
824      */
825     public String getParameterParserMethod()
826     {
827         return TypeMap.getPPMethod(torqueType);
828     }
829 
830     /***
831      * Returns true if the column type is boolean in the
832      * java object and a numeric (1 or 0) in the db.
833      */
834     public boolean isBooleanInt()
835     {
836         return TypeMap.isBooleanInt(torqueType);
837     }
838 
839     /***
840      * Returns true if the column type is boolean in the
841      * java object and a String ("Y" or "N") in the db.
842      */
843     public boolean isBooleanChar()
844     {
845         return TypeMap.isBooleanChar(torqueType);
846     }
847 
848     /***
849      * Returns true if the column type is boolean in the
850      * java object and a Bit ("1" or "0") in the db.
851      */
852     public boolean isBit()
853     {
854         return TypeMap.isBit(torqueType);
855     }
856 
857     /***
858      * returns true, if the columns java native type is an
859      * boolean, byte, short, int, long, float, double, char
860      */
861     public boolean isPrimitive()
862     {
863         String t = getJavaNative();
864         return "boolean".equals(t)
865             || "byte".equals(t)
866             || "short".equals(t)
867             || "int".equals(t)
868             || "long".equals(t)
869             || "float".equals(t)
870             || "double".equals(t)
871             || "char".equals(t);
872     }
873 
874     public boolean isUsePrimitive()
875     {
876         String s = getJavaType();
877         return (s != null && s.equals("primitive"))
878             || (s == null && !"object".equals(
879                getTable().getDatabase().getDefaultJavaType()));
880     }
881 }