View Javadoc

1   package org.apache.ojb.broker.metadata;
2   
3   /* Copyright 2002-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.io.Serializable;
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  import java.sql.Timestamp;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Vector;
32  
33  import org.apache.commons.lang.builder.ToStringBuilder;
34  import org.apache.commons.lang.builder.ToStringStyle;
35  import org.apache.ojb.broker.PersistenceBrokerException;
36  import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
37  import org.apache.ojb.broker.accesslayer.RowReader;
38  import org.apache.ojb.broker.accesslayer.RowReaderDefaultImpl;
39  import org.apache.ojb.broker.accesslayer.StatementsForClassFactory;
40  import org.apache.ojb.broker.accesslayer.StatementsForClassIF;
41  import org.apache.ojb.broker.core.ValueContainer;
42  import org.apache.ojb.broker.locking.IsolationLevels;
43  import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
44  import org.apache.ojb.broker.util.ClassHelper;
45  import org.apache.ojb.broker.util.SqlHelper;
46  import org.apache.ojb.broker.util.configuration.Configuration;
47  import org.apache.ojb.broker.util.configuration.Configurator;
48  import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
49  import org.apache.ojb.broker.util.logging.LoggerFactory;
50  
51  
52  /**
53   * A ClassDescriptor contains all information for mapping objects of a
54   * given class to database tables.
55   * <br>
56   * Note: Be careful when use ClassDescriptor variables or caching
57   * ClassDescriptor instances, because instances could become invalid
58   * during runtime (see {@link MetadataManager}).
59   *
60   * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
61   * @version $Id: ClassDescriptor.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
62   */
63  public final class ClassDescriptor extends DescriptorBase
64      implements Serializable, XmlCapable, IsolationLevels
65  {
66  	private String persistentFieldClassName;
67  
68      private static final long serialVersionUID = -5212253607374173965L;
69  
70      public static final String DYNAMIC_STR = "dynamic";
71      public static final String OJB_CONCRETE_CLASS = "ojbConcreteClass";
72      private static final Class[] NO_PARAMS = {};
73  
74      //---------------------------------------------------------------
75      /**
76       * The descriptor for the insert procedure/function.
77       */
78      private InsertProcedureDescriptor insertProcedure;
79  
80      //---------------------------------------------------------------
81      /**
82       * The descriptor for the update procedure/function.
83       */
84      private UpdateProcedureDescriptor updateProcedure;
85  
86      //---------------------------------------------------------------
87      /**
88       * The descriptor for the delete procedure/function.
89       */
90      private DeleteProcedureDescriptor deleteProcedure;
91  
92      //---------------------------------------------------------------
93      // transient fields, to make this class serializable we have to declare
94      // some transient fields and some associated string fields to reinitialze
95      // transient fields after serialization
96      //---------------------------------------------------------------
97      /**
98       * optional method to be invoked after instance fields are initialized
99       */
100     private transient Method initializationMethod;
101     private String initializationMethodName;
102 
103     private transient Method factoryMethod;
104     private String factoryMethodName;
105     /**
106      * whether we have already tried to look up the zero
107      * argument constructor. Transient declared, because
108      * {@link Constructor} is transient and we need to
109      * reinitialize constructor after serialization.
110      */
111     private transient boolean alreadyLookedupZeroArguments = false;
112     /**
113      * the zero argument constructor for this class
114      */
115     private transient Constructor zeroArgumentConstructor = null;
116 
117     /**
118      * used to signal use of ojbConcreteClass field
119      */
120     private transient boolean ojbConcreteFieldCheckDone = false;
121     private transient FieldDescriptor ojbConcreteClassField;
122     /**
123      * We have to bound {@link org.apache.ojb.broker.accesslayer.StatementsForClassIF}
124      * instance to this class, because metadata may change.
125      */
126     private transient StatementsForClassIF statementsForClass;
127     //---------------------------------------------------------------
128     // end transient fields
129     //---------------------------------------------------------------
130 
131     private DescriptorRepository m_repository;
132     /**
133      * optional class.method to be invoked to create object instance.  Both
134      * of these must be present for this function to be successful.
135      */
136     private Class factoryClass;
137     private int useIdentityColumn = 0;
138 
139     private String baseClass = null;
140     /**
141      * transaction isolation level specified for this class, used in the ODMG server
142      */
143     private int m_IsolationLevel;
144     /**
145      * the SQL SCHEMA of the underlying table of this class
146      */
147     private String schema = null;
148     /**
149      * the described class
150      */
151     private Class m_Class = null;
152     /**
153      * whether the described class is abstract
154      */
155     private boolean isAbstract = false;
156     /**
157      * the table name used to store the scalar attributes of this class
158      */
159     private String m_TableName = null;
160 //    private Vector superPersistentFieldDescriptors = null;
161     /**
162      * the RowReader for this class
163      */
164     private RowReader m_rowReader = null;
165 /*
166 arminw:
167 TODO: this feature doesn't work, so remove/reuse this in future
168 */
169     /**
170      * the class that this class extends
171      */
172     private String superClass;
173     /**
174      * reference column for the superclass
175      */
176     private int superClassFieldRef;
177     /**
178      * does the described class represent an interface?
179      */
180     private boolean m_isInterface = false;
181     /**
182      * the proxy class for the described class, may be null
183      */
184     private Class proxyClass = null;
185     /**
186      * the proxy class name for the described class, may be null
187      */
188     private String proxyClassName = null;
189     /**
190      * if false do not accept implicit locks on this class
191      */
192     private boolean acceptLocks = true;
193     /**
194      * if true instances of this class are always refreshed
195      * even if they are already in the cache.
196      * false by default.
197      */
198     private boolean alwaysRefresh = false;
199     private int m_ProxyPrefetchingLimit = 50;
200     /**
201      * optional, ObjectCacheDescriptor for representing class
202      */
203     private ObjectCacheDescriptor objectCacheDescriptor;
204     /**
205      * the vector of indices used in DDL generation.
206      */
207     private Vector indexes = new Vector();
208 
209     //-----------------------------------------------------------------
210     //-----------------------------------------------------------------
211     // !!! the following arrays and maps have take care of metadata changes!!!
212     //-----------------------------------------------------------------
213     //-----------------------------------------------------------------
214     private FieldDescriptor m_autoIncrementField = null;
215     /**
216      * the FieldDescriptors for the primitive attributes
217      */
218     private FieldDescriptor[] m_FieldDescriptions = null;
219     /**
220      * the descriptors for collection attributes
221      */
222     private Vector m_CollectionDescriptors = new Vector();
223     /**
224      * the descriptor for 1-1 reference attributes
225      */
226     private Vector m_ObjectReferenceDescriptors = new Vector();
227     /**
228      * the non-primary key FieldDescriptors
229      */
230     private FieldDescriptor[] m_nonPkFieldDescriptors = null;
231     /**
232      * the primary key FieldDescriptors
233      */
234     private FieldDescriptor[] m_PkFieldDescriptors = null;
235     /**
236      * the read/write FieldDescriptors BRJ
237      */
238     private FieldDescriptor[] m_RwFieldDescriptors = null;
239     private FieldDescriptor[] m_RwNonPkFieldDescriptors = null;
240     /**
241      * the optimistic lockingFieldDescriptors BRJ
242      */
243     private FieldDescriptor[] m_lockingFieldDescriptors = null;
244     /**
245      * the list of classes in the extent of this class. can be empty
246      */
247     private Vector extentClasses = new Vector();
248     /**
249      * the list of class names in the extent of this class. can be empty
250      */
251     private Vector extentClassNames = new Vector();
252     private Map m_fieldDescriptorNameMap = null;
253     private Map m_collectionDescriptorNameMap = null;
254     private Map m_objectReferenceDescriptorsNameMap = null;
255 
256     // BRJ: ClassDescriptor referenced by 'super' ObjectReferenceDescriptor
257     private ClassDescriptor m_superCld = null;
258     private boolean m_superCldSet = false;
259 
260     //-----------------------------------------------------------------
261     //-----------------------------------------------------------------
262     // END of cached metadata information
263     //-----------------------------------------------------------------
264     //-----------------------------------------------------------------
265 
266 
267     //---------------------------------------------------------------
268     /**
269      * Constructor declaration
270      */
271     public ClassDescriptor(DescriptorRepository pRepository)
272     {
273         m_repository = pRepository;
274         m_IsolationLevel = pRepository.getDefaultIsolationLevel();
275     }
276 
277 
278     //---------------------------------------------------------------
279     // method declarations
280     //---------------------------------------------------------------
281     public String getBaseClass()
282     {
283         return baseClass;
284     }
285     public void setBaseClass(String baseClass)
286     {
287         this.baseClass = baseClass;
288         // first deregister
289         getRepository().deregisterSuperClassMultipleJoinedTables(this);
290         // register classes using mapping of classes to multiple joined tables
291         getRepository().registerSuperClassMultipleJoinedTables(this);
292     }
293 
294 //    /**
295 //     * @deprecated no longer needed map class on multi joined table
296 //     */
297 //    public void setSuperPersistentFieldDescriptors(Vector superPersistentFieldDescriptors)
298 //    {
299 //        this.superPersistentFieldDescriptors = superPersistentFieldDescriptors;
300 //    }
301 //
302 //    /**
303 //     * @deprecated no longer needed map class on multi joined table
304 //     */
305 //    public Vector getSuperPersistentFieldDescriptors()
306 //    {
307 //        return superPersistentFieldDescriptors;
308 //    }
309 
310     /**
311      * Returns the appropriate {@link ObjectCacheDescriptor}
312      * or <code>null</code> if not specified.
313      */
314     public ObjectCacheDescriptor getObjectCacheDescriptor()
315     {
316         return objectCacheDescriptor;
317     }
318 
319     /**
320      * Sets the {@link ObjectCacheDescriptor} for representing class.
321      */
322     public void setObjectCacheDescriptor(ObjectCacheDescriptor objectCacheDescriptor)
323     {
324         this.objectCacheDescriptor = objectCacheDescriptor;
325     }
326 
327 
328     /**
329      * sets the row reader class for this descriptor
330      */
331     public void setRowReader(RowReader newReader)
332     {
333         m_rowReader = newReader;
334     }
335 
336     /**
337      * Returns the {@link org.apache.ojb.broker.accesslayer.RowReader}
338      * for this descriptor.
339      */
340     public synchronized RowReader getRowReader()
341     {
342         if (m_rowReader == null)
343         {
344             Configurator configurator = OjbConfigurator.getInstance();
345             Configuration config = configurator.getConfigurationFor(null);
346             Class rrClass = config.getClass("RowReaderDefaultClass", RowReaderDefaultImpl.class);
347 
348             setRowReader(rrClass.getName());
349         }
350         return m_rowReader;
351     }
352 
353     /**
354      * sets the row reader class name for thie class descriptor
355      */
356     public void setRowReader(String newReaderClassName)
357     {
358         try
359         {
360             m_rowReader =
361                 (RowReader) ClassHelper.newInstance(
362                     newReaderClassName,
363                     ClassDescriptor.class,
364                     this);
365         }
366         catch (Exception e)
367         {
368             throw new MetadataException("Instantiating of current set RowReader failed", e);
369         }
370     }
371 
372     public String getRowReaderClassName()
373     {
374         return m_rowReader != null ? m_rowReader.getClass().getName() : null;
375     }
376 
377     /**
378      * returns the name of the described class
379      * @return String name of the described class
380      */
381     public String getClassNameOfObject()
382     {
383         return m_Class != null ? m_Class.getName() : null;
384     }
385 
386     /**
387      * returns the class object of the described class
388      * @return Class the described class
389      */
390     public Class getClassOfObject()
391     {
392         return m_Class;
393     }
394 
395     /**
396      * sets the class object described by this descriptor.
397      * @param c the class to describe
398      */
399     public void setClassOfObject(Class c)
400     {
401         m_Class = c;
402         isAbstract = Modifier.isAbstract(m_Class.getModifiers());
403         // TODO : Shouldn't the HashMap in DescriptorRepository be updated as well?
404     }
405 
406     /**
407      * adds a FIELDDESCRIPTOR to this ClassDescriptor.
408      * @param fld
409      */
410     public void addFieldDescriptor(FieldDescriptor fld)
411     {
412         fld.setClassDescriptor(this); // BRJ
413         if (m_FieldDescriptions == null)
414         {
415             m_FieldDescriptions = new FieldDescriptor[1];
416             m_FieldDescriptions[0] = fld;
417         }
418         else
419         {
420             int size = m_FieldDescriptions.length;
421             FieldDescriptor[] tmpArray = new FieldDescriptor[size + 1];
422             System.arraycopy(m_FieldDescriptions, 0, tmpArray, 0, size);
423             tmpArray[size] = fld;
424             m_FieldDescriptions = tmpArray;
425             // 2. Sort fields according to their getOrder() Property
426             Arrays.sort(m_FieldDescriptions, FieldDescriptor.getComparator());
427         }
428 
429         m_fieldDescriptorNameMap = null;
430         m_PkFieldDescriptors = null;
431         m_nonPkFieldDescriptors = null;
432         m_lockingFieldDescriptors = null;
433         m_RwFieldDescriptors = null;
434         m_RwNonPkFieldDescriptors = null;
435     }
436 
437     public boolean removeFieldDescriptor(FieldDescriptor fld)
438     {
439         boolean result = false;
440         if(m_FieldDescriptions == null) return result;
441 
442         List list = new ArrayList(Arrays.asList(m_FieldDescriptions));
443         result = list.remove(fld);
444         m_FieldDescriptions = (FieldDescriptor[]) list.toArray(new FieldDescriptor[list.size()]);
445 
446         m_fieldDescriptorNameMap = null;
447         m_PkFieldDescriptors = null;
448         m_nonPkFieldDescriptors = null;
449         m_lockingFieldDescriptors = null;
450         m_RwFieldDescriptors = null;
451         m_RwNonPkFieldDescriptors = null;
452         return result;
453     }
454 
455     /**
456      * Returns all defined {@link CollectionDescriptor} for
457      * this class descriptor.
458      */
459     public Vector getCollectionDescriptors()
460     {
461         return m_CollectionDescriptors;
462     }
463 
464     /**
465      * Add a {@link CollectionDescriptor}.
466      */
467     public void addCollectionDescriptor(CollectionDescriptor cod)
468     {
469         m_CollectionDescriptors.add(cod);
470         cod.setClassDescriptor(this); // BRJ
471 
472         m_collectionDescriptorNameMap = null;
473     }
474 
475     public void removeCollectionDescriptor(CollectionDescriptor cod)
476     {
477         m_CollectionDescriptors.remove(cod);
478         m_collectionDescriptorNameMap = null;
479     }
480 
481     /**
482      * Returns all defined {@link ObjectReferenceDescriptor}.
483      */
484     public Vector getObjectReferenceDescriptors()
485     {
486         return m_ObjectReferenceDescriptors;
487     }
488 
489     /**
490      * Add a {@link ObjectReferenceDescriptor}.
491      */
492     public void addObjectReferenceDescriptor(ObjectReferenceDescriptor ord)
493     {
494         m_ObjectReferenceDescriptors.add(ord);
495         ord.setClassDescriptor(this); // BRJ
496 
497         m_objectReferenceDescriptorsNameMap = null;
498     }
499 
500     public void removeObjectReferenceDescriptor(ObjectReferenceDescriptor ord)
501     {
502         m_ObjectReferenceDescriptors.remove(ord);
503         m_objectReferenceDescriptorsNameMap = null;
504     }
505 
506     /**
507      * Get an ObjectReferenceDescriptor by name BRJ
508      * @param name
509      * @return ObjectReferenceDescriptor or null
510      */
511     public ObjectReferenceDescriptor getObjectReferenceDescriptorByName(String name)
512     {
513         ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor)
514             getObjectReferenceDescriptorsNameMap().get(name);
515 
516         //
517         // BRJ: if the ReferenceDescriptor is not found
518         // look in the ClassDescriptor referenced by 'super' for it
519         //
520         if (ord == null)
521         {
522             ClassDescriptor superCld = getSuperClassDescriptor();
523             if (superCld != null)
524             {
525                 ord = superCld.getObjectReferenceDescriptorByName(name);
526             }
527         }
528         return ord;
529     }
530 
531     private Map getObjectReferenceDescriptorsNameMap()
532     {
533         if (m_objectReferenceDescriptorsNameMap == null)
534         {
535             HashMap nameMap = new HashMap();
536 
537             Vector descriptors = getObjectReferenceDescriptors();
538             for (int i = descriptors.size() - 1; i >= 0; i--)
539             {
540                 ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) descriptors.get(i);
541                 nameMap.put(ord.getAttributeName(), ord);
542             }
543             m_objectReferenceDescriptorsNameMap = nameMap;
544         }
545 
546         return m_objectReferenceDescriptorsNameMap;
547     }
548 
549     /**
550      * Get an CollectionDescriptor by name  BRJ
551      * @param name
552      * @return CollectionDescriptor or null
553      */
554     public CollectionDescriptor getCollectionDescriptorByName(String name)
555     {
556         if (name == null)
557         {
558             return null;
559         }
560 
561         CollectionDescriptor cod  = (CollectionDescriptor) getCollectionDescriptorNameMap().get(name);
562 
563         //
564         // BRJ: if the CollectionDescriptor is not found
565         // look in the ClassDescriptor referenced by 'super' for it
566         //
567         if (cod == null)
568         {
569             ClassDescriptor superCld = getSuperClassDescriptor();
570             if (superCld != null)
571             {
572                 cod = superCld.getCollectionDescriptorByName(name);
573             }
574         }
575 
576         return cod;
577     }
578 
579     private Map getCollectionDescriptorNameMap()
580     {
581         if (m_collectionDescriptorNameMap == null)
582         {
583             HashMap nameMap = new HashMap();
584 
585             Vector descriptors = getCollectionDescriptors();
586             for (int i = descriptors.size() - 1; i >= 0; i--)
587             {
588                 CollectionDescriptor cod = (CollectionDescriptor) descriptors.get(i);
589                 nameMap.put(cod.getAttributeName(), cod);
590             }
591             m_collectionDescriptorNameMap = nameMap;
592         }
593 
594         return m_collectionDescriptorNameMap;
595     }
596 
597     /**
598      * Answers the ClassDescriptor referenced by 'super' ReferenceDescriptor.
599      * @return ClassDescriptor or null
600      */
601     public ClassDescriptor getSuperClassDescriptor()
602     {
603         if (!m_superCldSet)
604         {
605             if(getBaseClass() != null)
606             {
607                 m_superCld = getRepository().getDescriptorFor(getBaseClass());
608                 if(m_superCld.isAbstract() || m_superCld.isInterface())
609                 {
610                     throw new MetadataException("Super class mapping only work for real class, but declared super class" +
611                             " is an interface or is abstract. Declared class: " + m_superCld.getClassNameOfObject());
612                 }
613             }
614             m_superCldSet = true;
615         }
616 
617         return m_superCld;
618     }
619 
620     /**
621      * add an Extent class to the current descriptor
622      * @param newExtendClass
623      * @deprecated use {@link #addExtentClass(String newExtentClass)} instead
624      */
625     public void addExtentClassName(Class newExtendClass)
626     {
627         addExtentClass(newExtendClass);
628     }
629 
630     /**
631      * add an Extent class to the current descriptor
632      * @param newExtendClass
633      */
634     public void addExtentClass(Class newExtendClass)
635     {
636         extentClasses.add(newExtendClass);
637         this.addExtentClass(newExtendClass.getName());
638     }
639 
640     /**
641      * add an Extent class to the current descriptor
642      * @param newExtentClassName name of the class to add
643      */
644     public void addExtentClass(String newExtentClassName)
645     {
646         extentClassNames.add(newExtentClassName);
647         if(m_repository != null) m_repository.addExtent(newExtentClassName, this);
648     }
649 
650     public void removeExtentClass(String extentClassName)
651     {
652         extentClassNames.remove(extentClassName);
653         if(m_repository != null) m_repository.removeExtent(extentClassName);
654     }
655 
656     /**
657      * return all classes in this extent.
658      * Creation date: (02.02.2001 17:49:11)
659      * @return java.util.Vector
660      */
661     public synchronized Vector getExtentClasses()
662     {
663         if (extentClassNames.size() != extentClasses.size())
664         {
665             extentClasses.clear();
666             for (Iterator iter = extentClassNames.iterator(); iter.hasNext();)
667             {
668                 String classname = (String) iter.next();
669                 Class extentClass;
670                 try
671                 {
672                     extentClass = ClassHelper.getClass(classname);
673                 }
674                 catch (ClassNotFoundException e)
675                 {
676                     throw new MetadataException(
677                         "Unable to load class ["
678                             + classname
679                             + "]. Make sure it is available on the classpath.",
680                         e);
681                 }
682                 extentClasses.add(extentClass);
683             }
684         }
685         return extentClasses;
686     }
687 
688 
689     /**
690      * Return the names of all classes in this extent
691      * @return java.util.Vector a Vector containing the fully qualified names
692      * of all classes in this extent
693      */
694     public synchronized Vector getExtentClassNames()
695     {
696         return this.extentClassNames;
697     }
698 
699     /**
700      * Insert the method's description here.
701      * Creation date: (02.02.2001 17:49:11)
702      * @return boolean
703      */
704     public boolean isExtent()
705     {
706         return (getExtentClassNames().size() > 0);
707     }
708 
709     /**
710      * Insert the method's description here.
711      * Creation date: (26.01.2001 09:20:09)
712      * @return java.lang.Class
713      */
714     public synchronized Class getProxyClass()
715     {
716         if ((proxyClass == null) && (proxyClassName != null))
717         {
718             if (isDynamicProxy())
719             {
720                 /**
721                  * AClute: Return the same class back if it is dynamic. This signifies
722                  * that this class become the base to a generated sub-class, regadless
723                  * of which Proxy implementation is used
724                  */
725                 return getClassOfObject();
726             }
727             else
728             {
729                 try
730                 {
731                     proxyClass = ClassHelper.getClass(proxyClassName);
732                 }
733                 catch (ClassNotFoundException e)
734                 {
735                     throw new MetadataException(e);
736                 }
737             }
738         }
739         return proxyClass;
740     }
741 
742     public boolean isDynamicProxy()
743     {
744         return DYNAMIC_STR.equalsIgnoreCase(proxyClassName);
745     }
746 
747     /**
748      * Sets the proxy class to be used.
749      * @param newProxyClass java.lang.Class
750      */
751     public void setProxyClass(Class newProxyClass)
752     {
753         proxyClass = newProxyClass;
754         if (proxyClass == null)
755         {
756             setProxyClassName(null);
757         }
758         else
759         {
760             proxyClassName = proxyClass.getName();
761         }
762     }
763 
764     /**
765      * Sets the name of the proxy class to be used.
766      * using "dynamic" instead of a real classname
767      * will result in usage of dynamic proxies.
768      * @param newProxyClassName the classname or "dynamic"
769      */
770     public void setProxyClassName(String newProxyClassName)
771     {
772         proxyClassName = newProxyClassName;
773     }
774 
775     /**
776      * Get the name of the proxy class. This method doesn't try to access
777      * the real class, so it can be called even if the class doesn't exist.
778      */
779     public String getProxyClassName()
780     {
781         return proxyClassName;
782     }
783 
784     /**
785      * Returns array of all FieldDescriptors.
786      */
787     public FieldDescriptor[] getFieldDescriptions()
788     {
789         return m_FieldDescriptions;
790     }
791 
792     /**
793      * Returns the matching {@link FieldDescriptor}.
794      */
795     public FieldDescriptor getFieldDescriptorByIndex(int index)
796     {
797         return m_FieldDescriptions[index - 1];
798     }
799 
800     /**
801      * Returns the matching {@link FieldDescriptor} - only fields
802      * of the current class will be scanned, to include fields defined
803      * the the super-classes too, use method {@link #getFieldDescriptor(boolean)}.
804      */
805     public FieldDescriptor getFieldDescriptorByName(String name)
806     {
807         if (name == null || m_FieldDescriptions == null)
808         {
809             return null;
810         }
811 
812         if (m_fieldDescriptorNameMap == null)
813         {
814             HashMap nameMap = new HashMap();
815 
816             FieldDescriptor[] descriptors = getFieldDescriptions();
817             for (int i = descriptors.length - 1; i >= 0; i--)
818             {
819                 FieldDescriptor fld = descriptors[i];
820                 nameMap.put(fld.getPersistentField().getName(), fld);
821             }
822 
823             m_fieldDescriptorNameMap = nameMap;
824         }
825 
826         return (FieldDescriptor) m_fieldDescriptorNameMap.get(name);
827     }
828 
829     /**
830      * return the FieldDescriptor for the Attribute referenced in the path<br>
831      * the path may contain simple attribut names, functions and path expressions
832      * using relationships <br>
833      * ie: name, avg(price), adress.street
834      * @param aPath the path to the attribute
835      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
836      * if no segment was used.
837      * @return the FieldDescriptor or null (ie: for m:n queries)
838      */
839     public FieldDescriptor getFieldDescriptorForPath(String aPath, Map pathHints)
840     {
841         ArrayList desc = getAttributeDescriptorsForPath(aPath, pathHints);
842         FieldDescriptor fld = null;
843         Object temp;
844 
845         if (!desc.isEmpty())
846         {
847             temp = desc.get(desc.size() - 1);
848             if (temp instanceof FieldDescriptor)
849             {
850                 fld = (FieldDescriptor) temp;
851             }
852         }
853         return fld;
854     }
855 
856     /**
857      * return the FieldDescriptor for the Attribute referenced in the path<br>
858      * the path may contain simple attribut names, functions and path expressions
859      * using relationships <br>
860      * ie: name, avg(price), adress.street
861      * @param aPath the path to the attribute
862      * @return the FieldDescriptor or null (ie: for m:n queries)
863      */
864     public FieldDescriptor getFieldDescriptorForPath(String aPath)
865     {
866         return getFieldDescriptorForPath(aPath, null);
867     }
868 /*
869 arminw:
870 TODO: this feature doesn't work, so remove this in future
871 */
872     /**
873      *
874      * @return this classes FieldDescriptor's as well as it's parents and so on and so on
875      */
876     public FieldDescriptor[] getFieldDescriptorsInHeirarchy()
877     {
878         if (superClass == null)
879         {
880             return getFieldDescriptions();
881         }
882         ClassDescriptor cldSuper = getRepository().getDescriptorFor(superClass);
883         return appendFieldDescriptorArrays(
884             getFieldDescriptions(),
885             cldSuper.getFieldDescriptorsInHeirarchy());
886     }
887 
888     private FieldDescriptor[] appendFieldDescriptorArrays(
889         FieldDescriptor[] fieldDescriptions,
890         FieldDescriptor[] fieldDescriptorsInHeirarchy)
891     {
892         // take the 2 arrays and add them into one
893         int size = fieldDescriptions.length + fieldDescriptorsInHeirarchy.length;
894         FieldDescriptor[] newArray = new FieldDescriptor[size];
895         System.arraycopy(fieldDescriptions, 0, newArray, 0, fieldDescriptions.length);
896         System.arraycopy(fieldDescriptorsInHeirarchy, 0, newArray, fieldDescriptions.length, fieldDescriptorsInHeirarchy.length);
897         return newArray;
898     }
899 
900     /**
901      * Returns the first found autoincrement field
902      * defined in this class descriptor. Use carefully
903      * when multiple autoincrement field were defined.
904      * @deprecated does not make sense because it's possible to
905      * define more than one autoincrement field. Alternative
906      * see {@link #getAutoIncrementFields}
907      */
908     public FieldDescriptor getAutoIncrementField()
909     {
910         if (m_autoIncrementField == null)
911         {
912             FieldDescriptor[] fds = getPkFields();
913 
914             for (int i = 0; i < fds.length; i++)
915             {
916                 FieldDescriptor fd = fds[i];
917                 if (fd.isAutoIncrement())
918                 {
919                     m_autoIncrementField = fd;
920                     break;
921                 }
922             }
923         }
924         if (m_autoIncrementField == null)
925         {
926             LoggerFactory.getDefaultLogger().warn(
927                 this.getClass().getName()
928                     + ": "
929                     + "Could not find autoincrement attribute for class: "
930                     + this.getClassNameOfObject());
931         }
932         return m_autoIncrementField;
933     }
934 
935     public FieldDescriptor[] getAutoIncrementFields()
936     {
937         ArrayList result = new ArrayList();
938         for (int i = 0; i < m_FieldDescriptions.length; i++)
939         {
940             FieldDescriptor field = m_FieldDescriptions[i];
941             if(field.isAutoIncrement()) result.add(field);
942         }
943         return (FieldDescriptor[]) result.toArray(new FieldDescriptor[result.size()]);
944     }
945 
946     /**
947      * returns an Array with an Objects CURRENT locking VALUES , BRJ
948      * @throws PersistenceBrokerException if there is an erros accessing o field values
949      */
950     public ValueContainer[] getCurrentLockingValues(Object o) throws PersistenceBrokerException
951     {
952         FieldDescriptor[] fields = getLockingFields();
953         ValueContainer[] result = new ValueContainer[fields.length];
954         for (int i = 0; i < result.length; i++)
955         {
956             result[i] = new ValueContainer(fields[i].getPersistentField().get(o), fields[i].getJdbcType());
957         }
958         return result;
959     }
960 
961     /**
962      * updates the values for locking fields , BRJ
963      * handles int, long, Timestamp
964      * respects updateLock so locking field are only updated when updateLock is true
965      * @throws PersistenceBrokerException if there is an erros accessing obj field values
966      */
967     public void updateLockingValues(Object obj) throws PersistenceBrokerException
968     {
969         FieldDescriptor[] fields = getLockingFields();
970         for (int i = 0; i < fields.length; i++)
971         {
972             FieldDescriptor fmd = fields[i];
973             if (fmd.isUpdateLock())
974             {
975                 PersistentField f = fmd.getPersistentField();
976                 Object cv = f.get(obj);
977                 // int
978                 if ((f.getType() == int.class) || (f.getType() == Integer.class))
979                 {
980                     int newCv = 0;
981                     if (cv != null)
982                     {
983                         newCv = ((Number) cv).intValue();
984                     }
985                     newCv++;
986                     f.set(obj, new Integer(newCv));
987                 }
988                 // long
989                 else if ((f.getType() == long.class) || (f.getType() == Long.class))
990                 {
991                     long newCv = 0;
992                     if (cv != null)
993                     {
994                         newCv = ((Number) cv).longValue();
995                     }
996                     newCv++;
997                     f.set(obj, new Long(newCv));
998                 }
999                 // Timestamp
1000                 else if (f.getType() == Timestamp.class)
1001                 {
1002                     long newCv = System.currentTimeMillis();
1003                     f.set(obj, new Timestamp(newCv));
1004                 }
1005             }
1006         }
1007     }
1008 
1009     /**
1010      * return an array of NONPK-FieldDescription sorted ascending
1011      * according to the field-descriptions getOrder() property
1012      */
1013     public FieldDescriptor[] getNonPkFields()
1014     {
1015         if (m_nonPkFieldDescriptors == null)
1016         {
1017             // 1. collect all Primary Key fields from Field list
1018             Vector vec = new Vector();
1019             for (int i = 0; i < m_FieldDescriptions.length; i++)
1020             {
1021                 FieldDescriptor fd = m_FieldDescriptions[i];
1022                 if (!fd.isPrimaryKey())
1023                 {
1024                     vec.add(fd);
1025                 }
1026             }
1027             // 2. Sort fields according to their getOrder() Property
1028             Collections.sort(vec, FieldDescriptor.getComparator());
1029             m_nonPkFieldDescriptors =
1030                 (FieldDescriptor[]) vec.toArray(new FieldDescriptor[vec.size()]);
1031         }
1032         return m_nonPkFieldDescriptors;
1033     }
1034 
1035     /**
1036      * Return an array of PK FieldDescription sorted ascending
1037      * according to the field-descriptions getOrder() property
1038      */
1039     public FieldDescriptor[] getPkFields()
1040     {
1041         if (m_PkFieldDescriptors == null)
1042         {
1043             // 1. collect all Primary Key fields from Field list
1044             Vector vec = new Vector();
1045             // 1.a if descriptor describes an interface: take PK fields from an implementors ClassDescriptor
1046             if (m_isInterface)
1047             {
1048                 if (getExtentClasses().size() == 0)
1049                 {
1050                     throw new PersistenceBrokerException(
1051                         "No Implementors declared for interface "
1052                             + this.getClassOfObject().getName());
1053                 }
1054                 Class implementor = (Class) getExtentClasses().get(0);
1055                 ClassDescriptor implCld = this.getRepository().getDescriptorFor(implementor);
1056                 m_PkFieldDescriptors = implCld.getPkFields();
1057             }
1058             else
1059             {
1060                 FieldDescriptor[] fields;
1061                 // 1.b if not an interface The classdescriptor must have FieldDescriptors
1062                 fields = getFieldDescriptions();
1063                 // now collect all PK fields
1064                 for (int i = 0; i < fields.length; i++)
1065                 {
1066                     FieldDescriptor fd = fields[i];
1067                     if (fd.isPrimaryKey())
1068                     {
1069                         vec.add(fd);
1070                     }
1071                 }
1072                 // 2. Sort fields according to their getOrder() Property
1073                 Collections.sort(vec, FieldDescriptor.getComparator());
1074                 m_PkFieldDescriptors = (FieldDescriptor[]) vec.toArray(new FieldDescriptor[vec.size()]);
1075             }
1076         }
1077         return m_PkFieldDescriptors;
1078     }
1079 
1080     /**
1081      * Returns array of read/write non pk FieldDescriptors.
1082      */
1083     public FieldDescriptor[] getNonPkRwFields()
1084     {
1085         if (m_RwNonPkFieldDescriptors == null)
1086         {
1087             FieldDescriptor[] fields = getNonPkFields();
1088             Collection rwFields = new ArrayList();
1089 
1090             for (int i = 0; i < fields.length; i++)
1091             {
1092                 FieldDescriptor fd = fields[i];
1093                 if (!fd.isAccessReadOnly())
1094                 {
1095                     rwFields.add(fd);
1096                 }
1097             }
1098             m_RwNonPkFieldDescriptors =
1099                 (FieldDescriptor[]) rwFields.toArray(new FieldDescriptor[rwFields.size()]);
1100         }
1101         return m_RwNonPkFieldDescriptors;
1102     }
1103 
1104     /**
1105      * Returns array of read/write FieldDescriptors.
1106      */
1107     public FieldDescriptor[] getAllRwFields()
1108     {
1109         if (m_RwFieldDescriptors == null)
1110         {
1111             FieldDescriptor[] fields = getFieldDescriptions();
1112             Collection rwFields = new ArrayList();
1113 
1114             for (int i = 0; i < fields.length; i++)
1115             {
1116                 FieldDescriptor fd = fields[i];
1117                 /*
1118                 arminw: if locking is enabled and the increment of locking
1119                 values is done by the database, the field is read-only
1120                 */
1121                 if(fd.isAccessReadOnly() || (fd.isLocking() && !fd.isUpdateLock()))
1122                 {
1123                     continue;
1124                 }
1125                 rwFields.add(fd);
1126             }
1127             m_RwFieldDescriptors =
1128                 (FieldDescriptor[]) rwFields.toArray(new FieldDescriptor[rwFields.size()]);
1129         }
1130 
1131         return m_RwFieldDescriptors;
1132     }
1133 
1134     /**
1135      * return an array of FieldDescription for optimistic locking sorted ascending
1136      * according to the field-descriptions getOrder() property
1137      */
1138     public FieldDescriptor[] getLockingFields()
1139     {
1140         if (m_lockingFieldDescriptors == null)
1141         {
1142             // 1. collect all Primary Key fields from Field list
1143             Vector vec = new Vector();
1144             for (int i = 0; i < m_FieldDescriptions.length; i++)
1145             {
1146                 FieldDescriptor fd = m_FieldDescriptions[i];
1147                 if (fd.isLocking())
1148                 {
1149                     vec.add(fd);
1150                 }
1151             }
1152             // 2. Sort fields according to their getOrder() Property
1153             Collections.sort(vec, FieldDescriptor.getComparator());
1154             m_lockingFieldDescriptors =
1155                 (FieldDescriptor[]) vec.toArray(new FieldDescriptor[vec.size()]);
1156         }
1157         return m_lockingFieldDescriptors;
1158     }
1159 
1160     /**
1161      * return true if optimistic locking is used
1162      */
1163     public boolean isLocking()
1164     {
1165         return getLockingFields().length > 0;
1166     }
1167 
1168     /**
1169      * return all AttributeDescriptors for the path<br>
1170      * ie: partner.addresses.street returns a Collection of 3 AttributeDescriptors
1171      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
1172      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
1173      * (ObjectReferenceDescriptor, CollectionDescriptor)
1174      * @param aPath the cleaned path to the attribute
1175      * @return ArrayList of AttributeDescriptors
1176      */
1177     public ArrayList getAttributeDescriptorsForPath(String aPath)
1178     {
1179         return getAttributeDescriptorsForPath(aPath, new HashMap());
1180     }
1181 
1182     /**
1183      * return all AttributeDescriptors for the path<br>
1184      * ie: partner.addresses.street returns a Collection of 3 AttributeDescriptors
1185      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
1186      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
1187      * (ObjectReferenceDescriptor, CollectionDescriptor)
1188      * @param aPath the cleaned path to the attribute
1189      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
1190      * if no segment was used.
1191      * @return ArrayList of AttributeDescriptors
1192      */
1193     public ArrayList getAttributeDescriptorsForPath(String aPath, Map pathHints)
1194     {
1195         return getAttributeDescriptorsForCleanPath(SqlHelper.cleanPath(aPath), pathHints);
1196     }
1197 
1198     /**
1199      * return all AttributeDescriptors for the path<br>
1200      * ie: partner.addresses.street returns a Collection of 3 AttributeDescriptors
1201      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
1202      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
1203      * (ObjectReferenceDescriptor, CollectionDescriptor)
1204      * @param aPath the cleaned path to the attribute
1205      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
1206      * if no segment is used.
1207      * @return ArrayList of AttributeDescriptors
1208      */
1209     private ArrayList getAttributeDescriptorsForCleanPath(String aPath, Map pathHints)
1210     {
1211         ArrayList result = new ArrayList();
1212         ClassDescriptor cld = this;
1213         ObjectReferenceDescriptor ord;
1214         FieldDescriptor fld;
1215         String currPath = aPath;
1216         String segment;
1217         StringBuffer processedSegment = new StringBuffer();
1218         int sepPos;
1219         Class itemClass;
1220 
1221         while (currPath.length() > 0)
1222         {
1223             sepPos = currPath.indexOf(".");
1224             if (sepPos >= 0)
1225             {
1226                 segment = currPath.substring(0, sepPos);
1227                 currPath = currPath.substring(sepPos + 1);
1228             }
1229             else
1230             {
1231                 segment = currPath;
1232                 currPath = "";
1233             }
1234 
1235             if (processedSegment.length() > 0)
1236             {
1237                 processedSegment.append(".");
1238             }
1239             processedSegment.append(segment);
1240 
1241             // look for 1:1 or n:1 Relationship
1242             ord = cld.getObjectReferenceDescriptorByName(segment);
1243             if (ord == null)
1244             {
1245                 // look for 1:n or m:n Relationship
1246                 ord = cld.getCollectionDescriptorByName(segment);
1247             }
1248 
1249             if (ord != null)
1250             {
1251                 // BRJ : look for hints for the processed segment
1252                 // ie: ref pointng to ClassA and ref.ref pointing to ClassC
1253                 List hintClasses = pathHints != null ? (List) pathHints.get(processedSegment.toString()) : null;
1254                 if (hintClasses != null && hintClasses.get(0) != null)
1255                 {
1256                     itemClass = (Class) hintClasses.get(0);
1257                 }
1258                 else
1259                 {
1260                     itemClass = ord.getItemClass();
1261                 }
1262 
1263                 cld = cld.getRepository().getDescriptorFor(itemClass);
1264                 result.add(ord);
1265             }
1266             else
1267             {
1268                 // look for Field
1269                 fld = cld.getFieldDescriptorByName(segment);
1270                 if (fld != null)
1271                 {
1272                     result.add(fld);
1273                 }
1274             }
1275         }
1276 
1277         return result;
1278     }
1279 
1280     /**
1281      * returns the zero argument constructor for the class represented by this class descriptor
1282      * or null if a zero argument constructor does not exist.  If the zero argument constructor
1283      * for this class is not public it is made accessible before being returned.
1284      */
1285     public Constructor getZeroArgumentConstructor()
1286     {
1287         if (zeroArgumentConstructor == null && !alreadyLookedupZeroArguments)
1288         {
1289             try
1290             {
1291                 zeroArgumentConstructor = getClassOfObject().getConstructor(NO_PARAMS);
1292             }
1293             catch (NoSuchMethodException e)
1294             {
1295                 //no public zero argument constructor available let's try for a private/protected one
1296                 try
1297                 {
1298                     zeroArgumentConstructor = getClassOfObject().getDeclaredConstructor(NO_PARAMS);
1299 
1300                     //we found one, now let's make it accessible
1301                     zeroArgumentConstructor.setAccessible(true);
1302                 }
1303                 catch (NoSuchMethodException e2)
1304                 {
1305                     //out of options, log the fact and let the method return null
1306                     LoggerFactory.getDefaultLogger().warn(
1307                         this.getClass().getName()
1308                             + ": "
1309                             + "No zero argument constructor defined for "
1310                             + this.getClassOfObject());
1311                 }
1312             }
1313 
1314             alreadyLookedupZeroArguments = true;
1315         }
1316 
1317         return zeroArgumentConstructor;
1318     }
1319 
1320     /*
1321      * @see XmlCapable#toXML()
1322      */
1323     public String toXML()
1324     {
1325         RepositoryTags tags = RepositoryTags.getInstance();
1326         String eol = System.getProperty("line.separator");
1327 
1328         // comment on class
1329         StringBuffer result = new StringBuffer(1024);
1330         result.append( eol);
1331         result.append( "  <!-- Mapping for Class ");
1332         result.append( this.getClassNameOfObject());
1333         result.append( " -->");
1334         result.append( eol );
1335 
1336         // opening tag and attributes
1337         result.append( "  ");
1338         result.append( tags.getOpeningTagNonClosingById(CLASS_DESCRIPTOR));
1339         result.append( eol );
1340 
1341         // class
1342         result.append( "    ");
1343         result.append( tags.getAttribute(CLASS_NAME, this.getClassNameOfObject()));
1344         result.append( eol );
1345 
1346         // isolation level is optional
1347         if (null != getRepository())
1348         {
1349             if (getIsolationLevel() != this.getRepository().getDefaultIsolationLevel())
1350             {
1351                 result.append( "    ");
1352                 result.append( tags.getAttribute(ISOLATION_LEVEL, this.isolationLevelXml()) );
1353                 result.append( eol );
1354             }
1355         }
1356 
1357         Class theProxyClass = null;
1358         try
1359         {
1360             theProxyClass = this.getProxyClass();
1361         }
1362         catch (Throwable t)
1363         {
1364             // Ignore this exception, just try to get the Class object of the
1365             // proxy class in order to be able to decide, whether the class
1366             // is a dynamic proxy or not.
1367         }
1368 
1369         // proxy is optional
1370         if (theProxyClass != null)
1371 	{
1372 	    if (isDynamicProxy())   // tomdz: What about VirtualProxy ?
1373             {
1374         	result.append( "    ");
1375                 result.append( tags.getAttribute(CLASS_PROXY, DYNAMIC_STR));
1376                 result.append( eol );
1377     	    }
1378             else
1379 	    {
1380     	        result.append( "    ");
1381             result.append( tags.getAttribute(CLASS_PROXY, this.getProxyClassName()));
1382             result.append( eol );
1383             }
1384             result.append( "        ");
1385         result.append( tags.getAttribute(PROXY_PREFETCHING_LIMIT, "" + this.getProxyPrefetchingLimit()));
1386         result.append( eol );
1387 	}
1388 
1389         // schema is optional
1390         if (this.getSchema() != null)
1391         {
1392             result.append( "    ");
1393             result.append( tags.getAttribute(SCHEMA_NAME, this.getSchema()));
1394             result.append( eol );
1395         }
1396 
1397         // table name
1398         if (this.getTableName() != null)
1399         {
1400             result.append("    ");
1401             result.append( tags.getAttribute(TABLE_NAME, this.getTableName()));
1402             result.append( eol );
1403         }
1404 
1405         // rowreader is optional
1406         if (this.getRowReaderClassName() != null)
1407         {
1408             result.append( "    ");
1409             result.append( tags.getAttribute(ROW_READER, this.getRowReaderClassName()));
1410             result.append( eol );
1411         }
1412 
1413         //accept-locks is optional, enabled by default
1414         if (!this.acceptLocks)
1415         {
1416             result.append( "        ");
1417             result.append( tags.getAttribute(ACCEPT_LOCKS, "false"));
1418             result.append( eol );
1419         }
1420         // sequence manager attribute not yet implemented
1421 
1422         // initialization method is optional
1423         if (this.getInitializationMethod() != null)
1424         {
1425             result.append( "    ");
1426             result.append( tags.getAttribute(INITIALIZATION_METHOD, this.getInitializationMethod().getName()));
1427             result.append( eol );
1428         }
1429 
1430         // factory class is optional
1431         if (this.getFactoryClass() != null)
1432         {
1433             result.append( "    ");
1434             result.append( tags.getAttribute(FACTORY_CLASS, this.getFactoryClass().getName()) );
1435             result.append( eol );
1436         }
1437 
1438         //	factory method is optional
1439         if (this.getFactoryMethod() != null)
1440         {
1441             result.append( "    ");
1442             result.append( tags.getAttribute(FACTORY_METHOD, this.getFactoryMethod().getName()) );
1443             result.append( eol );
1444         }
1445 
1446         //reference refresh is optional, disabled by default
1447         if (isAlwaysRefresh())
1448         {
1449             result.append( "    ");
1450             result.append( tags.getAttribute(REFRESH, "true"));
1451             result.append( eol );
1452         }
1453 
1454         result.append( "  >");
1455         result.append( eol );
1456 
1457         // end of attributes
1458 
1459         // begin of elements
1460         if (isInterface())
1461         {
1462             // extent-class
1463             for (int i = 0; i < getExtentClassNames().size(); i++)
1464             {
1465                 result.append( "      ");
1466                 result.append( tags.getOpeningTagNonClosingById(CLASS_EXTENT));
1467                 result.append( " " );
1468                 result.append( tags.getAttribute(CLASS_REF, getExtentClassNames().get(i).toString()) );
1469                 result.append( " />");
1470                 result.append( eol );
1471             }
1472         }
1473         else
1474         {
1475             // class extent is optional
1476             if (isExtent())
1477             {
1478                 for (int i = 0; i < getExtentClassNames().size(); i++)
1479                 {
1480                     result.append( "      ");
1481                     result.append( tags.getOpeningTagNonClosingById(CLASS_EXTENT));
1482                     result.append( " " );
1483                     result.append( tags.getAttribute(CLASS_REF, getExtentClassNames().get(i).toString()) );
1484                     result.append( " />");
1485                     result.append( eol );
1486                 }
1487             }
1488 
1489             // write all FieldDescriptors
1490             FieldDescriptor[] fields = getFieldDescriptions();
1491             for (int i = 0; i < fields.length; i++)
1492             {
1493                 result.append( fields[i].toXML() );
1494             }
1495 
1496             // write optional ReferenceDescriptors
1497             Vector refs = getObjectReferenceDescriptors();
1498             for (int i = 0; i < refs.size(); i++)
1499             {
1500                 result.append( ((ObjectReferenceDescriptor) refs.get(i)).toXML() );
1501             }
1502 
1503             // write optional CollectionDescriptors
1504             Vector cols = getCollectionDescriptors();
1505             for (int i = 0; i < cols.size(); i++)
1506             {
1507                 result.append( ((CollectionDescriptor) cols.get(i)).toXML() );
1508             }
1509 
1510             // write optional IndexDescriptors
1511             for (int i = 0; i < indexes.size(); i++)
1512             {
1513                 IndexDescriptor indexDescriptor = (IndexDescriptor) indexes.elementAt(i);
1514                 result.append( indexDescriptor.toXML() );
1515             }
1516 
1517             // Write out the procedures
1518             if (this.getInsertProcedure() != null)
1519             {
1520                 result.append( this.getInsertProcedure().toXML() );
1521             }
1522             if (this.getUpdateProcedure() != null)
1523             {
1524                 result.append( this.getUpdateProcedure().toXML() );
1525             }
1526             if (this.getDeleteProcedure() != null)
1527             {
1528                 result.append( this.getDeleteProcedure().toXML() );
1529             }
1530         }
1531         result.append( "  ");
1532         result.append( tags.getClosingTagById(CLASS_DESCRIPTOR) );
1533         return result.toString();
1534     }
1535 
1536     private String isolationLevelXml()
1537     {
1538         switch (this.getIsolationLevel())
1539         {
1540             case (IL_OPTIMISTIC) :
1541                 {
1542                     return LITERAL_IL_OPTIMISTIC;
1543                 }
1544             case (IL_READ_COMMITTED) :
1545                 {
1546                     return LITERAL_IL_READ_COMMITTED;
1547                 }
1548             case (IL_READ_UNCOMMITTED) :
1549                 {
1550                     return LITERAL_IL_READ_UNCOMMITTED;
1551                 }
1552             case (IL_REPEATABLE_READ) :
1553                 {
1554                     return LITERAL_IL_REPEATABLE_READ;
1555                 }
1556             case (IL_SERIALIZABLE) :
1557                 {
1558                     return LITERAL_IL_SERIALIZABLE;
1559                 }
1560             default :
1561                 {
1562                     return LITERAL_IL_READ_UNCOMMITTED;
1563                 }
1564         }
1565     }
1566 /*
1567 arminw:
1568 TODO: this feature doesn't work, so remove this in future
1569 */
1570     /**
1571      * Set name of the super class.
1572      */
1573     public void setSuperClass(String classname)
1574     {
1575         this.superClass = classname;
1576     }
1577 
1578     /**
1579      * Return the super class or <code>null</code>
1580      * if not declared in repository file.
1581      */
1582     public String getSuperClass()
1583     {
1584         return superClass;
1585     }
1586 
1587     /**
1588      * TODO drop this method?
1589      */
1590     public void setSuperClassFieldRef(int fieldId)
1591     {
1592         this.superClassFieldRef = fieldId;
1593     }
1594 
1595     /**
1596      * TODO drop this method?
1597      */
1598     public int getSuperClassFieldRef()
1599     {
1600         return superClassFieldRef;
1601     }
1602 
1603     /**
1604      * Return true, if the described class is
1605      * an interface.
1606      */
1607     public boolean isInterface()
1608     {
1609         return m_isInterface;
1610     }
1611 
1612     /**
1613      * Set <code>true</code> if described class is
1614      * a interface.
1615      */
1616     public void setIsInterface(boolean newIsInterface)
1617     {
1618         m_isInterface = newIsInterface;
1619     }
1620 
1621     /**
1622      * @return boolean true if the mapped class is abstract
1623      */
1624     public boolean isAbstract()
1625     {
1626         return isAbstract;
1627     }
1628 
1629     /**
1630      * Returns acceptLocks.
1631      * @return boolean
1632      */
1633     public boolean isAcceptLocks()
1634     {
1635         return acceptLocks;
1636     }
1637 
1638     /**
1639      * Sets acceptLocks.
1640      * @param acceptLocks The m_acceptLocks to set
1641      */
1642     public void setAcceptLocks(boolean acceptLocks)
1643     {
1644         this.acceptLocks = acceptLocks;
1645     }
1646 
1647     /**
1648      * Gets the IndexDescriptors used for DDL generation.
1649      */
1650     public Vector getIndexes()
1651     {
1652         return indexes;
1653     }
1654 
1655     /**
1656      * Sets the IndexDescriptors used for DDL generation.
1657      */
1658     public void setIndexes(Vector indexes)
1659     {
1660         this.indexes = indexes;
1661     }
1662 
1663     /**
1664      * Gets the repository.
1665      * @return Returns a DescriptorRepository
1666      */
1667     public DescriptorRepository getRepository()
1668     {
1669         return m_repository;
1670     }
1671 
1672     /**
1673      * Sets the repository.
1674      * @param repository The repository to set
1675      */
1676     public void setRepository(DescriptorRepository repository)
1677     {
1678         m_repository = repository;
1679     }
1680 
1681     /**
1682      * returns the transaction isolation level to be used for this class. Used only in the ODMG server
1683      */
1684     public int getIsolationLevel()
1685     {
1686         return m_IsolationLevel;
1687     }
1688 
1689     /**
1690      * Method declaration
1691      * @param isoLevel
1692      */
1693     public void setIsolationLevel(int isoLevel)
1694     {
1695         m_IsolationLevel = isoLevel;
1696     }
1697 
1698     /**
1699      * Method declaration
1700      * @return table name
1701      */
1702     private String getTableName()
1703     {
1704         return m_TableName;
1705     }
1706 
1707     /**
1708      * Method declaration
1709      * @param str
1710      */
1711     public void setTableName(String str)
1712     {
1713         m_TableName = str;
1714     }
1715 
1716     /**
1717      * Answer Table name including schema    BRJ
1718      */
1719     public String getFullTableName()
1720     {
1721         if (getSchema() != null)
1722             return getSchema() + "." + getTableName();
1723         else
1724             return getTableName();
1725     }
1726 
1727     /**
1728      * Gets the schema.
1729      * @return Returns a String
1730      */
1731     public String getSchema()
1732     {
1733         return schema;
1734     }
1735 
1736     /**
1737      * Sets the schema.
1738      * @param schema The schema to set
1739      */
1740     public void setSchema(String schema)
1741     {
1742         this.schema = schema;
1743     }
1744 
1745     /**
1746      * Return a string representation of this class.
1747      */
1748     public String toString()
1749     {
1750         ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
1751         return buf
1752             .append("classNameOfObject", getClassNameOfObject())
1753             .append("tableName", getTableName())
1754             .append("schema", getSchema())
1755             .append("isInterface", isInterface())
1756             .append("extendClassNames", getExtentClassNames().toString())
1757             //.append("[fieldDescriptions:")
1758             .append(getFieldDescriptions())
1759             //.append("]")
1760             .toString();
1761     }
1762 
1763     /**
1764      * sets the initialization method for this descriptor
1765      */
1766     private synchronized void setInitializationMethod(Method newMethod)
1767     {
1768         if (newMethod != null)
1769         {
1770             // make sure it's a no argument method
1771             if (newMethod.getParameterTypes().length > 0)
1772             {
1773                 throw new MetadataException(
1774                     "Initialization methods must be zero argument methods: "
1775                         + newMethod.getClass().getName()
1776                         + "."
1777                         + newMethod.getName());
1778             }
1779 
1780             // make it accessible if it's not already
1781             if (!newMethod.isAccessible())
1782             {
1783                 newMethod.setAccessible(true);
1784             }
1785         }
1786         this.initializationMethod = newMethod;
1787     }
1788 
1789     /**
1790      * sets the initialization method for this descriptor by name
1791      */
1792     public synchronized void setInitializationMethod(String newMethodName)
1793     {
1794         Method newMethod = null;
1795         if (newMethodName != null)
1796         {
1797             initializationMethodName = newMethodName;
1798             try
1799             {
1800                 // see if we have a publicly accessible method by the name
1801                 newMethod = getClassOfObject().getMethod(newMethodName, NO_PARAMS);
1802             }
1803             catch (NoSuchMethodException e)
1804             {
1805                 try
1806                 {
1807                     // no publicly accessible method, see if there is a private/protected one
1808                     newMethod = getClassOfObject().getDeclaredMethod(newMethodName, NO_PARAMS);
1809                 }
1810                 catch (NoSuchMethodException e2)
1811                 {
1812                     // there is no such method available
1813                     throw new MetadataException(
1814                         "Invalid initialization method, there is not"
1815                             + " a zero argument method named "
1816                             + newMethodName
1817                             + " on class "
1818                             + getClassOfObject().getName()
1819                             + ".");
1820                 }
1821             }
1822         }
1823         setInitializationMethod(newMethod);
1824     }
1825 
1826     /**
1827      * Returns the initialization method for this descriptor or null if no
1828      * initialization method is defined.
1829      */
1830     public synchronized Method getInitializationMethod()
1831     {
1832         if(this.initializationMethod == null)
1833         {
1834             setInitializationMethod(initializationMethodName);
1835         }
1836         return initializationMethod;
1837     }
1838 
1839     /**
1840      * if true instances of this class are always refreshed
1841      * even if they are already in the cache.
1842      * @return boolean
1843      */
1844     public boolean isAlwaysRefresh()
1845     {
1846         return alwaysRefresh;
1847     }
1848 
1849     /**
1850      * Sets the alwaysRefresh parameter.
1851      * @param alwaysRefresh The value to set
1852      */
1853     public void setAlwaysRefresh(boolean alwaysRefresh)
1854     {
1855         this.alwaysRefresh = alwaysRefresh;
1856     }
1857 
1858     public int getProxyPrefetchingLimit()
1859     {
1860         return m_ProxyPrefetchingLimit;
1861     }
1862 
1863     public void setProxyPrefetchingLimit(int proxyPrefetchingLimit)
1864     {
1865         m_ProxyPrefetchingLimit = proxyPrefetchingLimit;
1866     }
1867 
1868     /**
1869      * Return factory class.
1870      */
1871     public synchronized Class getFactoryClass()
1872     {
1873         return this.factoryClass;
1874     }
1875 
1876     /**
1877      * Return factory method.
1878      */
1879     public synchronized Method getFactoryMethod()
1880     {
1881         if(factoryMethod == null && factoryMethodName != null)
1882         {
1883             setFactoryMethod(factoryMethodName);
1884         }
1885         return this.factoryMethod;
1886     }
1887 
1888     /**
1889      * Set the object factory for class described by this
1890      * descriptor.
1891      * @see #setFactoryMethod
1892      */
1893     public synchronized void setFactoryClass(Class newClass)
1894     {
1895         this.factoryClass = newClass;
1896     }
1897 
1898     /**
1899      * @see #setFactoryClass
1900      */
1901     public void setFactoryClass(String newClass)
1902     {
1903         if (null != newClass)
1904         {
1905             try
1906             {
1907                 Class clazz = ClassHelper.getClass(newClass);
1908                 setFactoryClass(clazz);
1909             }
1910             catch (Exception e)
1911             {
1912                 // there is no such method available
1913                 throw new MetadataException("Invalid factory class: " + newClass + ".");
1914             }
1915         }
1916         else
1917         {
1918             setFactoryClass((Class) null);
1919         }
1920     }
1921 
1922     /**
1923      * Specify the method to instantiate objects
1924      * represented by this descriptor.
1925      * @see #setFactoryClass 
1926      */
1927     private synchronized void setFactoryMethod(Method newMethod)
1928     {
1929         if (newMethod != null)
1930         {
1931             // make sure it's a no argument method
1932             if (newMethod.getParameterTypes().length > 0)
1933             {
1934                 throw new MetadataException(
1935                     "Factory methods must be zero argument methods: "
1936                         + newMethod.getClass().getName()
1937                         + "."
1938                         + newMethod.getName());
1939             }
1940 
1941             // make it accessible if it's not already
1942             if (!newMethod.isAccessible())
1943             {
1944                 newMethod.setAccessible(true);
1945             }
1946         }
1947 
1948         this.factoryMethod = newMethod;
1949     }
1950 
1951     /**
1952      * sets the initialization method for this descriptor by name
1953      */
1954     public synchronized void setFactoryMethod(String factoryMethodName)
1955     {
1956         Method newMethod = null;
1957         this.factoryMethodName = factoryMethodName;
1958 
1959         if (factoryMethodName != null)
1960         {
1961             try
1962             {
1963                 // see if we have a publicly accessible method by the name
1964                 newMethod = getFactoryClass().getMethod(factoryMethodName, NO_PARAMS);
1965             }
1966             catch (NoSuchMethodException e)
1967             {
1968                 try
1969                 {
1970                     // no publicly accessible method, see if there is a private/protected one
1971                     newMethod = getFactoryClass().getDeclaredMethod(factoryMethodName, NO_PARAMS);
1972                 }
1973                 catch (NoSuchMethodException e2)
1974                 {
1975                     // there is no such method available
1976                     throw new MetadataException(
1977                         "Invalid factory method, there is not"
1978                             + " a zero argument method named "
1979                             + factoryMethodName
1980                             + " on class "
1981                             + getFactoryClass().getName()
1982                             + ".");
1983                 }
1984             }
1985         }
1986         setFactoryMethod(newMethod);
1987     }
1988 
1989     //---------------------------------------------------------------
1990     /**
1991      * Change the descriptor for the insert procedure/function.
1992      *
1993      * @param newValue the new value.
1994      */
1995     public void setInsertProcedure(InsertProcedureDescriptor newValue)
1996     {
1997         this.insertProcedure = newValue;
1998     }
1999 
2000     //---------------------------------------------------------------
2001     /**
2002      * Retrieve the descriptor for the insert procedure/function.
2003      *
2004      * @return The current value
2005      */
2006     public InsertProcedureDescriptor getInsertProcedure()
2007     {
2008         return this.insertProcedure;
2009     }
2010 
2011     //---------------------------------------------------------------
2012     /**
2013      * Change the descriptor for the update procedure/function.
2014      *
2015      * @param newValue the new value.
2016      */
2017     public void setUpdateProcedure(UpdateProcedureDescriptor newValue)
2018     {
2019         this.updateProcedure = newValue;
2020     }
2021 
2022     //---------------------------------------------------------------
2023     /**
2024      * Retrieve the descriptor for the update procedure/function.
2025      *
2026      * @return The current value
2027      */
2028     public UpdateProcedureDescriptor getUpdateProcedure()
2029     {
2030         return this.updateProcedure;
2031     }
2032 
2033     //---------------------------------------------------------------
2034     /**
2035      * Change the descriptor for the delete procedure/function.
2036      *
2037      * @param newValue the new value.
2038      */
2039     public void setDeleteProcedure(DeleteProcedureDescriptor newValue)
2040     {
2041         this.deleteProcedure = newValue;
2042     }
2043 
2044     //---------------------------------------------------------------
2045     /**
2046      * Retrieve the descriptor for the delete procedure/function.
2047      *
2048      * @return The current value
2049      */
2050     public DeleteProcedureDescriptor getDeleteProcedure()
2051     {
2052         return this.deleteProcedure;
2053     }
2054 
2055     /**
2056      * Returns the ojbConcreteClass field or <code>null</code> if none defined.
2057      */
2058     public FieldDescriptor getOjbConcreteClassField()
2059     {
2060         // if not checked before
2061         if(!ojbConcreteFieldCheckDone)
2062         {
2063             ojbConcreteClassField = getFieldDescriptorByName(OJB_CONCRETE_CLASS);
2064             ojbConcreteFieldCheckDone = true;
2065         }
2066         return ojbConcreteClassField;
2067     }
2068 
2069     public StatementsForClassIF getStatementsForClass(ConnectionManagerIF conMan)
2070     {
2071         if(statementsForClass == null)
2072         {
2073            statementsForClass = StatementsForClassFactory.getInstance().
2074                    getStatementsForClass(conMan.getConnectionDescriptor(), this);
2075         }
2076         return statementsForClass;
2077     }
2078 
2079 
2080     /**
2081      * Optional! Set the {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField}
2082      * implementation class used by this class.
2083      * @param pfClassName The full qualified class name of the
2084      * {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField}.
2085      */
2086     public void setPersistentFieldClassName(String pfClassName)
2087     {
2088 		this.persistentFieldClassName = pfClassName;
2089     }
2090 
2091 
2092     /**
2093      * Get the used {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField}
2094      * implementation name.
2095      */
2096     public String getPersistentFieldClassName()
2097     {
2098 		return persistentFieldClassName;
2099     }
2100 
2101     /**
2102      * Returns <em>true</em> if an DB Identity column field based sequence
2103      * manager was used. In that cases we will find an autoincrement field with
2104      * read-only access and return true, otherwise false.
2105      */
2106      public boolean useIdentityColumnField()
2107      {
2108          if(useIdentityColumn == 0)
2109          {
2110              useIdentityColumn = -1;
2111              FieldDescriptor[] pkFields = getPkFields();
2112              for (int i = 0; i < pkFields.length; i++)
2113              {
2114                  // to find the identity column we search for a autoincrement
2115                  // read-only field
2116                 if (pkFields[i].isAutoIncrement() && pkFields[i].isAccessReadOnly())
2117                 {
2118                     useIdentityColumn = 1;
2119                     break;
2120                 }
2121              }
2122          }
2123         return useIdentityColumn == 1;
2124      }
2125 
2126     /**
2127      * Returns all defined {@link ObjectReferenceDescriptor}.
2128      *
2129      * @param withInherited If <em>true</em> inherited super class references will be included.
2130      */
2131     public List getObjectReferenceDescriptors(boolean withInherited)
2132     {
2133         if(withInherited && getSuperClassDescriptor() != null)
2134         {
2135             List result = new ArrayList(m_ObjectReferenceDescriptors);
2136             result.addAll(getSuperClassDescriptor().getObjectReferenceDescriptors(true));
2137             return result;
2138         }
2139         else
2140         {
2141             return m_ObjectReferenceDescriptors;
2142         }
2143     }
2144 
2145     /**
2146      * Returns all defined {@link CollectionDescriptor} for
2147      * this class descriptor.
2148      *
2149      * @param withInherited If <em>true</em> inherited super class references will be included.
2150      */
2151     public List getCollectionDescriptors(boolean withInherited)
2152     {
2153         if(withInherited && getSuperClassDescriptor() != null)
2154         {
2155             List result = new ArrayList(m_CollectionDescriptors);
2156             result.addAll(getSuperClassDescriptor().getCollectionDescriptors(true));
2157             return result;
2158         }
2159         else
2160         {
2161             return m_CollectionDescriptors;
2162         }
2163     }
2164 
2165     /**
2166      * Return an array of all {@link FieldDescriptor} for this represented class, if
2167      * parameter <em>withInherited</em> is <em>true</em> all inherited descriptor
2168      * of declared super classes are included.
2169      *
2170      * @param withInherited If <em>true</em> inherited super class fields will be included.
2171      */
2172     public FieldDescriptor[] getFieldDescriptor(boolean withInherited)
2173     {
2174         if(withInherited && getSuperClassDescriptor() != null)
2175         {
2176             /*
2177             arminw: only return no-PK fields, because all PK fields are declared
2178             in sub-class too.
2179             */
2180             FieldDescriptor[] superFlds = getSuperClassDescriptor().getFieldDescriptorNonPk(true);
2181             if(m_FieldDescriptions == null)
2182             {
2183                 m_FieldDescriptions = new FieldDescriptor[0];
2184             }
2185             FieldDescriptor[] result = new FieldDescriptor[m_FieldDescriptions.length + superFlds.length];
2186             System.arraycopy(m_FieldDescriptions, 0, result, 0, m_FieldDescriptions.length);
2187             System.arraycopy(superFlds, 0, result, m_FieldDescriptions.length, superFlds.length);
2188             // System.out.println("all fields: " + ArrayUtils.toString(result));
2189             return result;
2190         }
2191         else
2192         {
2193             return m_FieldDescriptions;
2194         }
2195     }
2196 
2197     /**
2198      * Return an array of NON-PK {@link FieldDescriptor}, if parameter <em>withInherited</em>
2199      * is <em>true</em> all inherited descriptor of declared super classes are included.
2200      *
2201      * @param withInherited If <em>true</em> inherited super class fields will be included.
2202      */
2203     public FieldDescriptor[] getFieldDescriptorNonPk(boolean withInherited)
2204     {
2205         if(withInherited && getSuperClassDescriptor() != null)
2206         {
2207             FieldDescriptor[] flds = getNonPkFields();
2208             FieldDescriptor[] superFlds = getSuperClassDescriptor().getFieldDescriptorNonPk(true);
2209             FieldDescriptor[] result = new FieldDescriptor[flds.length + superFlds.length];
2210             System.arraycopy(flds, 0, result, 0, flds.length);
2211             System.arraycopy(superFlds, 0, result, flds.length, superFlds.length);
2212             return result;
2213         }
2214         else
2215         {
2216             return getNonPkFields();
2217         }
2218     }
2219 
2220     /**
2221      * Returns the {@link SuperReferenceDescriptor} of this class or <em>null</em>
2222      * if none was used.
2223      *
2224      * @return The reference descriptor for the <em>super</em>-reference or <em>null</em>
2225      * if not exists.
2226      */
2227     public SuperReferenceDescriptor getSuperReference()
2228     {
2229         return (SuperReferenceDescriptor) getObjectReferenceDescriptorByName(SuperReferenceDescriptor.SUPER_FIELD_INTERNAL_NAME);
2230     }
2231 }