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.lang.reflect.Constructor;
020    import java.lang.reflect.Method;
021    import java.lang.reflect.Modifier;
022    import java.sql.Timestamp;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.HashMap;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Vector;
032    
033    import org.apache.commons.lang.builder.ToStringBuilder;
034    import org.apache.commons.lang.builder.ToStringStyle;
035    import org.apache.ojb.broker.PersistenceBrokerException;
036    import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
037    import org.apache.ojb.broker.accesslayer.RowReader;
038    import org.apache.ojb.broker.accesslayer.RowReaderDefaultImpl;
039    import org.apache.ojb.broker.accesslayer.StatementsForClassFactory;
040    import org.apache.ojb.broker.accesslayer.StatementsForClassIF;
041    import org.apache.ojb.broker.core.ValueContainer;
042    import org.apache.ojb.broker.locking.IsolationLevels;
043    import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
044    import org.apache.ojb.broker.util.ClassHelper;
045    import org.apache.ojb.broker.util.SqlHelper;
046    import org.apache.ojb.broker.util.configuration.Configuration;
047    import org.apache.ojb.broker.util.configuration.Configurator;
048    import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
049    import org.apache.ojb.broker.util.logging.LoggerFactory;
050    
051    
052    /**
053     * A ClassDescriptor contains all information for mapping objects of a
054     * given class to database tables.
055     * <br>
056     * Note: Be careful when use ClassDescriptor variables or caching
057     * ClassDescriptor instances, because instances could become invalid
058     * during runtime (see {@link MetadataManager}).
059     *
060     * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
061     * @version $Id: ClassDescriptor.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
062     */
063    public final class ClassDescriptor extends DescriptorBase
064        implements Serializable, XmlCapable, IsolationLevels
065    {
066            private String persistentFieldClassName;
067    
068        private static final long serialVersionUID = -5212253607374173965L;
069    
070        public static final String DYNAMIC_STR = "dynamic";
071        public static final String OJB_CONCRETE_CLASS = "ojbConcreteClass";
072        private static final Class[] NO_PARAMS = {};
073    
074        //---------------------------------------------------------------
075        /**
076         * The descriptor for the insert procedure/function.
077         */
078        private InsertProcedureDescriptor insertProcedure;
079    
080        //---------------------------------------------------------------
081        /**
082         * The descriptor for the update procedure/function.
083         */
084        private UpdateProcedureDescriptor updateProcedure;
085    
086        //---------------------------------------------------------------
087        /**
088         * The descriptor for the delete procedure/function.
089         */
090        private DeleteProcedureDescriptor deleteProcedure;
091    
092        //---------------------------------------------------------------
093        // transient fields, to make this class serializable we have to declare
094        // some transient fields and some associated string fields to reinitialze
095        // transient fields after serialization
096        //---------------------------------------------------------------
097        /**
098         * optional method to be invoked after instance fields are initialized
099         */
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    }