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