KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > triactive > jdo > model > ClassMetaData


1 /*
2  * Copyright 2004 (C) TJDO.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the TJDO License version 1.0.
6  * See the terms of the TJDO License in the documentation provided with this software.
7  *
8  * $Id: ClassMetaData.java,v 1.10 2004/01/18 03:01:06 jackknifebarber Exp $
9  */

10
11 package com.triactive.jdo.model;
12
13 import com.triactive.jdo.ClassNotPersistenceCapableException;
14 import com.triactive.jdo.util.MacroString;
15 import com.triactive.jdo.util.XMLHelper;
16 import java.lang.reflect.Field JavaDoc;
17 import java.lang.reflect.Modifier JavaDoc;
18 import java.net.URL JavaDoc;
19 import java.util.Arrays JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.WeakHashMap JavaDoc;
29 import javax.jdo.JDOFatalInternalException;
30 import javax.jdo.JDOFatalUserException;
31 import javax.jdo.JDOUserException;
32 import javax.jdo.spi.JDOImplHelper;
33 import javax.jdo.spi.PersistenceCapable;
34 import org.w3c.dom.Element JavaDoc;
35 import org.w3c.dom.Node JavaDoc;
36 import org.w3c.dom.NodeList JavaDoc;
37 import org.apache.log4j.Category;
38
39
40 public final class ClassMetaData extends MetaData
41 {
42     private static final Category LOG = Category.getInstance(ClassMetaData.class);
43
44     private static Map JavaDoc metaDataByClass = new WeakHashMap JavaDoc();
45     private static Map JavaDoc searchedPathsByClassLoader = new WeakHashMap JavaDoc();
46
47     public static synchronized ClassMetaData forClass(Class JavaDoc c)
48     {
49         if (c.isArray() || c.isInterface() || c.isPrimitive())
50             return null;
51
52         if (metaDataByClass.containsKey(c))
53             return (ClassMetaData)metaDataByClass.get(c);
54
55         ClassLoader JavaDoc cl = c.getClassLoader();
56
57         if (cl == null)
58             cl = ClassLoader.getSystemClassLoader();
59
60         Set JavaDoc searchedPaths = (Set JavaDoc)searchedPathsByClassLoader.get(cl);
61
62         if (searchedPaths == null)
63         {
64             searchedPaths = new HashSet JavaDoc();
65             searchedPathsByClassLoader.put(cl, searchedPaths);
66         }
67
68         ClassMetaData cmd = null;
69         Iterator JavaDoc i = possiblePathsFor(c).iterator();
70
71         while (cmd == null && i.hasNext())
72         {
73             String JavaDoc path = (String JavaDoc)i.next();
74
75             if (!searchedPaths.contains(path))
76             {
77                 if (LOG.isDebugEnabled())
78                     LOG.debug("Searching for metadata at " + path + " using " + cl);
79
80                 URL JavaDoc url = cl.getResource(path);
81
82                 if (url != null)
83                     cmd = loadMetadataFileLookingFor(url, c, cl);
84
85                 searchedPaths.add(path);
86             }
87         }
88
89         if (cmd != null)
90             cmd.assertIsValidated();
91
92         return cmd;
93     }
94
95     private static List JavaDoc possiblePathsFor(Class JavaDoc c)
96     {
97         ArrayList JavaDoc paths = new ArrayList JavaDoc();
98
99         paths.add("META-INF/package.jdo");
100         paths.add("WEB-INF/package.jdo");
101         paths.add("package.jdo");
102
103         String JavaDoc path = c.getName().replace('.', '/');
104
105         for (int slash = 0; (slash = path.indexOf('/', slash)) >= 0;)
106             paths.add(path.substring(0, ++slash) + "package.jdo");
107
108         paths.add(path + ".jdo");
109
110         /* Add 1.0-style naming convention to the end of the search list. */
111         for (int slash = path.length() - 1; (slash = path.lastIndexOf('/', slash)) >= 0;)
112             paths.add(path.substring(0, slash--) + ".jdo");
113
114         return paths;
115     }
116
117     private static ClassMetaData loadMetadataFileLookingFor(URL JavaDoc url, Class JavaDoc targetClass, ClassLoader JavaDoc cl)
118     {
119         if (LOG.isDebugEnabled())
120             LOG.debug("Loading metadata from " + url);
121
122         ClassMetaData found = null;
123         Element JavaDoc docElement;
124
125         try
126         {
127             docElement = XMLHelper.getDocumentBuilder().parse(url.openStream()).getDocumentElement();
128         }
129         catch (Exception JavaDoc e)
130         {
131             throw new XMLMetaDataException(url, "Could not access or parse metadata file", e);
132         }
133
134         NodeList JavaDoc nodes = docElement.getElementsByTagName("class");
135
136         for (int i = 0; i < nodes.getLength(); ++i)
137         {
138             Element JavaDoc clsElement = (Element JavaDoc)nodes.item(i);
139             String JavaDoc packageName = ((Element JavaDoc)clsElement.getParentNode()).getAttribute("name");
140             String JavaDoc className = packageName + '.' + clsElement.getAttribute("name");
141             Class JavaDoc c;
142
143             try
144             {
145                 /*
146                  * Important that 'true' is passed here so that each class is
147                  * initialized. That's needed so that the static initialization
148                  * block registers with JDOImplHelper, which will be consulted
149                  * during ClassMetaData validation.
150                  */

151                 c = Class.forName(className, true, cl);
152             }
153             catch (ClassNotFoundException JavaDoc e)
154             {
155                 throw new XMLMetaDataException(url, "Class" + className + " not found", e);
156             }
157
158             if (!metaDataByClass.containsKey(c))
159             {
160                 ClassMetaData cmd = new ClassMetaData(c, url, clsElement);
161                 metaDataByClass.put(c, cmd);
162
163                 if (c.equals(targetClass))
164                     found = cmd;
165             }
166         }
167
168         return found;
169     }
170
171
172     public static final byte
173         NEW = 0,
174         CONSTRUCTED = 1,
175         VALIDATED = 2;
176
177     public static final int
178         NO_IDENTITY = 0,
179         DATASTORE_IDENTITY = 1,
180         APPLICATION_IDENTITY = 2;
181
182     private static final List JavaDoc identityTypeValues = Arrays.asList(new String JavaDoc[]
183         {"nondurable", "datastore", "application"});
184
185
186     private final Class JavaDoc clazz;
187     private final ClassLoader JavaDoc loader;
188     private final String JavaDoc packageName;
189     private final URL JavaDoc loadedFrom;
190     private final int identityType;
191     private final Class JavaDoc identityClass;
192     private final boolean requiresExtent;
193     private final Class JavaDoc pcSuperclass;
194     private final List JavaDoc managedFields = new ArrayList JavaDoc();
195     private final Map JavaDoc fieldNumbersByName = new HashMap JavaDoc();
196
197     private byte state = NEW;
198     private boolean validating = false;
199
200     /* Fields below are only valid after state == VALIDATED. */
201     private ClassMetaData pcSuperclassMetaData;
202     private int inheritedFieldCount;
203     private int totalFieldCount;
204     private int[] allFieldNumbers;
205     private int[] transactionalFieldNumbers;
206     private int[] persistentFieldNumbers;
207     private int[] defaultFetchGroupFieldNumbers;
208     private int[] secondClassMutableFieldNumbers;
209     private boolean[] transactionalFieldFlags;
210     private boolean[] persistentFieldFlags;
211     private boolean[] defaultFetchGroupFieldFlags;
212     private boolean[] secondClassMutableFieldFlags;
213
214
215     private ClassMetaData(Class JavaDoc clazz, URL JavaDoc loadedFrom, Element JavaDoc clsElement)
216     {
217         super(loadedFrom, clsElement);
218
219         String JavaDoc className = clazz.getName();
220         int lastDot = className.lastIndexOf('.');
221
222         this.clazz = clazz;
223         this.loader = clazz.getClassLoader();
224         this.packageName = lastDot < 0 ? "" : className.substring(0, lastDot);
225         this.loadedFrom = loadedFrom;
226
227         /* Process the "identity-type" attribute. */
228         String JavaDoc idTypeAttr = clsElement.getAttribute("identity-type");
229
230         if (idTypeAttr.length() > 0)
231         {
232             identityType = identityTypeValues.indexOf(idTypeAttr);
233
234             if (identityType < 0)
235                 throw new XMLMetaDataException(loadedFrom, "Unrecognized identity-type " + idTypeAttr);
236         }
237         else
238             identityType = DATASTORE_IDENTITY;
239
240         if (identityType == APPLICATION_IDENTITY)
241         {
242             /* Process the "objectid-class" attribute. */
243             String JavaDoc objIDClassName = clsElement.getAttribute("objectid-class");
244             if (objIDClassName.length() == 0)
245                 throw new XMLMetaDataException(loadedFrom, "Missing objectid-class attribute for class " + clazz.getName());
246
247             if (objIDClassName.indexOf('.') < 0)
248                 objIDClassName = packageName + '.' + objIDClassName;
249
250             try
251             {
252                 identityClass = Class.forName(objIDClassName, true, loader);
253             }
254             catch (ClassNotFoundException JavaDoc e)
255             {
256                 throw new XMLMetaDataException(loadedFrom, "Object ID class for class" + clazz.getName() + " not found", e);
257             }
258         }
259         else
260             identityClass = null;
261
262         /* Process the "requires-extent" attribute. */
263         String JavaDoc requiresExtentAttr = clsElement.getAttribute("requires-extent");
264         if (requiresExtentAttr.length() > 0)
265             requiresExtent = Boolean.valueOf(requiresExtentAttr).booleanValue();
266         else
267             requiresExtent = true;
268
269         /* Process the "persistence-capable-superclass" attribute. */
270         String JavaDoc pcSuperclassName = clsElement.getAttribute("persistence-capable-superclass");
271         if (pcSuperclassName.length() > 0)
272         {
273             Class JavaDoc c;
274
275             try
276             {
277                 c = Class.forName(pcSuperclassName, true, loader);
278             }
279             catch (ClassNotFoundException JavaDoc e)
280             {
281                 try
282                 {
283                     c = Class.forName(packageName + '.' + pcSuperclassName, true, loader);
284                 }
285                 catch (ClassNotFoundException JavaDoc e1)
286                 {
287                     throw new XMLMetaDataException(loadedFrom, "Class not found", e1);
288                 }
289             }
290
291             pcSuperclass = c;
292
293             if (pcSuperclass.equals(clazz) || !pcSuperclass.isAssignableFrom(clazz))
294                 throw new XMLMetaDataException(loadedFrom, "Specified persistence capable superclass " + pcSuperclass.getName() + " is not a superclass of " + clazz.getName());
295         }
296         else
297             pcSuperclass = null;
298
299         /* Process "field" child elements. */
300         for (Node JavaDoc node = clsElement.getFirstChild(); node != null; node = node.getNextSibling())
301         {
302             if (node instanceof Element JavaDoc)
303             {
304                 Element JavaDoc child = (Element JavaDoc)node;
305                 String JavaDoc childTag = child.getTagName();
306
307                 if (childTag.equals("field"))
308                     addField(new FieldMetaData(this, child));
309             }
310         }
311
312         /*
313          * Add any eligible fields that are declared in the class but not in
314          * its metadata.
315          */

316         Field JavaDoc declaredFields[] = clazz.getDeclaredFields();
317
318         for (int i = 0; i < declaredFields.length; ++i)
319         {
320             Field JavaDoc f = declaredFields[i];
321
322             if (!fieldNumbersByName.containsKey(f.getName()))
323             {
324                 int m = f.getModifiers();
325
326                 if (!Modifier.isFinal(m) &&
327                     !Modifier.isStatic(m) &&
328                     !Modifier.isTransient(m) &&
329                     Types.isDefaultPersistentType(f.getType()))
330                 {
331                     addField(new FieldMetaData(this, f));
332                 }
333             }
334         }
335
336         /*
337          * Sort the field list by field name, then create the field name to
338          * field number map.
339          */

340         Collections.sort(managedFields);
341         Iterator JavaDoc iter = managedFields.iterator();
342
343         for (int i = 0; iter.hasNext(); ++i)
344         {
345             FieldMetaData fmd = (FieldMetaData)iter.next();
346             fieldNumbersByName.put(fmd.getName(), new Integer JavaDoc(i));
347         }
348
349         state = CONSTRUCTED;
350     }
351
352     /** Helper method used during only construction. */
353     private void addField(FieldMetaData fm)
354     {
355         String JavaDoc name = fm.getName();
356         if (fieldNumbersByName.containsKey(name))
357             throw new DuplicateFieldException(loadedFrom, "Duplicate field name " + name + " in class " + clazz.getName());
358
359         fieldNumbersByName.put(name, null);
360
361         if (fm.getPersistenceModifier() != FieldMetaData.PERSISTENCE_MODIFIER_NONE)
362             managedFields.add(fm);
363     }
364
365     private synchronized void assertIsConstructed()
366     {
367         if (state < CONSTRUCTED)
368             throw new JDOFatalInternalException("Object not yet constructed: " + this);
369     }
370
371     public String JavaDoc getJavaName()
372     {
373         return clazz.getName();
374     }
375
376     public Class JavaDoc getPCClass()
377     {
378         return clazz;
379     }
380
381     public String JavaDoc getPackageName()
382     {
383         return packageName;
384     }
385
386     public URL JavaDoc getSourceURL()
387     {
388         return loadedFrom;
389     }
390
391     public Class JavaDoc getPCSuperclass()
392     {
393         return pcSuperclass;
394     }
395
396     public int getIdentityType()
397     {
398         return identityType;
399     }
400
401     public Class JavaDoc getIdentityClass()
402     {
403         return identityClass;
404     }
405
406     public boolean requiresExtent()
407     {
408         return requiresExtent;
409     }
410
411     public int getFieldCount()
412     {
413         assertIsConstructed();
414         return managedFields.size();
415     }
416
417     public FieldMetaData getFieldRelative(int relativeFieldNumber)
418     {
419         assertIsConstructed();
420         return (FieldMetaData)managedFields.get(relativeFieldNumber);
421     }
422
423     public int getRelativeFieldNumber(String JavaDoc name)
424     {
425         assertIsConstructed();
426         Integer JavaDoc i = (Integer JavaDoc)fieldNumbersByName.get(name);
427
428         return i == null ? -1 : i.intValue();
429     }
430
431     public String JavaDoc getViewImports()
432     {
433         return getVendorExtension(MY_VENDOR, "view-imports");
434     }
435
436     public String JavaDoc getViewDefinition(String JavaDoc vendorID)
437     {
438         String JavaDoc viewDef = null;
439
440         if (vendorID != null)
441             viewDef = getVendorExtension(MY_VENDOR, "view-definition" + '-' + vendorID);
442
443         if (viewDef == null)
444             viewDef = getVendorExtension(MY_VENDOR, "view-definition");
445
446         return viewDef;
447     }
448
449     private synchronized boolean assertIsValidated()
450     {
451         assertIsConstructed();
452
453         if (state < VALIDATED)
454         {
455             if (validating)
456                 throw new JDOFatalInternalException("assertIsValidated() called recursively");
457
458             validating = true;
459
460             try
461             {
462                 validateAgainstClass();
463             }
464             finally
465             {
466                 validating = false;
467             }
468
469             return true;
470         }
471         else
472             return false;
473     }
474
475     private void validateAgainstClass()
476     {
477         /*
478          * If the class has already been enhanced, verify that it agrees with
479          * the current metadata.
480          */

481         if (Types.isEnhancedClass(clazz))
482         {
483             JDOImplHelper helper = JDOImplHelper.getInstance();
484             String JavaDoc[] fieldNames = helper.getFieldNames(clazz);
485             Class JavaDoc[] fieldTypes = helper.getFieldTypes(clazz);
486             byte[] fieldFlags = helper.getFieldFlags(clazz);
487
488             if (fieldNames.length != managedFields.size())
489                 throw new ClassMetaDataMismatchException(clazz, " class has " + fieldNames.length + " fields, metadata has " + managedFields.size() + " fields");
490
491             for (int i = 0; i < fieldNames.length; ++i)
492             {
493                 FieldMetaData fmd = (FieldMetaData)managedFields.get(i);
494
495                 if (!fieldNames[i].equals(fmd.getName()))
496                     throw new ClassMetaDataMismatchException(clazz, " class has '" + fieldNames[i] + "' for field " + i + ", metadata has '" + fmd.getName() + "'");
497
498                 if (!fieldTypes[i].equals(fmd.getType()))
499                     throw new ClassMetaDataMismatchException(clazz, " class has type " + fieldTypes[i].getName() + " for field " + i + ", metadata has " + fmd.getType().getName());
500
501                 /*
502                  * Verify that our expectations re. the field flags agree with
503                  * those of the enhanced class.
504                  *
505                  * Enhancers are notorious for getting the "default" value for
506                  * default-fetch-group, i.e. the value to use if nothing is
507                  * given in the metadata, wrong sometimes. So for this one case
508                  * we tolerate a mismatch between enhanced class and metadata
509                  * and use the setting indicated by the enhanced class.
510                  */

511                 byte expectedFlag;
512                 byte actualFlag = (byte)(fieldFlags[i] & (PersistenceCapable.CHECK_READ |
513                                                           PersistenceCapable.CHECK_WRITE |
514                                                           PersistenceCapable.MEDIATE_READ |
515                                                           PersistenceCapable.MEDIATE_WRITE));
516
517                 if (fmd.getPersistenceModifier() == FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL)
518                 {
519                     expectedFlag = PersistenceCapable.CHECK_WRITE;
520                 }
521                 else if (fmd.isPrimaryKeyPart())
522                 {
523                     expectedFlag = PersistenceCapable.MEDIATE_WRITE;
524                 }
525                 else if (fmd.isInDefaultFetchGroup())
526                 {
527                     expectedFlag = PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE;
528
529                     /*
530                      * Metadata says DFG=true. If the enhanced class disagrees,
531                      * go with the enhanced class.
532                      */

533                     if (actualFlag == (PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE))
534                     {
535                         LOG.warn(new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag).getMessage() + ": will use the value in class (non-default fetch group)");
536
537                         expectedFlag = PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE;
538                         fmd.setDefaultFetchGroup(false);
539                     }
540                 }
541                 else
542                 {
543                     expectedFlag = PersistenceCapable.MEDIATE_READ | PersistenceCapable.MEDIATE_WRITE;
544
545                     /*
546                      * Metadata says DFG=false. If the enhanced class disagrees,
547                      * go with the enhanced class.
548                      */

549                     if (actualFlag == (PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE))
550                     {
551                         LOG.warn(new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag).getMessage() + ": will use the value in class (default fetch group)");
552
553                         expectedFlag = PersistenceCapable.CHECK_READ | PersistenceCapable.CHECK_WRITE;
554                         fmd.setDefaultFetchGroup(true);
555                     }
556                 }
557
558                 if (expectedFlag != actualFlag)
559                     throw new ClassMetaDataFlagMismatchException(clazz, i, expectedFlag, actualFlag);
560             }
561         }
562
563         if (pcSuperclass != null)
564         {
565             pcSuperclassMetaData = forClass(pcSuperclass);
566
567             if (pcSuperclassMetaData == null)
568                 throw new XMLMetaDataException(loadedFrom, "Specified persistence capable superclass " + pcSuperclass.getName() + " is not persistence capable");
569
570             inheritedFieldCount = pcSuperclassMetaData.getInheritedFieldCount() + pcSuperclassMetaData.getFieldCount();
571         }
572         else
573             inheritedFieldCount = 0;
574
575         totalFieldCount = inheritedFieldCount + managedFields.size();
576         allFieldNumbers = new int[totalFieldCount];
577         transactionalFieldFlags = new boolean[totalFieldCount];
578         persistentFieldFlags = new boolean[totalFieldCount];
579         defaultFetchGroupFieldFlags = new boolean[totalFieldCount];
580         secondClassMutableFieldFlags = new boolean[totalFieldCount];
581         int transactionalFieldCount = 0;
582         int persistentFieldCount = 0;
583         int defaultFetchGroupFieldCount = 0;
584         int secondClassMutableFieldCount = 0;
585
586         for (int i = 0; i < totalFieldCount; ++i)
587         {
588             allFieldNumbers[i] = i;
589
590             FieldMetaData fmd = getFieldAbsoluteInternal(i);
591
592             if (fmd.getPersistenceModifier() == FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL)
593             {
594                 transactionalFieldFlags[i] = true;
595                 ++transactionalFieldCount;
596             }
597             else
598             {
599                 persistentFieldFlags[i] = true;
600                 ++persistentFieldCount;
601
602                 if (fmd.isInDefaultFetchGroup())
603                 {
604                     defaultFetchGroupFieldFlags[i] = true;
605                     ++defaultFetchGroupFieldCount;
606                 }
607
608                 if (Types.isSecondClassMutableType(fmd.getType()))
609                 {
610                     secondClassMutableFieldFlags[i] = true;
611                     ++secondClassMutableFieldCount;
612                 }
613             }
614         }
615
616         transactionalFieldNumbers = new int[transactionalFieldCount];
617         persistentFieldNumbers = new int[persistentFieldCount];
618         defaultFetchGroupFieldNumbers = new int[defaultFetchGroupFieldCount];
619         secondClassMutableFieldNumbers = new int[secondClassMutableFieldCount];
620
621         for (int i = 0, txn = 0, per = 0, dfg = 0, scm = 0; i < totalFieldCount; ++i)
622         {
623             if (transactionalFieldFlags[i])
624                 transactionalFieldNumbers[txn++] = i;
625
626             if (persistentFieldFlags[i])
627                 persistentFieldNumbers[per++] = i;
628
629             if (defaultFetchGroupFieldFlags[i])
630                 defaultFetchGroupFieldNumbers[dfg++] = i;
631
632             if (secondClassMutableFieldFlags[i])
633                 secondClassMutableFieldNumbers[scm++] = i;
634         }
635
636         state = VALIDATED;
637     }
638
639     private FieldMetaData getFieldAbsoluteInternal(int absoluteFieldNumber)
640     {
641         if (absoluteFieldNumber < inheritedFieldCount)
642         {
643             if (pcSuperclassMetaData == null)
644                 return null;
645             else
646                 return pcSuperclassMetaData.getFieldAbsoluteInternal(absoluteFieldNumber);
647         }
648         else
649             return (FieldMetaData)managedFields.get(absoluteFieldNumber - inheritedFieldCount);
650     }
651
652     public int getInheritedFieldCount()
653     {
654         assertIsValidated();
655         return inheritedFieldCount;
656     }
657
658     public FieldMetaData getFieldAbsolute(int absoluteFieldNumber)
659     {
660         assertIsValidated();
661         return getFieldAbsoluteInternal(absoluteFieldNumber);
662     }
663
664     public int getAbsoluteFieldNumber(String JavaDoc name)
665     {
666         assertIsValidated();
667         int lastDot = name.lastIndexOf('.');
668
669         if (lastDot >= 0)
670         {
671             /* Field name is of the form "com.acme.MyClass.myField". */
672             Class JavaDoc c;
673
674             try
675             {
676                 c = Class.forName(name.substring(0, lastDot), true, loader);
677             }
678             catch (ClassNotFoundException JavaDoc e)
679             {
680                 throw new JDOUserException("Class in field name not found: " + name, e);
681             }
682
683             ClassMetaData cmd = ClassMetaData.forClass(c);
684
685             if (cmd == null)
686                 throw new ClassNotPersistenceCapableException(c);
687
688             if (!c.isAssignableFrom(clazz))
689                 throw new JDOUserException("Class in field name " + name + " not compatible with actual class " + clazz.getName());
690
691             return cmd.getAbsoluteFieldNumber(name.substring(lastDot + 1));
692         }
693         else
694         {
695             /* Field name is of the form "myField" */
696             int i = getRelativeFieldNumber(name);
697
698             if (i < 0)
699             {
700                 if (pcSuperclassMetaData != null)
701                     i = pcSuperclassMetaData.getAbsoluteFieldNumber(name);
702             }
703             else
704                 i += inheritedFieldCount;
705
706             return i;
707         }
708     }
709
710     public int[] getAllFieldNumbers()
711     {
712         assertIsValidated();
713         return allFieldNumbers;
714     }
715
716     public int[] getTransactionalFieldNumbers()
717     {
718         assertIsValidated();
719         return transactionalFieldNumbers;
720     }
721
722     public int[] getPersistentFieldNumbers()
723     {
724         assertIsValidated();
725         return persistentFieldNumbers;
726     }
727
728     public int[] getDefaultFetchGroupFieldNumbers()
729     {
730         assertIsValidated();
731         return defaultFetchGroupFieldNumbers;
732     }
733
734     public int[] getSecondClassMutableFieldNumbers()
735     {
736         assertIsValidated();
737         return secondClassMutableFieldNumbers;
738     }
739
740     public boolean[] getTransactionalFieldFlags()
741     {
742         assertIsValidated();
743         return transactionalFieldFlags;
744     }
745
746     public boolean[] getPersistentFieldFlags()
747     {
748         assertIsValidated();
749         return persistentFieldFlags;
750     }
751
752     public boolean[] getDefaultFetchGroupFieldFlags()
753     {
754         assertIsValidated();
755         return defaultFetchGroupFieldFlags;
756     }
757
758     public boolean[] getSecondClassMutableFieldFlags()
759     {
760         assertIsValidated();
761         return secondClassMutableFieldFlags;
762     }
763
764     public List JavaDoc getReferencedClasses(String JavaDoc vendorID)
765     {
766         Set JavaDoc referenced = new HashSet JavaDoc();
767         List JavaDoc orderedMetaData = new ArrayList JavaDoc();
768
769         getReferencedClasses(vendorID, orderedMetaData, referenced);
770
771         return orderedMetaData;
772     }
773
774     /**
775      * Get the ordered <code>ClassMetaData</code>s for classes referenced
776      * from this <code>ClassMetaData</code>. This will add the
777      * <code>ClassMetaData</code>s to <code>orderedCmds</code> ordered by
778      * dependency, and to <code>referenced</code> for fast lookups.
779      * <p>
780      * This method uses recursion to add all referenced
781      * <code>ClassMetaData</code> for any fields, identity classes,
782      * super classes, and classes referenced by a view definition.
783      *
784      * @param vendorID The vendorID for the database. This is used to
785      * get the appropriate view definition.
786      * @param orderedCmds A List that all ordered <code>ClassMetaData</code>s
787      * will be added to.
788      * @param referenced A Set that all <code>ClassMetaData</code>s are
789      * added to. This is used for fast lookups with contains().
790      */

791     void getReferencedClasses(final String JavaDoc vendorID, final List JavaDoc orderedCmds,
792                               final Set JavaDoc referenced)
793     {
794         assertIsValidated();
795         Map JavaDoc viewReferences = new HashMap JavaDoc();
796         getReferencedClasses(vendorID, orderedCmds, referenced, viewReferences);
797     }
798
799
800     /**
801      * Get the ordered <code>ClassMetaData</code>s for classes referenced
802      * from this <code>ClassMetaData</code>. This will add the
803      * <code>ClassMetaData</code>s to <code>orderedCmds</code> ordered by
804      * dependency, and to <code>referenced</code> for fast lookups.
805      * <p>
806      * This method uses recursion to add all referenced
807      * <code>ClassMetaData</code> for any fields, identity classes,
808      * super classes, and classes referenced by a view definition.
809      *
810      * @param vendorID The vendorID for the database. This is used to
811      * get the appropriate view definition.
812      * @param orderedCmds A List that all ordered <code>ClassMetaData</code>s
813      * will be added to.
814      * @param referenced A Set that all <code>ClassMetaData</code>s are
815      * added to. This is used for fast lookups with contains().
816      * @param viewReferences A Map mapping Class to a Set of referenced
817      * classes for all views.
818      */

819     private void getReferencedClasses(final String JavaDoc vendorID, final List JavaDoc orderedCmds,
820                                       final Set JavaDoc referenced, final Map JavaDoc viewReferences)
821     {
822         /*
823          * Recursively call getReferencedClasses(...) before adding them
824          * to the orderedCmds and referenced. This will ensure that any
825          * classes with dependencies on them are put in the orderedCmds List
826          * in the correct order.
827          */

828         if (!referenced.contains(this))
829         {
830             /*
831              * Go ahead and add this class to the referenced Set, it will
832              * get added to the orderedCmds List after all classes that this
833              * depends on have been added.
834              */

835             referenced.add(this);
836
837             Iterator JavaDoc iter = managedFields.iterator();
838
839             while (iter.hasNext())
840             {
841                 FieldMetaData fmd = (FieldMetaData)iter.next();
842                 fmd.getReferencedClasses(vendorID, orderedCmds, referenced);
843             }
844
845             if (pcSuperclass != null)
846             {
847                 forClass(pcSuperclass).getReferencedClasses(vendorID, orderedCmds, referenced);
848             }
849
850             if (identityClass != null)
851             {
852                 ClassMetaData icmd = forClass(identityClass);
853                 if (icmd != null)
854                     icmd.getReferencedClasses(vendorID, orderedCmds, referenced);
855             }
856
857             if (getViewDefinition(vendorID) != null)
858             {
859                 MacroString viewDef = new MacroString(this.clazz, getViewImports(),
860                                                       getViewDefinition(vendorID));
861                 viewDef.substituteMacros(new MacroString.MacroHandler()
862                     {
863                         public void onIdentifierMacro(MacroString.IdentifierMacro im)
864                         {
865                             addViewReference(viewReferences, im.clazz);
866                             forClass(im.clazz).getReferencedClasses(vendorID, orderedCmds, referenced, viewReferences);
867                         }
868
869                         public void onParameterMacro(MacroString.ParameterMacro pm)
870                         {
871                             throw new JDOUserException("Parameter macros not allowed in view definitions: " + pm);
872                         }
873                     }
874                 );
875             }
876
877             orderedCmds.add(this);
878         }
879     }
880
881
882     /**
883      * Add a reference from this class to the referenced class. Check the
884      * view references for circular dependencies. If there are any circular
885      * dependencies, throw a JDOUserException.
886      * @param viewReferences The Map of Class to Set of referenced Classes
887      * to add the reference to.
888      * @exception JDOFatalUserException If a circular reference is found in
889      * the view definitions.
890      */

891     private void addViewReference(Map JavaDoc viewReferences, Class JavaDoc referenced)
892         throws JDOFatalUserException
893     {
894         if (this.clazz != referenced)
895         {
896             /*
897              * Add this reference to the Map.
898              */

899             Set JavaDoc referencedSet = (Set JavaDoc)viewReferences.get(this.clazz);
900             if (null == referencedSet)
901             {
902                 referencedSet = new HashSet JavaDoc();
903                 viewReferences.put(this.clazz, referencedSet);
904             }
905
906             referencedSet.add(referenced);
907
908
909             /*
910              * Check to see if there is a circular dependency. This will
911              * be true if the referenced class references this class.
912              */

913             checkForCircularReferences(viewReferences, this.clazz, referenced, null);
914         }
915     }
916
917
918     /**
919      * Check for any circular references between referencer and referencee.
920      * If one is found, throw a JDOFatalUserException with the chain of references.
921      * @param viewReferences The Map of view references to check.
922      * @param referencer The class that has the reference.
923      * @param referencee The class that is being referenced.
924      * @param referenceChain The List of class that have been referenced so far.
925      * @exception JDOFatalUserException If a circular reference is found in
926      * the view definitions.
927      */

928     private void checkForCircularReferences(Map JavaDoc viewReferences, Class JavaDoc referencer,
929                                             Class JavaDoc referencee, List JavaDoc referenceChain)
930         throws JDOFatalUserException
931     {
932         Set JavaDoc classes = (Set JavaDoc)viewReferences.get(referencee);
933         if (null != classes)
934         {
935             /*
936              * Initialize the chain of references if needed. Add the referencee
937              * to the chain.
938              */

939             if (null == referenceChain)
940             {
941                 referenceChain = new ArrayList JavaDoc();
942                 referenceChain.add(referencer);
943             }
944             referenceChain.add(referencee);
945
946
947             /*
948              * Iterate through all referenced classes from the referencee. If
949              * any reference the referencer, throw an exception.
950              */

951             for (Iterator JavaDoc it=classes.iterator(); it.hasNext(); )
952             {
953                 Class JavaDoc current = (Class JavaDoc)it.next();
954
955                 if (current == referencer)
956                 {
957                     StringBuffer JavaDoc error =
958                       new StringBuffer JavaDoc("A circular dependency exists between views: ");
959
960                     for (Iterator JavaDoc chainIter=referenceChain.iterator(); chainIter.hasNext(); )
961                     {
962                         error.append(chainIter.next());
963                         if (chainIter.hasNext())
964                         {
965                             error.append(" -> ");
966                         }
967                     }
968
969                     throw new JDOFatalUserException(error.toString());
970                 }
971                 else
972                 {
973                     /*
974                      * Make a recursive call to check for any nested dependencies.
975                      * For example, A references B, B references C, C references A.
976                      */

977                     checkForCircularReferences(viewReferences, referencer,
978                                                current, referenceChain);
979                 }
980             }
981         }
982     }
983 }
984
Popular Tags