001: package org.apache.ojb.broker.metadata;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.io.Serializable;
019: import java.util.*;
020:
021: import org.apache.commons.lang.SystemUtils;
022: import org.apache.commons.lang.builder.ToStringBuilder;
023: import org.apache.commons.lang.builder.ToStringStyle;
024: import org.apache.ojb.broker.OJBRuntimeException;
025: import org.apache.ojb.broker.PersistenceBrokerException;
026: import org.apache.ojb.broker.locking.IsolationLevels;
027: import org.apache.ojb.broker.util.ClassHelper;
028: import org.apache.ojb.broker.util.logging.LoggerFactory;
029: import org.apache.ojb.broker.util.logging.Logger;
030:
031: /**
032: * The repository containing all object mapping and manipulation information of
033: * all used persistent objects.
034: * <br>
035: * Note: Be careful when use references of this class or caching instances of this class,
036: * because instances could become invalid (see {@link MetadataManager}).
037: *
038: * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
039: * @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
040: * @version $Id: DescriptorRepository.java,v 1.50.2.9 2005/12/21 22:26:11 tomdz Exp $
041: */
042: public final class DescriptorRepository extends DescriptorBase
043: implements Serializable, XmlCapable, IsolationLevels {
044: static final long serialVersionUID = -1556339982311359524L;
045: private Logger log = LoggerFactory
046: .getLogger(DescriptorRepository.class);
047:
048: /**
049: * The version identifier of the Repository.
050: * Used to validate repository.xml against the dtd.
051: */
052: private static final String VERSION = "1.0";
053: /**
054: * the default isolation level used for this repository
055: */
056: private int defaultIsolationLevel = IsolationLevels.IL_DEFAULT;
057: /**
058: * This table holds all known Mapping descriptions.
059: * Key values are the respective Class objects
060: */
061: private final HashMap descriptorTable;
062: /**
063: * We need a lot the extent, to which a class belongs
064: * (@see DescriptorRepository#getExtentClass). To speed up the costy
065: * evaluation, we use this tiny hash map.
066: */
067: private Map extentTable;
068:
069: private Map super ClassMultipleJoinedTablesMap;
070:
071: private transient Map m_multiMappedTableMap;
072: private transient Map m_topLevelClassTable;
073: private transient Map m_firstConcreteClassMap;
074: private transient Map m_allConcreteSubClass;
075:
076: /**
077: * Constructor declaration
078: */
079: public DescriptorRepository() throws PersistenceBrokerException {
080: descriptorTable = new HashMap();
081: extentTable = new HashMap();
082: super ClassMultipleJoinedTablesMap = new HashMap();
083: }
084:
085: public static String getVersion() {
086: return VERSION;
087: }
088:
089: /**
090: * Add a pair of extent/classdescriptor to the extentTable to gain speed
091: * while retrieval of extents.
092: * @param classname the name of the extent itself
093: * @param cld the class descriptor, where it belongs to
094: */
095: void addExtent(String classname, ClassDescriptor cld) {
096: synchronized (extentTable) {
097: extentTable.put(classname, cld);
098: }
099: }
100:
101: /**
102: * Remove a pair of extent/classdescriptor from the extentTable.
103: * @param classname the name of the extent itself
104: */
105: void removeExtent(String classname) {
106: synchronized (extentTable) {
107: // returns the super class for given extent class name
108: ClassDescriptor cld = (ClassDescriptor) extentTable
109: .remove(classname);
110: if (cld != null && m_topLevelClassTable != null) {
111: Class extClass = null;
112: try {
113: extClass = ClassHelper.getClass(classname);
114: } catch (ClassNotFoundException e) {
115: // Should not happen
116: throw new MetadataException(
117: "Can't instantiate class object for needed extent remove",
118: e);
119: }
120: // remove extent from super class descriptor
121: cld.removeExtentClass(classname);
122: m_topLevelClassTable.remove(extClass);
123: // clear map with first concrete classes, because the removed
124: // extent could be such a first found concrete class
125: m_firstConcreteClassMap = null;
126: }
127: }
128: }
129:
130: /**
131: * Returns the top level (extent) class to which the given class belongs.
132: * This may be a (abstract) base-class, an interface or the given class
133: * itself if given class is not defined as an extent in other class
134: * descriptors.
135: *
136: * @throws ClassNotPersistenceCapableException if clazz is not persistence capable,
137: * i.e. if clazz is not defined in the DescriptorRepository.
138: */
139: public Class getTopLevelClass(Class clazz)
140: throws ClassNotPersistenceCapableException {
141: if (m_topLevelClassTable == null) {
142: m_topLevelClassTable = new HashMap();
143: }
144: // try to find an extent that contains clazz
145: Class retval = (Class) m_topLevelClassTable.get(clazz);
146: if (retval == null) {
147: synchronized (extentTable) {
148: ClassDescriptor cld = (ClassDescriptor) extentTable
149: .get(clazz.getName());
150: if (cld == null) {
151: // walk the super-references
152: cld = getDescriptorFor(clazz)
153: .getSuperClassDescriptor();
154: }
155:
156: if (cld != null) {
157: // fix by Mark Rowell
158: // Changed to call getExtentClass recursively
159: retval = getTopLevelClass(cld.getClassOfObject());
160: // if such an extent could not be found just return clazz itself.
161: if (retval == null) {
162: retval = clazz;
163: }
164: } else {
165: // check if class is persistence capable
166: // +
167: // Adam Jenkins: use the class that is associated
168: // with the descriptor instead of the actual class
169: ClassDescriptor temp = getDescriptorFor(clazz);
170: retval = temp.getClassOfObject();
171: }
172: m_topLevelClassTable.put(clazz, retval);
173: }
174: }
175: return retval;
176: }
177:
178: /**
179: * @return all field descriptors for a class that belongs to a set of classes mapped
180: * to the same table, otherwise the select queries produced won't contain the necessary
181: * information to materialize extents mapped to the same class.
182: */
183: public synchronized FieldDescriptor[] getFieldDescriptorsForMultiMappedTable(
184: ClassDescriptor targetCld) {
185: if (m_multiMappedTableMap == null) {
186: m_multiMappedTableMap = new HashMap();
187: }
188:
189: FieldDescriptor[] retval = (FieldDescriptor[]) m_multiMappedTableMap
190: .get(targetCld.getClassNameOfObject());
191: if (retval == null) {
192: retval = getAllMappedColumns(getClassesMappedToSameTable(targetCld));
193: m_multiMappedTableMap.put(targetCld.getClassNameOfObject(),
194: retval);
195: }
196: return retval;
197: }
198:
199: private FieldDescriptor[] getAllMappedColumns(List classDescriptors) {
200: /* mkalen: Use an ordered implementation not to loose individual field ordering.
201: This is especially important for eg Oracle9i platform and LONGVARBINARY columns,
202: see http://download-west.oracle.com/docs/cd/B10501_01/java.920/a96654/basic.htm#1021777
203: "If you do not use the SELECT-list order to access data,
204: then you can lose the stream data."
205: */
206: List allFieldDescriptors = new Vector();
207:
208: Set visitedColumns = new HashSet();
209: Iterator it = classDescriptors.iterator();
210: ClassDescriptor temp = null;
211: FieldDescriptor[] fields;
212: while (it.hasNext()) {
213: temp = (ClassDescriptor) it.next();
214: fields = temp.getFieldDescriptions();
215: if (fields != null) {
216: for (int i = 0; i < fields.length; i++) {
217: /*
218: MBAIRD
219: hashmap will only allow one entry per unique key,
220: so no need to check contains(fields[i].getColumnName()).
221: arminw:
222: use contains to avoid overriding of target class fields by the same
223: field-descriptor of other classes mapped to the same DB table, because
224: the other class can use e.g. different FieldConversion.
225: In #getClassesMappedToSameTable(...) we make sure that target
226: class has first position in list.
227: */
228: final String columnName = fields[i].getColumnName();
229: if (!visitedColumns.contains(columnName)) {
230: visitedColumns.add(columnName);
231: allFieldDescriptors.add(fields[i]);
232: }
233: }
234: }
235: }
236: FieldDescriptor[] retval = new FieldDescriptor[allFieldDescriptors
237: .size()];
238: allFieldDescriptors.toArray(retval);
239: return retval;
240: }
241:
242: private List getClassesMappedToSameTable(ClassDescriptor targetCld) {
243: /*
244: try to find an extent that contains clazz
245: clone map to avoid synchronization problems, because another thread
246: can do a put(..) operation on descriptor table
247: */
248: Iterator iter = ((HashMap) descriptorTable.clone()).values()
249: .iterator();
250: List retval = new ArrayList();
251: // make sure that target class is at first position
252: retval.add(targetCld);
253: while (iter.hasNext()) {
254: ClassDescriptor cld = (ClassDescriptor) iter.next();
255: if (cld.getFullTableName() != null) {
256: if (cld.getFullTableName().equals(
257: targetCld.getFullTableName())
258: && !targetCld.getClassOfObject().equals(
259: cld.getClassOfObject())) {
260: retval.add(cld);
261: }
262: }
263: }
264: return retval;
265: }
266:
267: public Map getDescriptorTable() {
268: return descriptorTable;
269: }
270:
271: /**
272: * Return the first found concrete class {@link ClassDescriptor}.
273: * This means a class which is not an interface or an abstract class.
274: * If given class descriptor is a concrete class, given class descriptor
275: * was returned. If no concrete class can be found <code>null</code> will be
276: * returned.
277: */
278: public ClassDescriptor findFirstConcreteClass(ClassDescriptor cld) {
279: if (m_firstConcreteClassMap == null) {
280: m_firstConcreteClassMap = new HashMap();
281: }
282: ClassDescriptor result = (ClassDescriptor) m_firstConcreteClassMap
283: .get(cld.getClassNameOfObject());
284: if (result == null) {
285: if (cld.isInterface() || cld.isAbstract()) {
286: if (cld.isExtent()) {
287: List extents = cld.getExtentClasses();
288: for (int i = 0; i < extents.size(); i++) {
289: Class ext = (Class) extents.get(i);
290: result = findFirstConcreteClass(getDescriptorFor(ext));
291: if (result != null)
292: break;
293: }
294: } else {
295: LoggerFactory
296: .getDefaultLogger()
297: .error(
298: "["
299: + this .getClass().getName()
300: + "] Found interface/abstract class"
301: + " in metadata declarations without concrete class: "
302: + cld
303: .getClassNameOfObject());
304: }
305: m_firstConcreteClassMap.put(cld.getClassNameOfObject(),
306: result);
307: } else {
308: result = cld;
309: }
310: }
311: return result;
312: }
313:
314: /**
315: *
316: * Utility method to discover all concrete subclasses of a given super class. <br>
317: * This method was introduced in order to get Extent Aware Iterators.
318: *
319: * @return a Collection of ClassDescriptor objects
320: */
321: public Collection getAllConcreteSubclassDescriptors(
322: ClassDescriptor aCld) {
323: if (m_allConcreteSubClass == null) {
324: m_allConcreteSubClass = new HashMap();
325: }
326: Collection concreteSubclassClds = (Collection) m_allConcreteSubClass
327: .get(aCld.getClassOfObject());
328:
329: if (concreteSubclassClds == null) {
330: // BRJ: As long as we do not have an ordered Set
331: // duplicates have to be prevented manually.
332: // a HashSet should not be used because the order is unpredictable
333: concreteSubclassClds = new ArrayList();
334: Iterator iter = aCld.getExtentClasses().iterator();
335:
336: while (iter.hasNext()) {
337: Class extentClass = (Class) iter.next();
338: ClassDescriptor extCld = getDescriptorFor(extentClass);
339: if (aCld.equals(extCld)) {
340: // prevent infinite recursion caused by cyclic references
341: continue;
342: }
343: if (!extCld.isInterface() && !extCld.isAbstract()) {
344: if (!concreteSubclassClds.contains(extCld)) {
345: concreteSubclassClds.add(extCld);
346: }
347: }
348:
349: // recurse
350: Iterator subIter = getAllConcreteSubclassDescriptors(
351: extCld).iterator();
352: while (subIter.hasNext()) {
353: ClassDescriptor subCld = (ClassDescriptor) subIter
354: .next();
355: if (!concreteSubclassClds.contains(subCld)) {
356: concreteSubclassClds.add(subCld);
357: }
358: }
359: }
360: m_allConcreteSubClass.put(aCld.getClassOfObject(),
361: concreteSubclassClds);
362: }
363:
364: return concreteSubclassClds;
365: }
366:
367: /**
368: * Checks if repository contains given class.
369: */
370: public boolean hasDescriptorFor(Class c) {
371: return descriptorTable.containsKey(c.getName());
372: }
373:
374: /**
375: * lookup a ClassDescriptor in the internal Hashtable
376: * @param strClassName a fully qualified class name as it is returned by Class.getName().
377: */
378: public ClassDescriptor getDescriptorFor(String strClassName)
379: throws ClassNotPersistenceCapableException {
380: ClassDescriptor result = discoverDescriptor(strClassName);
381: if (result == null) {
382: throw new ClassNotPersistenceCapableException(strClassName
383: + " not found in OJB Repository");
384: } else {
385: return result;
386: }
387: }
388:
389: /**
390: * lookup a ClassDescriptor in the internal Hashtable
391: */
392: public ClassDescriptor getDescriptorFor(Class c)
393: throws ClassNotPersistenceCapableException {
394: return this .getDescriptorFor(c.getName());
395: }
396:
397: /**
398: * Convenience for {@link #put(Class c, ClassDescriptor cld)}
399: */
400: public void setClassDescriptor(ClassDescriptor cld) {
401: this .put(cld.getClassNameOfObject(), cld);
402: }
403:
404: /**
405: * Add a ClassDescriptor to the internal Hashtable<br>
406: * Set the Repository for ClassDescriptor
407: */
408: public void put(Class c, ClassDescriptor cld) {
409: this .put(c.getName(), cld);
410: }
411:
412: /**
413: * Add a ClassDescriptor to the internal Hashtable<br>
414: * Set the Repository for ClassDescriptor
415: */
416: public void put(String classname, ClassDescriptor cld) {
417: cld.setRepository(this ); // BRJ
418: synchronized (descriptorTable) {
419: descriptorTable.put(classname, cld);
420: List extentClasses = cld.getExtentClasses();
421: for (int i = 0; i < extentClasses.size(); ++i) {
422: addExtent(((Class) extentClasses.get(i)).getName(), cld);
423: }
424: changeDescriptorEvent();
425: }
426: }
427:
428: public void remove(String className) {
429: synchronized (descriptorTable) {
430: ClassDescriptor cld = (ClassDescriptor) descriptorTable
431: .remove(className);
432: if (cld != null) {
433: // class itself could no longer be a extent
434: Iterator it = descriptorTable.values().iterator();
435: while (it.hasNext()) {
436: ((ClassDescriptor) it.next())
437: .removeExtentClass(className);
438: }
439: removeExtent(className);
440: List extentClasses = cld.getExtentClasses();
441: for (int i = 0; i < extentClasses.size(); ++i) {
442: removeExtent(((Class) extentClasses.get(i))
443: .getName());
444: }
445: changeDescriptorEvent();
446: // deregister classes using mapping of classes to multiple joined tables
447: // the registration is done by the class-descriptor itself
448: deregisterSuperClassMultipleJoinedTables(cld);
449: }
450: }
451: }
452:
453: public void remove(Class clazz) {
454: remove(clazz.getName());
455: }
456:
457: private synchronized void changeDescriptorEvent() {
458: m_multiMappedTableMap = null;
459: m_topLevelClassTable = null;
460: m_firstConcreteClassMap = null;
461: m_allConcreteSubClass = null;
462: }
463:
464: /**
465: * Returns an iterator over all managed {@link ClassDescriptor}.
466: */
467: public Iterator iterator() {
468: /*
469: clone map to avoid synchronization problems
470: */
471: return ((HashMap) descriptorTable.clone()).values().iterator();
472: }
473:
474: /**
475: * Returns the defaultIsolationLevel.
476: * @return int
477: */
478: public int getDefaultIsolationLevel() {
479: return defaultIsolationLevel;
480: }
481:
482: /**
483: * Sets the defaultIsolationLevel.
484: * @param defaultIsolationLevel The defaultIsolationLevel to set
485: */
486: public void setDefaultIsolationLevel(int defaultIsolationLevel) {
487: this .defaultIsolationLevel = defaultIsolationLevel;
488: }
489:
490: /**
491: * returns a string representation
492: */
493: public String toString() {
494: Iterator it = descriptorTable.entrySet().iterator();
495: ToStringBuilder buf = new ToStringBuilder(this ,
496: ToStringStyle.MULTI_LINE_STYLE);
497: String className = "class name: ";
498: String tableName = "> table name: ";
499: while (it.hasNext()) {
500: Map.Entry me = (Map.Entry) it.next();
501: ClassDescriptor descriptor = (ClassDescriptor) me
502: .getValue();
503: buf.append(className + me.getKey() + " =", tableName
504: + descriptor.getFullTableName());
505: }
506: return buf.toString();
507: }
508:
509: /*
510: * @see XmlCapable#toXML()
511: */
512: public String toXML() {
513: String eol = SystemUtils.LINE_SEPARATOR;
514: StringBuffer buf = new StringBuffer();
515:
516: // write all ClassDescriptors
517: Iterator i = this .iterator();
518: while (i.hasNext()) {
519: buf.append(((XmlCapable) i.next()).toXML() + eol);
520: }
521: return buf.toString();
522: }
523:
524: /**
525: * returns IsolationLevel literal as matching
526: * to the corresponding id
527: * @return the IsolationLevel literal
528: */
529: protected String getIsolationLevelAsString() {
530: if (defaultIsolationLevel == IL_READ_UNCOMMITTED) {
531: return LITERAL_IL_READ_UNCOMMITTED;
532: } else if (defaultIsolationLevel == IL_READ_COMMITTED) {
533: return LITERAL_IL_READ_COMMITTED;
534: } else if (defaultIsolationLevel == IL_REPEATABLE_READ) {
535: return LITERAL_IL_REPEATABLE_READ;
536: } else if (defaultIsolationLevel == IL_SERIALIZABLE) {
537: return LITERAL_IL_SERIALIZABLE;
538: } else if (defaultIsolationLevel == IL_OPTIMISTIC) {
539: return LITERAL_IL_OPTIMISTIC;
540: }
541: return LITERAL_IL_READ_UNCOMMITTED;
542: }
543:
544: /**
545: * Starts by looking to see if the <code>className</code> is
546: * already mapped specifically to the descritpor repository.
547: * If the <code>className</code> is not specifically mapped we
548: * look at the <code>className</code>'s parent class for a mapping.
549: * We do this until the parent class is of the type
550: * <code>java.lang.Object</code>. If no mapping was found,
551: * <code>null</code> is returned. Mappings successfuly discovered
552: * through inheritence are added to the internal table of
553: * class descriptors to improve performance on subsequent requests
554: * for those classes.
555: *
556: * <br/>
557: * author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
558: * @param className name of class whose descriptor we need to find.
559: * @return ClassDescriptor for <code>className</code> or <code>null</code>
560: * if no ClassDescriptor could be located.
561: */
562: protected ClassDescriptor discoverDescriptor(String className) {
563: ClassDescriptor result = (ClassDescriptor) descriptorTable
564: .get(className);
565: if (result == null) {
566: Class clazz;
567: try {
568: clazz = ClassHelper.getClass(className, true);
569: } catch (ClassNotFoundException e) {
570: throw new OJBRuntimeException("Class, " + className
571: + ", could not be found.", e);
572: }
573: result = discoverDescriptor(clazz);
574: }
575: return result;
576: }
577:
578: /**
579: * Internal method for recursivly searching for a class descriptor that avoids
580: * class loading when we already have a class object.
581: *
582: * @param clazz The class whose descriptor we need to find
583: * @return ClassDescriptor for <code>clazz</code> or <code>null</code>
584: * if no ClassDescriptor could be located.
585: */
586: private ClassDescriptor discoverDescriptor(Class clazz) {
587: ClassDescriptor result = (ClassDescriptor) descriptorTable
588: .get(clazz.getName());
589:
590: if (result == null) {
591: Class super Class = clazz.getSuperclass();
592: // only recurse if the superClass is not java.lang.Object
593: if (super Class != null) {
594: result = discoverDescriptor(super Class);
595: }
596: if (result == null) {
597: // we're also checking the interfaces as there could be normal
598: // mappings for them in the repository (using factory-class,
599: // factory-method, and the property field accessor)
600: Class[] interfaces = clazz.getInterfaces();
601:
602: if ((interfaces != null) && (interfaces.length > 0)) {
603: for (int idx = 0; (idx < interfaces.length)
604: && (result == null); idx++) {
605: result = discoverDescriptor(interfaces[idx]);
606: }
607: }
608: }
609:
610: if (result != null) {
611: descriptorTable.put(clazz.getName(), result);
612: }
613: }
614: return result;
615: }
616:
617: /**
618: * Internal used! Register sub-classes of specified class when mapping class to
619: * multiple joined tables is used. Normally this method is called by the {@link ClassDescriptor}
620: * itself.
621: *
622: * @param cld The {@link ClassDescriptor} of the class to register.
623: */
624: protected void registerSuperClassMultipleJoinedTables(
625: ClassDescriptor cld) {
626: /*
627: arminw: Sadly, we can't map to sub class-descriptor, because it's not guaranteed
628: that they exist when this method is called. Thus we map the class instance instead
629: of the class-descriptor.
630: */
631: if (cld.getBaseClass() != null) {
632: try {
633: Class super Class = ClassHelper.getClass(cld
634: .getBaseClass());
635: Class currentClass = cld.getClassOfObject();
636: synchronized (descriptorTable) {
637: List subClasses = (List) super ClassMultipleJoinedTablesMap
638: .get(super Class);
639: if (subClasses == null) {
640: subClasses = new ArrayList();
641: super ClassMultipleJoinedTablesMap.put(
642: super Class, subClasses);
643: }
644: if (!subClasses.contains(currentClass)) {
645: if (log.isDebugEnabled()) {
646: log
647: .debug("(MultipleJoinedTables): Register sub-class '"
648: + currentClass
649: + "' for class '"
650: + super Class);
651: }
652: subClasses.add(currentClass);
653: }
654: }
655: } catch (Exception e) {
656: throw new MetadataException(
657: "Can't register super class '"
658: + cld.getBaseClass()
659: + "' for class-descriptor: " + cld, e);
660: }
661: }
662: }
663:
664: /**
665: * Internal used! Deregister sub-classes of specified class when mapping to multiple joined tables
666: * is used. Normally this method is called when {@link #remove(Class)} a class.
667: *
668: * @param cld The {@link ClassDescriptor} of the class to register.
669: */
670: protected void deregisterSuperClassMultipleJoinedTables(
671: ClassDescriptor cld) {
672: try {
673: Class currentClass = cld.getClassOfObject();
674: synchronized (descriptorTable) {
675: // first remove registered sub-classes for current class
676: List subClasses = (List) super ClassMultipleJoinedTablesMap
677: .remove(currentClass);
678: if (subClasses != null && log.isDebugEnabled()) {
679: log
680: .debug("(MultipleJoinedTables): Deregister class "
681: + currentClass
682: + " with sub classes " + subClasses);
683: }
684: if (cld.getBaseClass() != null) {
685: // then remove sub-class entry of current class for super-class
686: Class super Class = ClassHelper.getClass(cld
687: .getBaseClass());
688: subClasses = (List) super ClassMultipleJoinedTablesMap
689: .get(super Class);
690: if (subClasses != null) {
691: boolean removed = subClasses
692: .remove(currentClass);
693: if (removed && log.isDebugEnabled()) {
694: log
695: .debug("(MultipleJoinedTables): Remove sub-class entry '"
696: + currentClass
697: + "' in mapping for class '"
698: + super Class + "'");
699: }
700: }
701: }
702: }
703: } catch (Exception e) {
704: throw new MetadataException(
705: "Can't deregister super class '"
706: + cld.getBaseClass()
707: + "' for class-descriptor: " + cld, e);
708: }
709: }
710:
711: /**
712: * Return <em>sub-classes</em> of the specified class using the
713: * <em>"super"-Reference</em> concept.
714: * @param cld The {@link ClassDescriptor} of the class to search for sub-classes.
715: * @param wholeTree If set <em>true</em>, the whole sub-class tree of the specified
716: * class will be returned. If <em>false</em> only the direct sub-classes of the specified class
717: * will be returned.
718: * @return An array of <em>sub-classes</em> for the specified class.
719: */
720: public Class[] getSubClassesMultipleJoinedTables(
721: ClassDescriptor cld, boolean wholeTree) {
722: ArrayList result = new ArrayList();
723: createResultSubClassesMultipleJoinedTables(result, cld,
724: wholeTree);
725: return (Class[]) result.toArray(new Class[result.size()]);
726: }
727:
728: /**
729: * Add all sub-classes using multiple joined tables feature for specified class.
730: * @param result The list to add results.
731: * @param cld The {@link ClassDescriptor} of the class to search for sub-classes.
732: * @param wholeTree If set <em>true</em>, the whole sub-class tree of the specified
733: * class will be returned. If <em>false</em> only the direct sub-classes of the specified class
734: * will be returned.
735: */
736: private void createResultSubClassesMultipleJoinedTables(
737: List result, ClassDescriptor cld, boolean wholeTree) {
738: List tmp = (List) super ClassMultipleJoinedTablesMap.get(cld
739: .getClassOfObject());
740: if (tmp != null) {
741: result.addAll(tmp);
742: if (wholeTree) {
743: for (int i = 0; i < tmp.size(); i++) {
744: Class subClass = (Class) tmp.get(i);
745: ClassDescriptor subCld = getDescriptorFor(subClass);
746: createResultSubClassesMultipleJoinedTables(result,
747: subCld, wholeTree);
748: }
749: }
750: }
751: }
752:
753: protected void finalize() throws Throwable {
754: log.info("# finalize DescriptorRepository instance #");
755: super.finalize();
756: }
757: }
|