001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.jdbc.schema;
020:
021: import java.math.BigDecimal;
022: import java.math.BigInteger;
023: import java.sql.DatabaseMetaData;
024: import java.sql.Date;
025: import java.sql.Time;
026: import java.sql.Timestamp;
027: import java.sql.Types;
028:
029: import org.apache.commons.lang.StringUtils;
030: import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
031: import org.apache.openjpa.jdbc.meta.VersionStrategy;
032: import org.apache.openjpa.meta.JavaTypes;
033: import serp.util.Numbers;
034:
035: /**
036: * Represents a database column. Closely aligned with the column
037: * information available from {@link DatabaseMetaData}.
038: *
039: * @author Abe White
040: * @author Stephen Kim
041: */
042: public class Column extends ReferenceCounter {
043:
044: public static final int FLAG_UNINSERTABLE = 2 << 0;
045: public static final int FLAG_UNUPDATABLE = 2 << 1;
046: public static final int FLAG_DIRECT_INSERT = 2 << 2;
047: public static final int FLAG_DIRECT_UPDATE = 2 << 3;
048: public static final int FLAG_FK_INSERT = 2 << 4;
049: public static final int FLAG_FK_UPDATE = 2 << 5;
050: public static final int FLAG_PK_JOIN = 2 << 6;
051:
052: private String _name = null;
053: private String _fullName = null;
054: private Table _table = null;
055: private String _tableName = null;
056: private String _schemaName = null;
057: private int _type = Types.OTHER;
058: private String _typeName = null;
059: private int _javaType = JavaTypes.OBJECT;
060: private int _size = 0;
061: private int _decimals = 0;
062: private String _defaultStr = null;
063: private Object _default = null;
064: private Boolean _notNull = null;
065: private boolean _autoAssign = false;
066: private boolean _rel = false;
067: private String _target = null;
068: private String _targetField = null;
069: private int _flags = 0;
070:
071: private int _index = 0;
072: private boolean _pk = false;
073: private VersionStrategy _versionStrategy = null;
074: private String _comment = null;
075:
076: /**
077: * Default constructor.
078: */
079: public Column() {
080: }
081:
082: /**
083: * Constructor.
084: *
085: * @param name the name of the column
086: * @param table the column's table
087: */
088: public Column(String name, Table table) {
089: setName(name);
090: if (table != null) {
091: setTableName(table.getName());
092: setSchemaName(table.getSchemaName());
093: }
094: _table = table;
095: }
096:
097: /**
098: * Called when the column is removed from its table. Removes the column
099: * from all table constraints and indexes, then invalidates it.
100: */
101: void remove() {
102: Table table = getTable();
103: if (table == null)
104: return;
105:
106: Schema schema = table.getSchema();
107: if (schema != null && schema.getSchemaGroup() != null) {
108: Schema[] schemas = schema.getSchemaGroup().getSchemas();
109: Table[] tabs;
110: ForeignKey[] fks;
111: Column[] cols;
112: Column[] pks;
113: for (int i = 0; i < schemas.length; i++) {
114: tabs = schemas[i].getTables();
115: for (int j = 0; j < tabs.length; j++) {
116: fks = tabs[j].getForeignKeys();
117: for (int k = 0; k < fks.length; k++) {
118: cols = fks[k].getColumns();
119: pks = fks[k].getPrimaryKeyColumns();
120: for (int l = 0; l < cols.length; l++)
121: if (this .equals(cols[l])
122: || this .equals(pks[l]))
123: fks[k].removeJoin(cols[l]);
124:
125: cols = fks[k].getConstantColumns();
126: for (int l = 0; l < cols.length; l++)
127: if (this .equals(cols[l]))
128: fks[k].removeJoin(cols[l]);
129:
130: pks = fks[k].getConstantPrimaryKeyColumns();
131: for (int l = 0; l < pks.length; l++)
132: if (this .equals(pks[l]))
133: fks[k].removeJoin(pks[l]);
134:
135: if (fks[k].getColumns().length == 0
136: && fks[k].getConstantColumns().length == 0)
137: tabs[j].removeForeignKey(fks[k]);
138: }
139: }
140: }
141: }
142:
143: Index[] idxs = table.getIndexes();
144: for (int i = 0; i < idxs.length; i++)
145: if (idxs[i].removeColumn(this )
146: && idxs[i].getColumns().length == 0)
147: table.removeIndex(idxs[i]);
148:
149: Unique[] unqs = table.getUniques();
150: for (int i = 0; i < unqs.length; i++)
151: if (unqs[i].removeColumn(this )
152: && unqs[i].getColumns().length == 0)
153: table.removeUnique(unqs[i]);
154:
155: PrimaryKey pk = table.getPrimaryKey();
156: if (pk != null && pk.removeColumn(this )
157: && pk.getColumns().length == 0)
158: table.removePrimaryKey();
159:
160: _table = null;
161: }
162:
163: /**
164: * Return the table for the column.
165: */
166: public Table getTable() {
167: return _table;
168: }
169:
170: /**
171: * The column's table name.
172: */
173: public String getTableName() {
174: return _tableName;
175: }
176:
177: /**
178: * The column's table name. You can only call this method on columns
179: * whose table object is not set.
180: */
181: public void setTableName(String name) {
182: if (getTable() != null)
183: throw new IllegalStateException();
184: _tableName = name;
185: _fullName = null;
186: }
187:
188: /**
189: * Reset the table name with the fully qualified table name which
190: * includes the schema name
191: */
192: public void resetTableName(String name) {
193: _tableName = name;
194: }
195:
196: /**
197: * The column's schema name.
198: */
199: public String getSchemaName() {
200: return _schemaName;
201: }
202:
203: /**
204: * The column's schema name. You can only call this method on columns
205: * whose table object is not set.
206: */
207: public void setSchemaName(String name) {
208: if (getTable() != null)
209: throw new IllegalStateException();
210: _schemaName = name;
211: }
212:
213: /**
214: * Return the column's name.
215: */
216: public String getName() {
217: return _name;
218: }
219:
220: /**
221: * Set the column's name. You can only call this method on columns
222: * whose table object is not set.
223: */
224: public void setName(String name) {
225: if (getTable() != null)
226: throw new IllegalStateException();
227: _name = name;
228: _fullName = null;
229: }
230:
231: /**
232: * Return the column's full name, in the form <table>.<name>.
233: */
234: public String getFullName() {
235: if (_fullName == null) {
236: String name = getName();
237: if (name == null)
238: return null;
239: String tname = getTableName();
240: if (tname == null)
241: return name;
242: _fullName = tname + "." + name;
243: }
244: return _fullName;
245: }
246:
247: /**
248: * Return the column's SQL type. This will be one of the type constants
249: * defined in {@link Types}.
250: */
251: public int getType() {
252: return _type;
253: }
254:
255: /**
256: * Set the column's SQL type. This should be one of the type constants
257: * defined in {@link Types}.
258: */
259: public void setType(int sqlType) {
260: _type = sqlType;
261: }
262:
263: /**
264: * The database-specific SQL type of this column.
265: */
266: public String getTypeName() {
267: return _typeName;
268: }
269:
270: /**
271: * The database-specific SQL type of this column.
272: */
273: public void setTypeName(String typeName) {
274: _typeName = typeName;
275: }
276:
277: /**
278: * The Java type the data in this column is treated as, from
279: * {@link JavaTypes} or {@link JavaSQLTypes}.
280: */
281: public int getJavaType() {
282: return _javaType;
283: }
284:
285: /**
286: * The Java type the data in this column is treated as, from
287: * {@link JavaTypes} or {@link JavaSQLTypes}.
288: */
289: public void setJavaType(int type) {
290: _javaType = type;
291: }
292:
293: /**
294: * Return the column's size.
295: */
296: public int getSize() {
297: return _size;
298: }
299:
300: /**
301: * Set the column's size.
302: */
303: public void setSize(int size) {
304: _size = size;
305: }
306:
307: /**
308: * Return the number of decimal digits for the column, if applicable.
309: */
310: public int getDecimalDigits() {
311: return _decimals;
312: }
313:
314: /**
315: * Set the number of decimal digits for the column.
316: */
317: public void setDecimalDigits(int digits) {
318: _decimals = digits;
319: }
320:
321: /**
322: * Return the default value set for the column, if any.
323: */
324: public String getDefaultString() {
325: return _defaultStr;
326: }
327:
328: /**
329: * Set the default value for the column.
330: */
331: public void setDefaultString(String def) {
332: _defaultStr = def;
333: _default = null;
334: }
335:
336: /**
337: * Return the default value set for this column, if any. If only a default
338: * string has been set, attempts to convert it to the right type based
339: * on the Java type set for this column.
340: */
341: public Object getDefault() {
342: if (_default != null)
343: return _default;
344: if (_defaultStr == null)
345: return null;
346:
347: switch (_javaType) {
348: case JavaTypes.BOOLEAN:
349: case JavaTypes.BOOLEAN_OBJ:
350: _default = ("true".equals(_defaultStr)) ? Boolean.TRUE
351: : Boolean.FALSE;
352: break;
353: case JavaTypes.BYTE:
354: case JavaTypes.BYTE_OBJ:
355: _default = new Byte(_defaultStr);
356: break;
357: case JavaTypes.CHAR:
358: case JavaTypes.CHAR_OBJ:
359: _default = new Character(_defaultStr.charAt(0));
360: break;
361: case JavaTypes.DOUBLE:
362: case JavaTypes.DOUBLE_OBJ:
363: _default = new Double(_defaultStr);
364: break;
365: case JavaTypes.FLOAT:
366: case JavaTypes.FLOAT_OBJ:
367: _default = new Float(_defaultStr);
368: break;
369: case JavaTypes.INT:
370: case JavaTypes.INT_OBJ:
371: _default = Numbers.valueOf(Integer.parseInt(_defaultStr));
372: break;
373: case JavaTypes.LONG:
374: case JavaTypes.LONG_OBJ:
375: _default = Numbers.valueOf(Long.parseLong(_defaultStr));
376: break;
377: case JavaTypes.NUMBER:
378: case JavaTypes.BIGDECIMAL:
379: _default = new BigDecimal(_defaultStr);
380: break;
381: case JavaTypes.SHORT:
382: case JavaTypes.SHORT_OBJ:
383: _default = new Short(_defaultStr);
384: break;
385: case JavaTypes.DATE:
386: _default = new java.util.Date(_defaultStr);
387: break;
388: case JavaTypes.BIGINTEGER:
389: _default = new BigInteger(_defaultStr);
390: break;
391: case JavaSQLTypes.SQL_DATE:
392: _default = Date.valueOf(_defaultStr);
393: break;
394: case JavaSQLTypes.TIMESTAMP:
395: _default = Timestamp.valueOf(_defaultStr);
396: break;
397: case JavaSQLTypes.TIME:
398: _default = Time.valueOf(_defaultStr);
399: break;
400: default:
401: _default = _defaultStr;
402: }
403: return _default;
404: }
405:
406: /**
407: * Set the default value for the column.
408: */
409: public void setDefault(Object def) {
410: _default = def;
411: _defaultStr = (def == null) ? null : def.toString();
412: }
413:
414: /**
415: * Return true if this is a NOT NULL column.
416: */
417: public boolean isNotNull() {
418: return _notNull == Boolean.TRUE;
419: }
420:
421: /**
422: * Set whether this is a NOT NULL column.
423: */
424: public void setNotNull(boolean notNull) {
425: _notNull = (notNull) ? Boolean.TRUE : Boolean.FALSE;
426: }
427:
428: /**
429: * Whether the not-null property has been set.
430: */
431: public boolean isNotNullExplicit() {
432: return _notNull != null;
433: }
434:
435: /**
436: * Whether this column is auto-assigned a value on insert.
437: */
438: public boolean isAutoAssigned() {
439: return _autoAssign;
440: }
441:
442: /**
443: * Whether this column is auto-incrementing.
444: */
445: public void setAutoAssigned(boolean autoAssign) {
446: if (autoAssign != _autoAssign && getTable() != null)
447: getTable().changeAutoAssigned(this );
448: _autoAssign = autoAssign;
449: }
450:
451: /**
452: * Whether this column stores some form of serialized identity value for
453: * a related record. This makes the column dependent on the knowing the
454: * final identity of the relation before the column value is set.
455: */
456: public boolean isRelationId() {
457: return _rel;
458: }
459:
460: /**
461: * Whether this column stores some form of serialized identity value for
462: * a related record. This makes the column dependent on the knowing the
463: * final identity of the relation before the column value is set.
464: */
465: public void setRelationId(boolean rel) {
466: if (rel != _rel && getTable() != null)
467: getTable().changeRelationId(this );
468: _rel = rel;
469: }
470:
471: /**
472: * The name of the column this column joins to, if any. Used for mapping.
473: */
474: public String getTarget() {
475: return _target;
476: }
477:
478: /**
479: * The name of the column this column joins to, if any. Used for mapping.
480: */
481: public void setTarget(String target) {
482: _target = StringUtils.trimToNull(target);
483: }
484:
485: /**
486: * The name of the field this column joins to, if any. Used for mapping.
487: */
488: public String getTargetField() {
489: return _targetField;
490: }
491:
492: /**
493: * The name of the field this column joins to, if any. Used for mapping.
494: */
495: public void setTargetField(String target) {
496: if (target != null && target.length() == 0)
497: target = null;
498: _targetField = target;
499: }
500:
501: /**
502: * Flags are used for bookkeeping information. They are ignored at runtime.
503: */
504: public boolean getFlag(int flag) {
505: return (_flags & flag) != 0;
506: }
507:
508: /**
509: * Flags are used for bookkeeping information. They are ignored at runtime.
510: */
511: public void setFlag(int flag, boolean on) {
512: if (on)
513: _flags |= flag;
514: else
515: _flags &= ~flag;
516: }
517:
518: /**
519: * Return true if this column belongs to the table's primary key.
520: */
521: public boolean isPrimaryKey() {
522: return _pk;
523: }
524:
525: /**
526: * Set whether this column belongs to the table's primary key.
527: */
528: void setPrimaryKey(boolean pk) {
529: _pk = pk;
530: }
531:
532: /**
533: * Return the column's 0-based index in the owning table.
534: */
535: public int getIndex() {
536: if (getTable() != null)
537: getTable().indexColumns();
538: return _index;
539: }
540:
541: /**
542: * Set the column's 0-based index in the owning table.
543: */
544: void setIndex(int index) {
545: _index = index;
546: }
547:
548: /**
549: * Whether this column is a LOB.
550: */
551: public boolean isLob() {
552: switch (_type) {
553: case Types.BINARY:
554: case Types.BLOB:
555: case Types.LONGVARBINARY:
556: case Types.VARBINARY:
557: case Types.CLOB:
558: return true;
559: default:
560: return false;
561: }
562: }
563:
564: /**
565: * Return true if this column is compatible with the given JDBC type
566: * from {@link Types} and size.
567: */
568: public boolean isCompatible(int type, String typeName, int size,
569: int decimals) {
570: if (type == Types.OTHER || getType() == Types.OTHER)
571: return true;
572:
573: // note that the given size is currently ignored, but may be useful
574: // to dynamically-populating subclasses
575: switch (getType()) {
576: case Types.BIT:
577: case Types.TINYINT:
578: case Types.BIGINT:
579: case Types.INTEGER:
580: case Types.NUMERIC:
581: case Types.SMALLINT:
582: case Types.DECIMAL:
583: case Types.DOUBLE:
584: case Types.FLOAT:
585: case Types.REAL:
586: switch (type) {
587: case Types.BIT:
588: case Types.TINYINT:
589: case Types.BIGINT:
590: case Types.INTEGER:
591: case Types.NUMERIC:
592: case Types.SMALLINT:
593: case Types.DECIMAL:
594: case Types.DOUBLE:
595: case Types.FLOAT:
596: case Types.REAL:
597: return true;
598: default:
599: return false;
600: }
601: case Types.BINARY:
602: case Types.BLOB:
603: case Types.LONGVARBINARY:
604: case Types.VARBINARY:
605: case Types.OTHER:
606: switch (type) {
607: case Types.BINARY:
608: case Types.BLOB:
609: case Types.LONGVARBINARY:
610: case Types.VARBINARY:
611: case Types.OTHER:
612: return true;
613: default:
614: return false;
615: }
616: case Types.CLOB:
617: case Types.CHAR:
618: case Types.LONGVARCHAR:
619: case Types.VARCHAR:
620: switch (type) {
621: case Types.CLOB:
622: case Types.CHAR:
623: case Types.LONGVARCHAR:
624: case Types.VARCHAR:
625: case Types.DATE:
626: case Types.TIME:
627: case Types.TIMESTAMP:
628: return true;
629: default:
630: return false;
631: }
632: case Types.DATE:
633: case Types.TIME:
634: case Types.TIMESTAMP:
635: switch (type) {
636: case Types.LONGVARCHAR:
637: case Types.CLOB:
638: case Types.VARCHAR:
639: case Types.DATE:
640: case Types.TIME:
641: case Types.TIMESTAMP:
642: return true;
643: default:
644: return false;
645: }
646: default:
647: return type == getType();
648: }
649: }
650:
651: /**
652: * Returns the column name.
653: */
654: public String toString() {
655: return getName();
656: }
657:
658: /**
659: * Useful for debugging.
660: */
661: public String getDescription() {
662: StringBuffer buf = new StringBuffer();
663: buf.append("Full Name: ").append(getFullName()).append("\n");
664: buf.append("Type: ").append(Schemas.getJDBCName(getType()))
665: .append("\n");
666: buf.append("Size: ").append(getSize()).append("\n");
667: buf.append("Default: ").append(getDefaultString()).append("\n");
668: buf.append("Not Null: ").append(isNotNull()).append("\n");
669: return buf.toString();
670: }
671:
672: /**
673: * Tests compatibility.
674: */
675: public boolean equalsColumn(Column col) {
676: if (col == this )
677: return true;
678: if (col == null)
679: return false;
680:
681: if (!getFullName().equalsIgnoreCase(col.getFullName()))
682: return false;
683: if (!isCompatible(col.getType(), col.getTypeName(), col
684: .getSize(), col.getDecimalDigits()))
685: return false;
686: if (getType() == Types.VARCHAR && getSize() > 0
687: && col.getSize() > 0 && getSize() != col.getSize())
688: return false;
689: return true;
690: }
691:
692: /**
693: * Copy information from the given column to this one.
694: */
695: public void copy(Column from) {
696: if (from == null)
697: return;
698: if (getName() == null)
699: setName(from.getName());
700: if (getType() == Types.OTHER)
701: setType(from.getType());
702: if (getTypeName() == null)
703: setTypeName(from.getTypeName());
704: if (getJavaType() == JavaTypes.OBJECT)
705: setJavaType(from.getJavaType());
706: if (getSize() == 0)
707: setSize(from.getSize());
708: if (getDecimalDigits() == 0)
709: setDecimalDigits(from.getDecimalDigits());
710: if (getDefaultString() == null)
711: setDefaultString(from.getDefaultString());
712: if (!isNotNullExplicit() && from.isNotNullExplicit())
713: setNotNull(from.isNotNull());
714: if (!isAutoAssigned())
715: setAutoAssigned(from.isAutoAssigned());
716: if (!isRelationId())
717: setRelationId(from.isRelationId());
718: if (getTarget() == null)
719: setTarget(from.getTarget());
720: if (getTargetField() == null)
721: setTargetField(from.getTargetField());
722: if (_flags == 0)
723: _flags = from._flags;
724: }
725:
726: /**
727: * Whether this column is an XML type.
728: */
729: public boolean isXML() {
730: return _typeName != null && _typeName.startsWith("XML");
731: }
732:
733: public VersionStrategy getVersionStrategy() {
734: return _versionStrategy;
735: }
736:
737: public void setVersionStrategy(VersionStrategy strategy) {
738: this ._versionStrategy = strategy;
739: }
740:
741: public boolean hasComment() {
742: return _comment != null && !_comment.equalsIgnoreCase(_name);
743: }
744:
745: public String getComment() {
746: return _comment;
747: }
748:
749: public void setComment(String comment) {
750: _comment = comment;
751: }
752: }
|