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.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
65
66
67
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
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
138
139 javaNamingMethod = attrib.getValue("javaNamingMethod");
140 if (javaNamingMethod == null)
141 {
142 javaNamingMethod
143 = parentTable.getDatabase().getDefaultJavaNamingMethod();
144 }
145
146
147 String primaryKey = attrib.getValue("primaryKey");
148
149 isPrimaryKey = ("true".equals (primaryKey));
150
151
152 if ("true".equals (primaryKey))
153 {
154 isNotNull = true;
155 }
156
157
158
159 String notNull = attrib.getValue("required");
160 isNotNull = (notNull != null && "true".equals(notNull));
161
162
163 String autoIncrement = attrib.getValue("autoIncrement");
164 isAutoIncrement = ("true".equals(autoIncrement));
165
166
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
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
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
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 }