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