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.io.File;
022: import java.util.ArrayList;
023: import java.util.Collection;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.TreeMap;
027: import java.util.LinkedHashMap;
028:
029: import org.apache.commons.lang.StringUtils;
030: import org.apache.openjpa.lib.meta.SourceTracker;
031:
032: /**
033: * Represents a database table.
034: *
035: * @author Abe White
036: * @author Stephen Kim
037: */
038: public class Table extends NameSet implements Comparable, SourceTracker {
039:
040: private String _name = null;
041: private String _schemaName = null;
042: private Map _colMap = null;
043: private Map _idxMap = null;
044: private Collection _fkList = null;
045: private Collection _unqList = null;
046: private Schema _schema = null;
047: private PrimaryKey _pk = null;
048:
049: // keep track of source
050: private File _source = null;
051: private int _srcType = SRC_OTHER;
052:
053: // cache
054: private String _fullName = null;
055: private Column[] _cols = null;
056: private Column[] _autoAssign = null;
057: private Column[] _rels = null;
058: private ForeignKey[] _fks = null;
059: private Index[] _idxs = null;
060: private Unique[] _unqs = null;
061: private String _comment = null;
062:
063: /**
064: * Default constructor.
065: */
066: public Table() {
067: }
068:
069: /**
070: * Constructor.
071: *
072: * @param name the table name
073: * @param schema the table schema
074: */
075: public Table(String name, Schema schema) {
076: setName(name);
077: addName(name, true);
078: if (schema != null)
079: setSchemaName(schema.getName());
080: _schema = schema;
081: }
082:
083: /**
084: * Called when the table is removed from its schema. Removes all table
085: * members, and invalidates the table.
086: */
087: void remove() {
088: ForeignKey[] fks = getForeignKeys();
089: for (int i = 0; i < fks.length; i++)
090: removeForeignKey(fks[i]);
091: Index[] idxs = getIndexes();
092: for (int i = 0; i < idxs.length; i++)
093: removeIndex(idxs[i]);
094: Unique[] unqs = getUniques();
095: for (int i = 0; i < unqs.length; i++)
096: removeUnique(unqs[i]);
097: removePrimaryKey();
098: Column[] cols = getColumns();
099: for (int i = 0; i < cols.length; i++)
100: removeColumn(cols[i]);
101: _schema = null;
102: _schemaName = null;
103: _fullName = null;
104: }
105:
106: /**
107: * Return the schema for the table.
108: */
109: public Schema getSchema() {
110: return _schema;
111: }
112:
113: /**
114: * The table's schema name.
115: */
116: public String getSchemaName() {
117: return _schemaName;
118: }
119:
120: /**
121: * The table's schema name. You can only call this method on tables
122: * whose schema object is not set.
123: */
124: public void setSchemaName(String name) {
125: if (getSchema() != null)
126: throw new IllegalStateException();
127: _schemaName = name;
128: _fullName = null;
129: }
130:
131: /**
132: * Return the name of the table.
133: */
134: public String getName() {
135: return _name;
136: }
137:
138: /**
139: * Set the name of the table. This method can only be called on tables
140: * that are not part of a schema.
141: */
142: public void setName(String name) {
143: if (getSchema() != null)
144: throw new IllegalStateException();
145: _name = name;
146: _fullName = null;
147: }
148:
149: /**
150: * Return the table name, including schema, using '.' as the
151: * catalog separator.
152: */
153: public String getFullName() {
154: if (_fullName == null) {
155: Schema schema = getSchema();
156: if (schema == null || schema.getName() == null)
157: _fullName = getName();
158: else
159: _fullName = schema.getName() + "." + getName();
160: }
161: return _fullName;
162: }
163:
164: public File getSourceFile() {
165: return _source;
166: }
167:
168: public Object getSourceScope() {
169: return null;
170: }
171:
172: public int getSourceType() {
173: return _srcType;
174: }
175:
176: public void setSource(File source, int srcType) {
177: _source = source;
178: _srcType = srcType;
179: }
180:
181: public String getResourceName() {
182: return getFullName();
183: }
184:
185: /**
186: * Return the table's columns, in alphabetical order.
187: */
188: public Column[] getColumns() {
189: if (_cols == null) {
190: if (_colMap == null)
191: _cols = Schemas.EMPTY_COLUMNS;
192: else {
193: Column[] cols = new Column[_colMap.size()];
194: Iterator itr = _colMap.values().iterator();
195: for (int i = 0; itr.hasNext(); i++) {
196: cols[i] = (Column) itr.next();
197: cols[i].setIndex(i);
198: }
199: _cols = cols;
200: }
201: }
202: return _cols;
203: }
204:
205: /**
206: * Return this table's auto-assigned columns.
207: */
208: public Column[] getAutoAssignedColumns() {
209: if (_autoAssign == null) {
210: if (_colMap == null)
211: _autoAssign = Schemas.EMPTY_COLUMNS;
212: else {
213: Collection autos = null;
214: Column[] cols = getColumns();
215: for (int i = 0; i < cols.length; i++) {
216: if (cols[i].isAutoAssigned()) {
217: if (autos == null)
218: autos = new ArrayList(3);
219: autos.add(cols[i]);
220: }
221: }
222: _autoAssign = (autos == null) ? Schemas.EMPTY_COLUMNS
223: : (Column[]) autos.toArray(new Column[autos
224: .size()]);
225: }
226: }
227: return _autoAssign;
228: }
229:
230: /**
231: * Return this table's relation id columns.
232: */
233: public Column[] getRelationIdColumns() {
234: if (_rels == null) {
235: if (_colMap == null)
236: _rels = Schemas.EMPTY_COLUMNS;
237: else {
238: Collection rels = null;
239: Column[] cols = getColumns();
240: for (int i = 0; i < cols.length; i++) {
241: if (cols[i].isRelationId()) {
242: if (rels == null)
243: rels = new ArrayList(3);
244: rels.add(cols[i]);
245: }
246: }
247: _rels = (rels == null) ? Schemas.EMPTY_COLUMNS
248: : (Column[]) rels.toArray(new Column[rels
249: .size()]);
250: }
251: }
252: return _rels;
253: }
254:
255: /**
256: * Return the column with the given name, or null if none.
257: */
258: public Column getColumn(String name) {
259: if (name == null || _colMap == null)
260: return null;
261: return (Column) _colMap.get(name.toUpperCase());
262: }
263:
264: /**
265: * Add a column to the table.
266: */
267: public Column addColumn(String name) {
268: addName(name, true);
269: Schema schema = getSchema();
270: Column col;
271: if (schema != null && schema.getSchemaGroup() != null)
272: col = schema.getSchemaGroup().newColumn(name, this );
273: else
274: col = new Column(name, this );
275: if (_colMap == null)
276: _colMap = new LinkedHashMap();
277: _colMap.put(name.toUpperCase(), col);
278: _cols = null;
279: return col;
280: }
281:
282: /**
283: * Remove the given column from the table.
284: *
285: * @return true if the column was removed, false if not in the table
286: */
287: public boolean removeColumn(Column col) {
288: if (col == null || _colMap == null)
289: return false;
290:
291: Column cur = (Column) _colMap.get(col.getName().toUpperCase());
292: if (!col.equals(cur))
293: return false;
294:
295: removeName(col.getName());
296: _colMap.remove(col.getName().toUpperCase());
297: _cols = null;
298: if (col.isAutoAssigned())
299: _autoAssign = null;
300: if (col.isRelationId())
301: _rels = null;
302: col.remove();
303: return true;
304: }
305:
306: /**
307: * Import a column from another table.
308: */
309: public Column importColumn(Column col) {
310: if (col == null)
311: return null;
312:
313: Column copy = addColumn(col.getName());
314: copy.setType(col.getType());
315: copy.setTypeName(col.getTypeName());
316: copy.setJavaType(col.getJavaType());
317: copy.setNotNull(col.isNotNull());
318: copy.setDefaultString(col.getDefaultString());
319: copy.setSize(col.getSize());
320: copy.setDecimalDigits(col.getDecimalDigits());
321: copy.setAutoAssigned(col.isAutoAssigned());
322: return copy;
323: }
324:
325: /**
326: * Return the primary key for the table, if any.
327: */
328: public PrimaryKey getPrimaryKey() {
329: return _pk;
330: }
331:
332: /**
333: * Set the primary key for the table.
334: */
335: public PrimaryKey addPrimaryKey() {
336: return addPrimaryKey(null);
337: }
338:
339: /**
340: * Set the primary key for the table.
341: */
342: public PrimaryKey addPrimaryKey(String name) {
343: Schema schema = getSchema();
344: if (schema != null && schema.getSchemaGroup() != null) {
345: schema.getSchemaGroup().addName(name, false);
346: _pk = schema.getSchemaGroup().newPrimaryKey(name, this );
347: } else
348: _pk = new PrimaryKey(name, this );
349: return _pk;
350: }
351:
352: /**
353: * Remove the primary key from this table.
354: *
355: * @return true if there was a pk to remove, false otherwise
356: */
357: public boolean removePrimaryKey() {
358: boolean rem = _pk != null;
359: if (rem) {
360: Schema schema = getSchema();
361: if (schema != null && schema.getSchemaGroup() != null)
362: schema.getSchemaGroup().removeName(_pk.getName());
363: _pk.remove();
364: }
365: _pk = null;
366: return rem;
367: }
368:
369: /**
370: * Import a primary key; column names must match columns of this table.
371: */
372: public PrimaryKey importPrimaryKey(PrimaryKey pk) {
373: if (pk == null)
374: return null;
375:
376: PrimaryKey copy = addPrimaryKey(pk.getName());
377: copy.setLogical(pk.isLogical());
378: Column[] cols = pk.getColumns();
379: for (int i = 0; i < cols.length; i++)
380: copy.addColumn(getColumn(cols[i].getName()));
381: return copy;
382: }
383:
384: /**
385: * Return the foreign key with the given name. If multiple foreign keys
386: * have the name, the first match is returned.
387: */
388: public ForeignKey getForeignKey(String name) {
389: ForeignKey[] fks = getForeignKeys();
390: for (int i = 0; i < fks.length; i++)
391: if (StringUtils.equalsIgnoreCase(name, fks[i].getName()))
392: return fks[i];
393: return null;
394: }
395:
396: /**
397: * Return all foreign keys for the table.
398: */
399: public ForeignKey[] getForeignKeys() {
400: if (_fks == null) {
401: if (_fkList == null)
402: _fks = Schemas.EMPTY_FOREIGN_KEYS;
403: else {
404: ForeignKey[] fks = new ForeignKey[_fkList.size()];
405: Iterator itr = _fkList.iterator();
406: for (int i = 0; itr.hasNext(); i++) {
407: fks[i] = (ForeignKey) itr.next();
408: fks[i].setIndex(i);
409: }
410: _fks = fks;
411: }
412: }
413: return _fks;
414: }
415:
416: /**
417: * Add a foreign key to the table.
418: */
419: public ForeignKey addForeignKey() {
420: return addForeignKey(null);
421: }
422:
423: /**
424: * Add a foreign key to the table. Duplicate key names are not allowed.
425: */
426: public ForeignKey addForeignKey(String name) {
427: Schema schema = getSchema();
428: ForeignKey fk;
429: if (schema != null && schema.getSchemaGroup() != null) {
430: schema.getSchemaGroup().addName(name, false);
431: fk = schema.getSchemaGroup().newForeignKey(name, this );
432: } else
433: fk = new ForeignKey(name, this );
434: if (_fkList == null)
435: _fkList = new ArrayList(3);
436: _fkList.add(fk);
437: _fks = null;
438: return fk;
439: }
440:
441: /**
442: * Remove the given foreign key from the table.
443: *
444: * @return true if the key was removed, false if not in the table
445: */
446: public boolean removeForeignKey(ForeignKey fk) {
447: if (fk == null || _fkList == null)
448: return false;
449:
450: if (!_fkList.remove(fk))
451: return false;
452:
453: Schema schema = getSchema();
454: if (schema != null && schema.getSchemaGroup() != null)
455: schema.getSchemaGroup().removeName(fk.getName());
456: _fks = null;
457: fk.remove();
458: return true;
459: }
460:
461: /**
462: * Import a foreign key; column names must match columns of this table.
463: */
464: public ForeignKey importForeignKey(ForeignKey fk) {
465: if (fk == null)
466: return null;
467:
468: ForeignKey copy = addForeignKey(fk.getName());
469: copy.setDeleteAction(fk.getDeleteAction());
470:
471: Schema schema = getSchema();
472: if (schema != null && schema.getSchemaGroup() != null) {
473: Column[] pks = fk.getPrimaryKeyColumns();
474: Table joined = null;
475: if (pks.length > 0)
476: joined = schema.getSchemaGroup().findTable(
477: pks[0].getTable());
478:
479: Column[] cols = fk.getColumns();
480: for (int i = 0; i < cols.length; i++)
481: copy.join(getColumn(cols[i].getName()), joined
482: .getColumn(pks[i].getName()));
483:
484: cols = fk.getConstantColumns();
485: for (int i = 0; i < cols.length; i++)
486: copy.joinConstant(getColumn(cols[i].getName()), fk
487: .getPrimaryKeyConstant(cols[i]));
488:
489: pks = fk.getConstantPrimaryKeyColumns();
490: if (joined == null && pks.length > 0)
491: joined = schema.getSchemaGroup().findTable(
492: pks[0].getTable());
493: for (int i = 0; i < pks.length; i++)
494: copy.joinConstant(fk.getConstant(pks[i]), joined
495: .getColumn(pks[i].getName()));
496: }
497: return copy;
498: }
499:
500: /**
501: * Return the table's indexes.
502: */
503: public Index[] getIndexes() {
504: if (_idxs == null || _idxs.length == 0)
505: _idxs = (_idxMap == null) ? Schemas.EMPTY_INDEXES
506: : (Index[]) _idxMap.values().toArray(
507: new Index[_idxMap.size()]);
508: return _idxs;
509: }
510:
511: /**
512: * Return the index with the given name, or null if none.
513: */
514: public Index getIndex(String name) {
515: if (name == null || _idxMap == null)
516: return null;
517: return (Index) _idxMap.get(name.toUpperCase());
518: }
519:
520: /**
521: * Add an index to the table.
522: */
523: public Index addIndex(String name) {
524: Schema schema = getSchema();
525: Index idx;
526: if (schema != null && schema.getSchemaGroup() != null) {
527: schema.getSchemaGroup().addName(name, true);
528: idx = schema.getSchemaGroup().newIndex(name, this );
529: } else
530: idx = new Index(name, this );
531: if (_idxMap == null)
532: _idxMap = new TreeMap();
533: _idxMap.put(name.toUpperCase(), idx);
534: _idxs = null;
535: return idx;
536: }
537:
538: /**
539: * Remove the given index from the table.
540: *
541: * @return true if the index was removed, false if not in the table
542: */
543: public boolean removeIndex(Index idx) {
544: if (idx == null || _idxMap == null)
545: return false;
546:
547: Index cur = (Index) _idxMap.get(idx.getName().toUpperCase());
548: if (!idx.equals(cur))
549: return false;
550:
551: _idxMap.remove(idx.getName().toUpperCase());
552: Schema schema = getSchema();
553: if (schema != null && schema.getSchemaGroup() != null)
554: schema.getSchemaGroup().removeName(idx.getName());
555: idx.remove();
556: _idxs = null;
557: return true;
558: }
559:
560: /**
561: * Import an index; column names must match columns of this table.
562: */
563: public Index importIndex(Index idx) {
564: if (idx == null)
565: return null;
566:
567: Index copy = addIndex(idx.getName());
568: copy.setUnique(idx.isUnique());
569:
570: Column[] cols = idx.getColumns();
571: for (int i = 0; i < cols.length; i++)
572: copy.addColumn(getColumn(cols[i].getName()));
573: return copy;
574: }
575:
576: /**
577: * Return the table's unique constraints.
578: */
579: public Unique[] getUniques() {
580: if (_unqs == null)
581: _unqs = (_unqList == null) ? Schemas.EMPTY_UNIQUES
582: : (Unique[]) _unqList.toArray(new Unique[_unqList
583: .size()]);
584: return _unqs;
585: }
586:
587: /**
588: * Return the unique constraint with the given name, or null if none.
589: */
590: public Unique getUnique(String name) {
591: Unique[] unqs = getUniques();
592: for (int i = 0; i < unqs.length; i++)
593: if (StringUtils.equalsIgnoreCase(name, unqs[i].getName()))
594: return unqs[i];
595: return null;
596: }
597:
598: /**
599: * Add a unique constraint to the table.
600: */
601: public Unique addUnique(String name) {
602: Schema schema = getSchema();
603: Unique unq;
604: if (schema != null && schema.getSchemaGroup() != null) {
605: schema.getSchemaGroup().addName(name, false);
606: unq = schema.getSchemaGroup().newUnique(name, this );
607: } else
608: unq = new Unique(name, this );
609: if (_unqList == null)
610: _unqList = new ArrayList(3);
611: _unqList.add(unq);
612: _unqs = null;
613: return unq;
614: }
615:
616: /**
617: * Remove the given unique constraint from the table.
618: *
619: * @return true if the constraint was removed, false if not in the table
620: */
621: public boolean removeUnique(Unique unq) {
622: if (unq == null || _unqList == null)
623: return false;
624:
625: if (!_unqList.remove(unq))
626: return false;
627:
628: Schema schema = getSchema();
629: if (schema != null && schema.getSchemaGroup() != null)
630: schema.getSchemaGroup().removeName(unq.getName());
631: _unqs = null;
632: unq.remove();
633: return true;
634: }
635:
636: /**
637: * Import a constraint; column names must match columns of this table.
638: */
639: public Unique importUnique(Unique unq) {
640: if (unq == null)
641: return null;
642:
643: Unique copy = addUnique(unq.getName());
644: copy.setDeferred(unq.isDeferred());
645:
646: Column[] cols = unq.getColumns();
647: for (int i = 0; i < cols.length; i++)
648: copy.addColumn(getColumn(cols[i].getName()));
649: return copy;
650: }
651:
652: /**
653: * Called by columns to ensure that all columns are properly indexed
654: * before returning that information to the user.
655: */
656: void indexColumns() {
657: getColumns();
658: }
659:
660: /**
661: * Called by foreign keys to ensure that all fks are properly indexed
662: * before returning that information to the user.
663: */
664: void indexForeignKeys() {
665: getForeignKeys();
666: }
667:
668: /**
669: * Called by columns when their auto increment status changes.
670: */
671: void changeAutoAssigned(Column col) {
672: _autoAssign = null;
673: }
674:
675: /**
676: * Called by columns when their relation id status changes.
677: */
678: void changeRelationId(Column col) {
679: _rels = null;
680: }
681:
682: public int compareTo(Object other) {
683: String name = getFullName();
684: String otherName = ((Table) other).getFullName();
685: if (name == null && otherName == null)
686: return 0;
687: if (name == null)
688: return 1;
689: if (otherName == null)
690: return -1;
691: return name.compareTo(otherName);
692: }
693:
694: public String toString() {
695: return getFullName();
696: }
697:
698: public boolean hasComment() {
699: return _comment != null && !_comment.equalsIgnoreCase(_name);
700: }
701:
702: public String getComment() {
703: return _comment;
704: }
705:
706: public void setComment(String comment) {
707: _comment = comment;
708: }
709: }
|