001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency;
010:
011: import com.completex.objective.util.PropertyMap;
012: import com.completex.objective.components.OdalRuntimeException;
013:
014: import java.math.BigDecimal;
015: import java.util.*;
016:
017: /**
018: * @author Gennady Krizhevsky
019: */
020: public class MetaTable implements ModelConsts, Cloneable,
021: DescriptorMappable {
022:
023: public static final MetaColumn[] NULL_META_COLUMNS = new MetaColumn[0];
024: public static final String TAG_NATURAL_KEY = "naturalKey";
025:
026: private static final JavaToMetaType DEFAULT_JAVA_TO_META_TYPE = new JavaToMetaTypeImpl();
027:
028: private String tableName;
029: private String tableAlias;
030: private List columnsList; // List<MetaColumn> of columns
031: private Map columns; // Map<String, MetaColumn> of (columnName, column)
032: private List primaryKey; // List<Integer> of pk indexes
033: private List optLockKey; // List<Integer> of optimistic lock keys indexes
034: private ForeignKeys foreignKeys; // ForeignKeys<String, ForeignKeyEntries<ForeignKeyEntry>> of fk
035: private ForeignKeys internalForeignKeys; // ForeignKeys<String, ForeignKeyEntries<ForeignKeyEntry>> of fk
036: private int[] primaryKeyIndeces;
037: private MetaNaturalKey naturalKey = MetaNaturalKey.NULL_NATURAL_KEY;
038:
039: protected boolean exclude;
040: protected boolean transformed;
041: private boolean staticFinal;
042:
043: public MetaTable(String tableName, String tableAlias) {
044: this (tableName, tableAlias, 0, 0);
045: }
046:
047: public MetaTable(String tableName, String tableAlias, int size,
048: int keySize) {
049: this .tableName = tableName;
050: this .tableAlias = tableAlias;
051:
052: // Some optimization:
053: if (size > 0) {
054: columns = new LinkedHashMap(size);
055: columnsList = new ArrayList(size);
056: } else {
057: columns = new LinkedHashMap();
058: columnsList = new ArrayList();
059: }
060: if (keySize > 0 && keySize <= size) {
061: primaryKey = new ArrayList(keySize);
062: } else {
063: primaryKey = new ArrayList();
064: }
065: if (keySize > 0 && keySize <= size) {
066: foreignKeys = new ForeignKeys(keySize);
067: internalForeignKeys = new ForeignKeys(keySize);
068: } else {
069: foreignKeys = new ForeignKeys();
070: internalForeignKeys = new ForeignKeys();
071: }
072: optLockKey = new ArrayList(0);
073: }
074:
075: public MetaTable(String tableName) {
076: this (tableName, null);
077: }
078:
079: public boolean isStaticFinal() {
080: return staticFinal;
081: }
082:
083: public void setStaticFinal(boolean staticFinal) {
084: this .staticFinal = staticFinal;
085: }
086:
087: /**
088: *
089: * @return number of columns
090: */
091: public int size() {
092: return columns.size();
093: }
094:
095: /**
096: *
097: * @return primary key size
098: */
099: public int keySize() {
100: return primaryKey.size();
101: }
102:
103: /**
104: *
105: * @return List<Integer> of optimistic lock columns indeces
106: */
107: public List getOptLockKey() {
108: return optLockKey;
109: }
110:
111: /**
112: *
113: * @return size of List<Integer> of optimistic lock columns indeces
114: */
115: public int getOptLockKeySize() {
116: return optLockKey.size();
117: }
118:
119: /**
120: *
121: * @return ForeignKeys
122: */
123: public ForeignKeys getForeignKeys() {
124: return foreignKeys;
125: }
126:
127: /**
128: *
129: * @return ForeignKeys size
130: */
131: public int foreignKeySize() {
132: return foreignKeys.size();
133: }
134:
135: /**
136: *
137: * @return tableName
138: */
139: public String getTableName() {
140: return tableName;
141: }
142:
143: /**
144: *
145: * @param tableName
146: */
147: public void setTableName(String tableName) {
148: this .tableName = tableName;
149: }
150:
151: /**
152: *
153: * @param tableAlias
154: */
155: public void setTableAlias(String tableAlias) {
156: this .tableAlias = tableAlias;
157: }
158:
159: /**
160: *
161: * @return tableAlias
162: */
163: public String getTableAlias() {
164: return tableAlias == null ? tableName : tableAlias;
165: }
166:
167: /**
168: *
169: * @return List<Integer> of primary key column indeces
170: */
171: public List getPrimaryKey() {
172: return primaryKey;
173: }
174:
175: /**
176: *
177: * @return true if this table is excluded from code generation
178: */
179: public boolean isExclude() {
180: return exclude;
181: }
182:
183: /**
184: *
185: * @param exclude true if this table is excluded from code generation
186: */
187: public void setExclude(boolean exclude) {
188: this .exclude = exclude;
189: }
190:
191: /**
192: *
193: * @return true if table has been transformed
194: */
195: public boolean isTransformed() {
196: return transformed;
197: }
198:
199: /**
200: *
201: * @param transformed true if table has been transformed
202: */
203: public void setTransformed(boolean transformed) {
204: this .transformed = transformed;
205: }
206:
207: public void addToOptLockKey(Object column) {
208: if (!optLockKey.contains(column)) {
209: optLockKey.add(column);
210: }
211: }
212:
213: /**
214: * Adds index to primary key List
215: *
216: * @param columnIndex
217: */
218: public void addToPrimaryKey(Integer columnIndex) {
219: if (!primaryKey.contains(columnIndex)) {
220: primaryKey.add(columnIndex);
221: }
222: }
223:
224: /**
225: *
226: * @param fkTable
227: * @param foreignKeyEntry
228: */
229: public void addToForeignKey(String fkTable,
230: ForeignKeyEntry foreignKeyEntry) {
231: addToForeignKey(fkTable, foreignKeyEntry, true);
232: }
233:
234: /**
235: * Adds foreignKeyEntry to foreign keys
236: *
237: * @param fkTableName
238: * @param foreignKeyEntry
239: * @param internal
240: */
241: public void addToForeignKey(String fkTableName,
242: ForeignKeyEntry foreignKeyEntry, boolean internal) {
243: final ForeignKeyEntries foreignKeyList = lazyForeignKeyList(
244: fkTableName, internal);
245: int indexOfExistingForeignEntry = foreignKeyList
246: .indexOf(foreignKeyEntry);
247: ForeignKeyEntry existingForeignKeyEntry = (ForeignKeyEntry) (indexOfExistingForeignEntry >= 0 ? foreignKeyList
248: .get(indexOfExistingForeignEntry)
249: : null);
250: if (existingForeignKeyEntry == null) {
251: //
252: // If entry does not exist - insert it in any case:
253: //
254: foreignKeyEntry.setInternal(internal);
255: foreignKeyList.add(foreignKeyEntry);
256: } else if (existingForeignKeyEntry.isInternal()
257: && foreignKeyEntry.isExternal()) {
258: //
259: // If the entry exists but it is internal & newer one in external - overwrite:
260: //
261: foreignKeyEntry.setInternal(false);
262: foreignKeyList.remove(indexOfExistingForeignEntry);
263: foreignKeyList.add(foreignKeyEntry);
264: } else {
265: //
266: // Unexpected case:
267: //
268: throw new RuntimeException(
269: "Unexpected case: existingForeignKeyEntry != null "
270: + "&& doubled internal foreignKeyEntry "
271: + foreignKeyEntry);
272: }
273: }
274:
275: private ForeignKeyEntries lazyForeignKeyList(String fkTable,
276: boolean internal) {
277: ForeignKeyEntries foreignKeyList = (ForeignKeyEntries) foreignKeys
278: .get(fkTable);
279: if (foreignKeyList == null) {
280: foreignKeyList = new ForeignKeyEntries(internal);
281: foreignKeys.put(fkTable, foreignKeyList);
282: if (internal) {
283: internalForeignKeys.put(fkTable, foreignKeyList);
284: }
285: }
286: foreignKeyList.setInternal(internal);
287: return foreignKeyList;
288: }
289:
290: /**
291: * Gets column index by primary key index
292: *
293: * @param primaryKeyIndex index of value in primaryKey object
294: * @return column index by primary key index
295: */
296: public int getColumnIndexByPrimaryKeyIndex(int primaryKeyIndex) {
297: if (primaryKeyIndex < 0 || primaryKeyIndex > keySize()) {
298: throw new IndexOutOfBoundsException(
299: "Cannot get ColumnIndex by primaryKeyIndex "
300: + primaryKeyIndex);
301: }
302: return ((Integer) primaryKey.get(primaryKeyIndex)).intValue();
303: }
304:
305: /**
306: *
307: * @param columnName
308: * @return MetaColumn
309: */
310: public MetaColumn getColumn(String columnName) {
311: return (MetaColumn) columns.get(columnName);
312: }
313:
314: /**
315: *
316: * @param columnName
317: * @return <tt>true</tt> if this table contains column with columnName
318: */
319: public boolean containsColumn(String columnName) {
320: return columns.containsKey(columnName);
321: }
322:
323: /**
324: *
325: * @param columnIndex
326: * @return MetaColumn
327: */
328: public MetaColumn getColumn(int columnIndex) {
329: return (MetaColumn) columnsList.get(columnIndex);
330: }
331:
332: /**
333: * Sets column at certasin index
334: *
335: * @param columnIndex
336: * @param column
337: * @throws IndexOutOfBoundsException if the index is out of range
338: * (index < 0 || index >= size()).
339: */
340: public void setColumn(int columnIndex, MetaColumn column) {
341: columnsList.set(columnIndex, column);
342: }
343:
344: /**
345: * Adds new MetaColumn to the table if MetaColumn with the name columnName did not exist.
346: * Returns new/existing MetaColumn with given column name
347: *
348: * @param columnName
349: * @return MetaColumn by columnName
350: */
351: public MetaColumn addColumn(String columnName) {
352: if (columnName == null) {
353: throw new IllegalArgumentException("columnName == null");
354: }
355: MetaColumn column = getColumn(columnName);
356: if (column == null) {
357: column = new MetaColumn(columnName, this );
358: addColumn0(columnName, column);
359: }
360: return column;
361: }
362:
363: /**
364: * Adds new MetaColumn to the table
365: * Returns MetaColumn passed as parameter
366: *
367: * @param column
368: * @return MetaColumn
369: */
370: public MetaColumn addColumn(MetaColumn column) {
371: if (column == null) {
372: throw new IllegalArgumentException("column == null");
373: }
374: addColumn0(column.getColumnName(), column);
375: return column;
376: }
377:
378: private void addColumn0(String columnName, MetaColumn column) {
379: if (columns.containsKey(columnName)) {
380: throw new IllegalArgumentException("columnName "
381: + columnName + " already exist in columns map");
382: }
383: columns.put(columnName, column);
384: columnsList.add(column);
385: column.setColumnIndex(columnsList.size() - 1);
386: }
387:
388: /**
389: *
390: * @return primary key indeces array
391: */
392: public int[] toPrimaryKeyIndeces() {
393: if (primaryKeyIndeces == null && primaryKey != null
394: && !primaryKey.isEmpty()) {
395: synchronized (this ) {
396: if (primaryKeyIndeces == null) {
397: primaryKeyIndeces = new int[primaryKey.size()];
398: for (int i = 0; i < primaryKeyIndeces.length; i++) {
399: primaryKeyIndeces[i] = ((Integer) primaryKey
400: .get(i)).intValue();
401: }
402: }
403: }
404: }
405: return primaryKeyIndeces;
406: }
407:
408: private void fromDescriptorToForeignKey(Map tableMap,
409: boolean internal) {
410: Map foreignKeyTemp = (Map) tableMap.get(ModelConsts.FK_TAG);
411: if (foreignKeyTemp != null) {
412: for (Iterator it = foreignKeyTemp.keySet().iterator(); it
413: .hasNext();) {
414: String tableName = (String) it.next();
415: //
416: // ForeignKeyEntries always gets overwritten:
417: //
418: final List foreignKeyListTemp = (List) foreignKeyTemp
419: .get(tableName);
420: if (foreignKeyListTemp != null) {
421: if (foreignKeyListTemp.size() > 0) {
422: for (int i = 0; i < foreignKeyListTemp.size(); i++) {
423: final Map foreignKeyMap = (Map) foreignKeyListTemp
424: .get(i);
425: final ForeignKeyEntry foreignKeyEntry = new ForeignKeyEntry();
426: foreignKeyEntry
427: .setForeignColumn((String) foreignKeyMap
428: .get(ModelConsts.FK_FOREIGN_COLUMN));
429: foreignKeyEntry
430: .setLocalColumn((String) foreignKeyMap
431: .get(ModelConsts.FK_LOCAL_COLUMN));
432: addToForeignKey(tableName, foreignKeyEntry,
433: internal);
434: }
435: } else { // size == 0 :
436: ForeignKeyEntries foreignKeyList = (ForeignKeyEntries) foreignKeys
437: .get(tableName);
438: boolean external = !internal;
439: boolean existingInternal = foreignKeyList != null;
440: if (external && existingInternal) {
441: foreignKeyList = new ForeignKeyEntries(
442: internal);
443: foreignKeys.put(tableName, foreignKeyList);
444: }
445: }
446: }
447: }
448: // System.out.println("foreignKeys from internal map = " + foreignKeys);
449: }
450: }
451:
452: /**
453: * @see DescriptorMappable
454: */
455: public Map toInternalMap() {
456: final Map tableMap = new LinkedHashMap();
457: tableMap.put(TABLE_NAME_TAG, tableName);
458: // Add columns:
459: Map columnsList = new LinkedHashMap();
460: tableMap.put(ModelConsts.COLUMNS_TAG, columnsList);
461: for (Iterator it = columns.keySet().iterator(); it.hasNext();) {
462: String columnName = (String) it.next();
463: MetaColumn column = getColumn(columnName);
464: columnsList.put(column.getColumnName(), column
465: .toInternalMap());
466: }
467:
468: // Put PK:
469: tableMap.put(ModelConsts.PK_TAG, primaryKey);
470: // Put FK:
471: tableMap.put(ModelConsts.FK_TAG, internalForeignKeys);
472: return tableMap;
473: }
474:
475: /**
476: * @see DescriptorMappable
477: */
478: public void fromInternalMap(Map tableMap) {
479: tableName = (String) tableMap.get(TABLE_NAME_TAG);
480: Map columnsMap = (Map) tableMap.get(COLUMNS_TAG);
481: for (Iterator it = columnsMap.keySet().iterator(); it.hasNext();) {
482: Object key = it.next();
483: Map columnMap = (Map) columnsMap.get(key);
484: String columnName = (String) columnMap.get(COLUMN_NAME_TAG);
485: MetaColumn column = getColumn(columnName);
486: if (column == null) {
487: column = new MetaColumn(this );
488: }
489: column.fromInternalMap(columnMap);
490: addColumn0(columnName, column);
491: }
492:
493: // Load PK (we assume that pk contains only names):
494: List primaryKeyTemp = (List) tableMap.get(ModelConsts.PK_TAG);
495: for (int i = 0; i < primaryKeyTemp.size(); i++) {
496: BigDecimal value = (BigDecimal) primaryKeyTemp.get(i);
497: addToPrimaryKey(new Integer(value.intValue()));
498: }
499:
500: //
501: // Load FK (we assume that the tables are linked by column names):
502: //
503: fromDescriptorToForeignKey(tableMap, true);
504: }
505:
506: /**
507: * @see DescriptorMappable
508: */
509: public Map toExternalMap() {
510: final Map tableMap = new LinkedHashMap();
511: tableMap.put(TABLE_ALIAS_TAG, tableAlias == null ? tableName
512: : tableAlias);
513: tableMap.put(TABLE_NAME_TAG, tableName);
514:
515: // Add columns:
516: Map columnsMap = new LinkedHashMap();
517: tableMap.put(ModelConsts.COLUMNS_TAG, columnsMap);
518: tableMap.put(EXCLUDE_TAG, Boolean.valueOf(exclude));
519: tableMap.put(TRANSFORMED_TAG, Boolean.valueOf(transformed));
520:
521: for (int i = 0; i < columnsList.size(); i++) {
522: MetaColumn column = getColumn(i);
523: // Columns is map by ColumnNames:
524: columnsMap.put(column.getColumnName(), column
525: .toExternalMap());
526: }
527:
528: //
529: // Put FK:
530: //
531: tableMap.put(ModelConsts.FK_TAG, foreignKeys
532: .getExternalForeignKeys());
533: //
534: // Natural key:
535: //
536: Map naturalKeyMap = naturalKey == null ? null : naturalKey
537: .toMap();
538: if (naturalKey == null) {
539: int[] pkIndices = toPrimaryKeyIndeces();
540: if (pkIndices != null) {
541: naturalKey = new MetaNaturalKey();
542: for (int i = 0; i < pkIndices.length; i++) {
543: int pkIndex = pkIndices[i];
544: MetaColumn column = getColumn(pkIndex);
545: naturalKey.addColumnName(column.getColumnName());
546: }
547: naturalKeyMap = naturalKey.toMap();
548: }
549: } else {
550: naturalKeyMap = naturalKey.toMap();
551: }
552:
553: tableMap.put(TAG_NATURAL_KEY, naturalKeyMap);
554: return tableMap;
555: }
556:
557: /**
558: * @see DescriptorMappable
559: */
560: public void fromExternalMap(Map tableMap) {
561: PropertyMap propertyMap = PropertyMap.toPropertyMap(tableMap,
562: true);
563: tableAlias = (String) propertyMap
564: .get(ModelConsts.TABLE_ALIAS_TAG);
565: tableName = tableName != null ? tableName
566: : (String) propertyMap.get(TABLE_NAME_TAG);
567: exclude = extractBoolean(propertyMap, EXCLUDE_TAG);
568: transformed = extractBoolean(propertyMap, TRANSFORMED_TAG);
569: Map columnsMap = (Map) propertyMap.get(COLUMNS_TAG);
570: for (Iterator it = columnsMap.keySet().iterator(); it.hasNext();) {
571: Object key = it.next();
572: Map columnMap = (Map) columnsMap.get(key);
573: String columnName = (String) columnMap.get(COLUMN_NAME_TAG);
574: MetaColumn column = getColumn(columnName);
575: if (column != null) {
576: column.fromExternalMap(columnMap);
577: if (column.isOptimisticLock()) {
578: optLockKey
579: .add(new Integer(column.getColumnIndex()));
580: }
581: }
582: }
583:
584: //
585: // Load FK (we assume that the tables are linked by column names):
586: //
587: fromDescriptorToForeignKey(propertyMap, false);
588: //
589: // Natural key:
590: //
591: Map naturalKeyMap = propertyMap.getPropertyMap(TAG_NATURAL_KEY);
592: naturalKey = naturalKeyMap == null ? null : new MetaNaturalKey(
593: naturalKeyMap);
594: }
595:
596: static int extractInt(Map map, String key) {
597: BigDecimal value = (BigDecimal) map.get(key);
598: return value == null ? 0 : value.intValue();
599: }
600:
601: static boolean extractBoolean(Map map, String key) {
602: Object object = map.get(key);
603: if (object != null) {
604: if (object instanceof Boolean) {
605: return ((Boolean) object).booleanValue();
606: } else if (object instanceof String) {
607: String value = (String) object;
608: return "true".equalsIgnoreCase(value);
609: } else {
610: String clazzName = object.getClass().getName();
611: throw new OdalRuntimeException(
612: "extractBoolean: Expected type : String, gotten: "
613: + clazzName);
614: }
615: }
616: return false;
617: }
618:
619: public Object clone() throws CloneNotSupportedException {
620: return (MetaTable) super .clone();
621: }
622:
623: public MetaTable cloneSafe() {
624: try {
625: return (MetaTable) clone();
626: } catch (CloneNotSupportedException e) {
627: throw new OdalRuntimePersistencyException(
628: "Cannot clone MetaTable", e);
629: }
630: }
631:
632: public Object cloneDeep() throws CloneNotSupportedException {
633: MetaTable table = (MetaTable) super .clone();
634:
635: ArrayList tempColumnList = new ArrayList(table.columnsList);
636: table.columnsList = new ArrayList(columnsList.size());
637: table.columns = new LinkedHashMap(columnsList.size());
638: for (int i = 0; i < table.size(); i++) {
639: MetaColumn column = (MetaColumn) tempColumnList.get(i);
640: column = column.cloneSafe();
641: table.addColumn(column);
642: }
643: return table;
644: }
645:
646: public MetaTable cloneDeepSafe() {
647: try {
648: return (MetaTable) cloneDeep();
649: } catch (CloneNotSupportedException e) {
650: throw new OdalRuntimePersistencyException(
651: "Cannot clone MetaTable", e);
652: }
653: }
654:
655: /**
656: *
657: * @return default JavaToMetaType object
658: */
659: public static JavaToMetaType getDefaultJavaToMetaType() {
660: return DEFAULT_JAVA_TO_META_TYPE;
661: }
662:
663: public String toString() {
664: StringBuffer buffer = new StringBuffer();
665: buffer.append("\n{\n tableName = ").append(tableName).append(
666: "\n");
667: buffer.append(" tableAlias = ").append(tableAlias)
668: .append("\n");
669: String pkString = primaryKey + "";
670: String fkString = foreignKeys + "";
671: buffer.append(" primaryKey = ").append(pkString).append("\n");
672: buffer.append(" foreignKeys = ").append(fkString).append("\n");
673: buffer.append(" columns = ").append("\n [\n");
674: for (Iterator it = columns.keySet().iterator(); it.hasNext();) {
675: String columnName = (String) it.next();
676: MetaColumn column = (MetaColumn) columns.get(columnName);
677: buffer.append(" ").append(columnName).append("=")
678: .append(column).append("\n");
679: }
680: buffer.append(" ]");
681: return buffer.toString();
682: }
683:
684: /**
685: *
686: * @param tableName
687: * @return MetaColumn [] that are in foreign keys
688: */
689: public MetaColumn[] getColumnsInFk(String tableName) {
690: if (foreignKeySize() == 0) {
691: return NULL_META_COLUMNS;
692: }
693:
694: ArrayList list = new ArrayList(foreignKeySize());
695: ForeignKeyEntries foreignKeyList = (ForeignKeyEntries) foreignKeys
696: .get(tableName);
697: if (foreignKeyList != null) {
698: for (int i = 0; i < foreignKeyList.size(); i++) {
699: ForeignKeyEntry foreignKey = (ForeignKeyEntry) foreignKeyList
700: .get(i);
701: MetaColumn column = getColumn(foreignKey
702: .getLocalColumn());
703: if (column != null) {
704: list.add(column);
705: }
706: }
707: }
708:
709: if (list.size() > 0) {
710: return (MetaColumn[]) list.toArray(new MetaColumn[list
711: .size()]);
712: } else {
713: return NULL_META_COLUMNS;
714: }
715: }
716:
717: /**
718: * @see MetaNaturalKey
719: * @return MetaNaturalKey
720: */
721: public MetaNaturalKey getNaturalKey() {
722: return naturalKey;
723: }
724:
725: /**
726: * @see MetaNaturalKey
727: * @param naturalKey
728: */
729: public void setNaturalKey(MetaNaturalKey naturalKey) {
730: this.naturalKey = naturalKey;
731: }
732:
733: }
|