KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > metadata > MetaDataBuilder


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.metadata;
13
14 import com.versant.core.common.Debug;
15 import com.versant.core.common.config.ConfigInfo;
16 import com.versant.core.jdo.QueryDetails;
17 import com.versant.core.jdo.sco.VersantSCOFactoryRegistry;
18 import com.versant.core.metadata.parser.*;
19 import com.versant.core.jdo.query.ParseException;
20 import com.versant.core.jdo.query.QueryParser;
21 import com.versant.core.jdo.externalizer.Externalizer;
22 import com.versant.core.jdo.externalizer.SerializedExternalizer;
23 import com.versant.core.jdo.externalizer.TypeAsBytesExternalizer;
24 import com.versant.core.jdo.externalizer.TypeAsStringExternalizer;
25 import com.versant.core.util.classhelper.ClassHelper;
26 import com.versant.core.util.BeanUtils;
27 import com.versant.core.util.IntArray;
28
29 import java.io.Serializable JavaDoc;
30 import java.lang.reflect.*;
31 import java.util.*;
32
33 import com.versant.core.common.BindingSupportImpl;
34
35 /**
36  * This will parse one or more .jdo files and load and analyze the persistent
37  * classes to produce basic meta data. There are different subclasses for
38  * different StorageManager's.
39  */

40 public class MetaDataBuilder implements MDStatics {
41
42     protected final ConfigInfo config;
43     protected final ClassLoader JavaDoc loader;
44     protected final boolean quiet;
45     protected final ModelMetaData jmd;
46
47     private MetaDataPreProcessor metaDataPreProcessor;
48
49     private final MetaDataEnums MDE = new MetaDataEnums();
50
51     protected final MetaDataUtils mdutils = new MetaDataUtils();
52     protected final VersantSCOFactoryRegistry scoFactoryRegistry;
53
54     private Map appIdUniqueMap = new HashMap();
55     private Map externalizerMap = new HashMap();
56
57     private PackageMetaData[] packageMetaData;
58     private HashMap classMap = new HashMap(); // Class -> ClassMetaData
59
private HashMap classInfoMap = new HashMap(); // Class -> ClassInfo
60
private HashMap interfaceMap = new HashMap(); // Class -> InterfaceMetaData
61
private HashMap classAndInterfaceMap = new HashMap();
62     // Class -> ClassMetaData/InterfaceMetaData
63

64     private final FetchGroupBuilder fetchGroupBuilder;
65     private final QueryParser queryParser;
66     private static final String JavaDoc ARRAY_SUFFIX = "[]";
67
68     /**
69      * Info extracted from package extensions. These form defaults that
70      * apply to all classes in the package unless overridden at class level.
71      */

72     private static class PackageMetaData {
73
74         public String JavaDoc nameWithDot;
75         public JdoPackage jdoPackage;
76     }
77
78     /**
79      * Info extracted from class extensions that is only needed during
80      * meta data generation.
81      */

82     private static class ClassInfo {
83
84         public boolean refsInDefaultFetchGroup;
85         public JdoClass jdoClass;
86         public ArrayList horizontalFields;
87     }
88
89     public MetaDataBuilder(ConfigInfo config, ClassLoader JavaDoc loader,
90             boolean quiet) {
91         this.config = config;
92         this.loader = loader;
93         this.quiet = quiet;
94         jmd = new ModelMetaData();
95         jmd.testing = config.testing;
96         fetchGroupBuilder = createFetchGroupBuilder();
97         queryParser = new QueryParser(jmd);
98         scoFactoryRegistry = new VersantSCOFactoryRegistry(
99                 config.scoFactoryRegistryMappings, loader);
100
101
102         if (config.metaDataPreProcessor == null)
103             config.metaDataPreProcessor = EJB_JDBC_PRE_PROCESSOR;
104
105
106         if (config.metaDataPreProcessor != null) {
107             try {
108                 Class JavaDoc cls = loadClass(config.metaDataPreProcessor);
109                 metaDataPreProcessor = (MetaDataPreProcessor) cls.getConstructor(
110                         new Class JavaDoc[] {ClassLoader JavaDoc.class, MetaDataUtils.class}).
111                         newInstance(new Object JavaDoc[] {loader, mdutils});
112             } catch (Throwable JavaDoc e) {
113                 System.out.println("Unable to load 'metaDataPreProcessor' class '"
114                         + config.metaDataPreProcessor + "'");
115             }
116         }
117     }
118
119     protected FetchGroupBuilder createFetchGroupBuilder() {
120         return new FetchGroupBuilder(jmd,
121                 isSendCurrentForFGWithSecFields(), isReadObjectBeforeWrite());
122     }
123
124     public FetchGroupBuilder getFetchGroupBuilder() {
125         return fetchGroupBuilder;
126     }
127
128
129
130     /**
131      * Create meta data for already parsed .jdo meta data.
132      */

133     public ModelMetaData buildMetaData(JdoRoot[] roots) {
134
135         // populate the externalizer map
136
fillExternalizerMap();
137
138         // combine all the meta data into a single structure
139
JdoRoot jdoRoot = buildSingleJdoRoot(roots);
140
141         // pull out some package level defaults
142
int n = jdoRoot.packages.length;
143         packageMetaData = new PackageMetaData[n];
144         for (int i = 0; i < n; i++) {
145             packageMetaData[i] = createPackageMetaData(jdoRoot.packages[i]);
146         }
147
148         // create initial meta data for all classes
149
if (Debug.DEBUG) {
150             System.out.println(
151                     "MDB: Creating initial meta data for all classes and interfaces");
152         }
153
154         ClassMetaData[] classes = jmd.classes = new ClassMetaData[jmd.classResourceMap.size()];
155         for (int i = 0, c = 0; i < packageMetaData.length; i++) {
156             PackageMetaData pmd = packageMetaData[i];
157             JdoClass[] ca = pmd.jdoPackage.classes;
158             for (int j = 0; j < ca.length; j++) {
159                 if (metaDataPreProcessor != null) metaDataPreProcessor.process(ca[j]);
160                 ClassMetaData cmd = createMetaData(pmd, ca[j], quiet);
161                 classes[c++] = cmd;
162                 classMap.put(cmd.cls, cmd);
163             }
164             JdoExtension[] extensions = pmd.jdoPackage.extensions;
165             if (extensions != null) {
166                 for (int j = 0; j < extensions.length; j++) {
167                     JdoExtension e = extensions[j];
168                     if (e.key == JdoExtensionKeys.INTERFACE) {
169                         InterfaceMetaData imd = createInterfaceMetaData(pmd, e);
170                         interfaceMap.put(imd.cls, imd);
171                     }
172                 }
173             }
174         }
175         jmd.buildObjectIdClassMap();
176         jmd.buildAbstractSchemaNameMap();
177         classAndInterfaceMap.putAll(classMap);
178         classAndInterfaceMap.putAll(interfaceMap);
179
180         // sort the classes by name and generate classId's - the name sort
181
// ensures that the classIds are allocated deterministicly
182
if (Debug.DEBUG) {
183             System.out.println(
184                     "MDB: Sorting classes by name and generating classIds");
185         }
186         Arrays.sort(classes, new Comparator() {
187             public int compare(Object JavaDoc aa, Object JavaDoc bb) {
188                 ClassMetaData a = (ClassMetaData)aa;
189                 ClassMetaData b = (ClassMetaData)bb;
190                 return a.qname.compareTo(b.qname);
191             }
192         });
193         int clen = classes.length;
194         for (int i = 0; i < clen; i++) {
195             ClassMetaData c = classes[i];
196             c.setClassId(mdutils.generateClassId(c.qname));
197         }
198
199         // fill in the pcClassMetaData field for all classes
200
if (Debug.DEBUG) System.out.println("MDB: Filling pcClassMetaData");
201         for (int i = 0; i < clen; i++) {
202             ClassMetaData c = classes[i];
203             Class JavaDoc pcs = c.pcSuperClass;
204             if (pcs == null) continue;
205             c.pcSuperMetaData = (ClassMetaData)classMap.get(pcs);
206             if (c.pcSuperMetaData == null) {
207                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("persistence-capable-superclass not declared in meta data: " +
208                         pcs.getName() + "\n" +
209                         c.jdoClass.getContext());
210                 c.addError(x, quiet);
211             } else if (c.pcSuperMetaData.horizontal) {
212                 c.horizontalCMD = c.pcSuperMetaData;
213                 c.pcSuperMetaData = null;
214             }
215         }
216
217         // build the pcSubclasses arrays
218
if (Debug.DEBUG) {
219             System.out.println("MDB: Building pcSubclasses arrays");
220         }
221         ArrayList a = new ArrayList();
222         for (int i = 0; i < clen; i++) {
223             ClassMetaData c = classes[i];
224             a.clear();
225             for (int j = 0; j < clen; j++) {
226                 ClassMetaData sc = classes[j];
227                 if (sc.pcSuperMetaData == c) a.add(sc);
228             }
229             int len = a.size();
230             if (len > 0) {
231                 c.pcSubclasses = new ClassMetaData[len];
232                 a.toArray(c.pcSubclasses);
233             }
234         }
235
236         // fill the pcHeirachy field for all classes and copy the identityType
237
// down the heirachy
238
if (Debug.DEBUG) {
239             System.out.println(
240                     "MDB: Filling pcHeirachy field + copying identity type down");
241         }
242         for (int i = 0; i < clen; i++) {
243             ClassMetaData cmd = classes[i];
244             if (cmd.pcSuperMetaData == null) {
245                 cmd.calcPcHeirachy();
246                 if (config.hyperdrive) {
247                     cmd.oidClassName = MDStatics.GEN_OID_START +
248                             cmd.qname.replace('.', '_').replace('$', '_');
249                 }
250             }
251         }
252
253         // name the hyperdrive OID and State classes
254
if (config.hyperdrive) {
255             for (int i = 0; i < clen; i++) {
256                 ClassMetaData cmd = classes[i];
257                 if (cmd.pcSuperMetaData != null) {
258                     cmd.oidClassName = cmd.top.oidClassName;
259                 }
260                 cmd.stateClassName = MDStatics.GEN_STATE_START +
261                         cmd.qname.replace('.', '_').replace('$', '_');
262             }
263         }
264
265         // create meta data for all fields of all classes
266
if (Debug.DEBUG) System.out.println("MDB: Creating field meta data");
267         ClassMetaData[] ca = jmd.classes;
268         for (int j = 0; j < ca.length; j++) {
269             ClassMetaData cmd = ca[j];
270             try {
271                 createFieldMetaData(cmd, quiet);
272             } catch (RuntimeException JavaDoc e) {
273                 cmd.addError(e, quiet);
274             }
275         }
276
277         doEmbedded(ca);
278         doHorizontal(ca);
279
280         // extract all the primary key fields for all classes
281
if (Debug.DEBUG) {
282             System.out.println("MDB: Extracting primary key fields");
283         }
284         for (int i = 0; i < clen; i++) {
285             extractPrimaryKeyFields(classes[i], quiet);
286         }
287
288         // sort the classes array by classId so we can do a binary search
289
// and fill the index fields on all classes
290
if (Debug.DEBUG) System.out.println("MDB: Sorting classes by classId");
291         Arrays.sort(jmd.classes);
292         for (int i = 0; i < clen; i++) classes[i].index = i;
293
294         // call init1 on all our DataStore's - they may add additional store
295
// specific meta data at this time
296
if (Debug.DEBUG) {
297             System.out.println("MDB: Calling preBuildFetchGroupsHook");
298         }
299         preBuildFetchGroupsHook();
300
301         // create all the fetch groups
302
if (Debug.DEBUG) System.out.println("MDB: Creating fetch groups");
303         fetchGroupBuilder.buildFetchGroups(quiet);
304
305         calcSuperCounts(classes);
306
307         // resolve all ordering fields
308
if (Debug.DEBUG) {
309             System.out.println("MDB: Resolving collection field ordering");
310         }
311         for (int i = 0; i < clen; i++) resolveOrdering(classes[i], quiet);
312
313         // give all DataStore's a chance to do extra stuff now that
314
// superFieldCount and superFetchGroupCount are filled in
315
if (Debug.DEBUG) {
316             System.out.println("MDB: Calling postAllFieldsCreatedHook");
317         }
318         postAllFieldsCreatedHook();
319
320         // fill in the maxFieldsLength value
321
if (Debug.DEBUG) {
322             System.out.println("MDB: Calculating maxFieldsLength");
323         }
324         int max = 0;
325         for (int i = 0; i < clen; i++) {
326             ClassMetaData c = classes[i];
327             int len = c.stateFields == null ? 0 : c.stateFields.length;
328             if (len > max) max = len;
329         }
330         jmd.maxFieldsLength = max;
331
332         // collect all the pass 2 fields for each class
333
if (Debug.DEBUG) {
334             System.out.println("MDB: Creating pass2Fields[] for each class");
335         }
336         IntArray ia = new IntArray();
337         for (int i = 0; i < clen; i++) {
338             ClassMetaData cmd = classes[i];
339             ia.clear();
340             FieldMetaData[] fields = cmd.fields;
341             if (fields == null) continue; // possible if previous errors
342
int fc = fields.length;
343             for (int j = 0; j < fc; j++) {
344                 FieldMetaData fmd = fields[j];
345                 if (fmd.secondaryField) ia.add(fmd.fieldNo);
346             }
347             cmd.pass2Fields = ia.toArray();
348         }
349
350         // Fill in the fieldNo arrays now that everything is cool.
351
if (Debug.DEBUG) {
352             System.out.println("MDB: Calling initMDFields() for each class");
353         }
354         for (int i = 0; i < classes.length; i++) {
355             ClassMetaData cmd = classes[i];
356             if (cmd.pcSuperMetaData == null) cmd.initMDFields();
357         }
358
359         for (int i = 0; i < classes.length; i++) {
360             ClassMetaData cmd = classes[i];
361             if (cmd.pcSuperMetaData == null) cmd.initMDFields2();
362         }
363
364         /**
365          * Set flags in the meta data for the client to correctly handle
366          * bidirectional relationships.
367          */

368         setMasterDetailFlags(classes);
369
370         /**
371          * Update the totalSubClassCount
372          */

373         for (int i = 0; i < classes.length; i++) {
374             ClassMetaData cmd = classes[i];
375             cmd.totalNoOfSubClasses = getSubClassCount(cmd);
376         }
377
378         for (int i = 0; i < classes.length; i++) {
379             ClassMetaData aClass = classes[i];
380             if (aClass.objectIdClass != null) {
381                 try {
382                     validateIDClass(aClass);
383                 } catch (RuntimeException JavaDoc e) {
384                     aClass.addError(e, quiet);
385                 }
386             }
387         }
388
389         // finish initialization of fetch groups
390
if (Debug.DEBUG) {
391             System.out.println("MDB: Finishing fetch group initialization");
392         }
393         for (int i = 0; i < clen; i++) classes[i].finishFetchGroups();
394         for (int i = 0; i < clen; i++) classes[i].finishFetchGroups2();
395         for (int i = 0; i < clen; i++) {
396             ClassMetaData cmd = classes[i];
397             if (cmd.pcSuperMetaData == null) checkHeirachy(cmd);
398         }
399         for (int i = 0; i < clen; i++) {
400             ClassMetaData cmd = classes[i];
401             FieldMetaData[] fmds = cmd.fields;
402             if (fmds == null) continue; // possible if previous error
403
for (int j = 0; j < fmds.length; j++) {
404                 FieldMetaData.setStateMethodName(fmds[j]);
405             }
406         }
407
408         // This must be done after all fmd's have been finished.
409
// This must set the inverseFieldNos
410
// and null the inverseFieldMetaData
411
for (int i = 0; i < classes.length; i++) {
412             ClassMetaData cmd = classes[i];
413             FieldMetaData[] fmds = cmd.stateFields;
414             if (fmds == null) continue; // possible if previous error
415
for (int j = 0; j < fmds.length; j++) {
416                 FieldMetaData fmd = fmds[j];
417                 if (fmd.inverseFieldMetaData != null) {
418                     fmd.inverseFieldNo = fmd.inverseFieldMetaData.managedFieldNo;
419                 }
420             }
421         }
422
423         // Update the cacheStrat of the subclasses to that of the base class.
424
for (int i = 0; i < clen; i++) {
425             ClassMetaData cmd = classes[i];
426             if (cmd.pcSuperMetaData == null) cmd.overRideCacheStrategy();
427         }
428
429         // create QueryParam's for all the named queries
430
for (int i = 0; i < clen; i++) {
431             ClassMetaData cmd = classes[i];
432             try {
433                 createQueryParamsForNamedQueries(cmd);
434             } catch (RuntimeException JavaDoc e) {
435                 cmd.addError(e, quiet);
436             }
437         }
438
439         appIdUniqueMap = null;
440
441         // cleanup data structures not required after meta data generation
442
jmd.cleanupAfterMetaDataGeneration();
443
444         // check the meta data for consistency
445
jmd.validate();
446
447         return jmd;
448     }
449
450     protected void doHorizontal(ClassMetaData[] ca) {
451     }
452
453     protected void doEmbedded(ClassMetaData[] ca) {
454     }
455
456     protected void createHorizontalFieldMetaData(ClassMetaData cmd, boolean quiet) {
457         ClassMetaData superCmd = cmd.horizontalCMD;
458         if (superCmd == null) return;
459         if (superCmd.fields == null) return;
460
461         Map fieldMap = new HashMap();
462         createFmdWithReflection(cmd.horizontalCMD, fieldMap, superCmd.getShortName() + ".");
463         updateMDFromHorizontalSuper(cmd);
464         updateFmdFromMetadata(cmd.horizontalCMD, fieldMap, quiet, cmd.jdoClass.elements);
465         List fields = updateFmd(fieldMap, cmd, quiet);
466
467         for (int i = 0; i < fields.size(); i++) {
468             FieldMetaData fieldMetaData = (FieldMetaData) fields.get(i);
469             fieldMetaData.classMetaData = cmd;
470             fieldMetaData.fake = true;
471             fieldMetaData.origFmd = superCmd.fields[i];
472             fieldMetaData.horizontalFakeField = true;
473         }
474
475         cmd.horizontalFields = new FieldMetaData[fields.size()];
476         fields.toArray(cmd.horizontalFields);
477
478         fields.addAll(0, Arrays.asList(cmd.fields));
479         cmd.fields = new FieldMetaData[fields.size()];
480         fields.toArray(cmd.fields);
481
482         for (int i = cmd.fields.length - 1; i >= 0; i--) {
483             cmd.fields[i].fieldNo = i;
484         }
485         
486         updateScoFields(cmd);
487     }
488
489     /**
490      * Fill the superFieldCount and superFetchGroupCount values and
491      * build the stateFields arrays.
492      */

493     protected void calcSuperCounts(ClassMetaData[] classes) {
494         if (Debug.DEBUG) {
495             System.out.println("MDB: Calcing superFieldCount and superFetchGroupCount " +
496                     "filling stateFields");
497         }
498         int clen = classes.length;
499         for (int i = 0; i < clen; i++) {
500             ClassMetaData cmd = classes[i];
501             if (cmd.pcSuperMetaData == null) {
502                 cmd.calcSuperCounts();
503             }
504         }
505     }
506
507     /**
508      * Set flags in the meta data for the client to correctly handle
509      * bidirectional relationships.
510      */

511     protected void setMasterDetailFlags(ClassMetaData[] classes) {
512         // nothing to do
513
}
514
515     /**
516      * This is invoked before fetch groups are built. Subclasses can override
517      * this to do additional processing.
518      */

519     protected void preBuildFetchGroupsHook() {
520     }
521
522     /**
523      * Second hook called late in the build process after all fields have
524      * been created and fetch groups have been built.
525      */

526     protected void postAllFieldsCreatedHook() {
527     }
528
529     private void createQueryParamsForNamedQueries(ClassMetaData cmd) {
530         JdoClass jdoClass = getClassInfo(cmd).jdoClass;
531         ArrayList queries = jdoClass.queries;
532         if (queries == null || queries.isEmpty()) return;
533         int n = queries.size();
534         for (int i = 0; i < n; i++) {
535             JdoQuery q = (JdoQuery)queries.get(i);
536             if (cmd.getNamedQuery(q.name) != null) {
537                 throw BindingSupportImpl.getInstance().runtime("There " +
538                         "is already a query called '" + q.name + "' on " +
539                         cmd.qname + "\n" + q.getContext());
540             }
541             cmd.addNamedQuery(q.name, new QueryDetails(cmd, q));
542         }
543     }
544
545     /**
546      * What is the default cache strategy?
547      */

548     public int getCdCacheStrategy() {
549         return MDStatics.CACHE_STRATEGY_YES;
550     }
551
552     /**
553      * Are references placed in the default fetch group?
554      */

555     public boolean isCdRefsInDefaultFetchGroup() {
556         return false;
557     }
558
559     /**
560      * Recursive method to calculate the total subclass count.
561      */

562     private int getSubClassCount(ClassMetaData cmd) {
563         if (cmd.pcSubclasses == null) return 0;
564         int count = cmd.pcSubclasses.length;
565         for (int i = 0; i < cmd.pcSubclasses.length; i++) {
566             count += getSubClassCount(cmd.pcSubclasses[i]);
567         }
568         return count;
569     }
570
571     /**
572      * Resolve the ordering nodes for all collection fields with ordering.
573      */

574     protected void resolveOrdering(ClassMetaData cmd, boolean quiet) {
575         FieldMetaData[] fields = cmd.fields;
576         if (fields == null) return; // possible if previous errors
577
int flen = fields.length;
578         for (int i = 0; i < flen; i++) {
579             FieldMetaData fmd = fields[i];
580             if (fmd.ordering != null) {
581                 int len = fmd.ordering.length;
582                 try {
583                     for (int j = 0; j < len; j++) {
584                         fmd.ordering[j].resolve(queryParser,
585                                 fmd.elementTypeMetaData, false);
586                     }
587                 } catch (Exception JavaDoc e) {
588                     if (BindingSupportImpl.getInstance().isOwnFatalUserException(
589                             e)) {
590                         fmd.addError((RuntimeException JavaDoc)e, quiet);
591                     } else {
592                         RuntimeException JavaDoc jfue = BindingSupportImpl.getInstance().runtime(
593                                 "Invalid ordering extension for field " + fmd.getQName(),
594                                 e);
595                         fmd.addError(jfue, quiet);
596                     }
597                 }
598             }
599         }
600     }
601
602     private void checkHeirachy(ClassMetaData cmd) {
603         if (cmd.pcSubclasses == null) return;
604         ClassMetaData[] pcSubs = cmd.pcSubclasses;
605         FetchGroup[] superFG = cmd.fetchGroups;
606         for (int i = 0; i < pcSubs.length; i++) {
607             ClassMetaData pcSub = pcSubs[i];
608             for (int j = 0; j < superFG.length; j++) {
609                 FetchGroup fetchGroup = superFG[j];
610                 FetchGroup subFG = pcSub.getFetchGroup(fetchGroup.name);
611                 if (subFG == null || subFG.superFetchGroup != fetchGroup) {
612                     throw BindingSupportImpl.getInstance().internal(
613                             "FG hierachy broken");
614                 }
615             }
616             checkHeirachy(pcSub);
617         }
618     }
619
620     /**
621      * Throw a JDOFatalUserException for an unexpected extension.
622      */

623     public static void throwUnexpectedExtension(JdoExtension e) {
624         throw BindingSupportImpl.getInstance().runtime("Extension not allowed here: " +
625                 e + "\n" + e.getContext());
626     }
627
628     /**
629      * Find all the primary key fields for cmd.
630      */

631     private void extractPrimaryKeyFields(ClassMetaData cmd, boolean quiet) {
632         // collect pk fields in meta data order
633
ArrayList a = new ArrayList(4);
634         JdoElement[] ea = cmd.jdoClass.elements;
635         int n = ea.length;
636         for (int i = 0; i < n; i++) {
637             JdoElement o = ea[i];
638             if (!(o instanceof JdoField)) continue;
639             FieldMetaData fmd = cmd.getFieldMetaData(((JdoField)o).name);
640             if (fmd != null && fmd.primaryKey) a.add(fmd);
641         }
642
643         boolean appid = cmd.identityType == IDENTITY_TYPE_APPLICATION;
644         if (a.isEmpty()) {
645             if (appid && cmd.pcSuperClass == null) {
646                 RuntimeException JavaDoc e = BindingSupportImpl.getInstance().runtime("No primary key fields found for class with identity-type " +
647                         "application and no persistence-capable-superclass\n" +
648                         cmd.jdoClass.getContext());
649                 cmd.addError(e, quiet);
650                 return;
651             }
652         } else {
653             if (!appid && !cmd.horizontal) {
654                 RuntimeException JavaDoc e = BindingSupportImpl.getInstance().runtime("Only classes with identity-type application may have " +
655                         "primary-key fields\n" +
656                         cmd.jdoClass.getContext());
657                 cmd.addError(e, quiet);
658             }
659             cmd.pkFields = new FieldMetaData[a.size()];
660             a.toArray(cmd.pkFields);
661
662             initPkFieldDefaultValue(cmd, a, quiet);
663         }
664     }
665
666     private void initPkFieldDefaultValue(ClassMetaData cmd, ArrayList a, boolean quiet) {
667         if (cmd.identityType != IDENTITY_TYPE_APPLICATION) return;
668         try {
669             Object JavaDoc inst = null;
670             if (cmd.objectIdClass != null) {
671                 inst = cmd.objectIdClass.newInstance();
672             } else {
673                 inst = cmd.cls.newInstance();
674
675             }
676             for (int i = 0; i < a.size(); i++) {
677                 FieldMetaData fmd = (FieldMetaData) a.get(i);
678                 Field field = null;
679                 Class JavaDoc cls = inst.getClass();
680                 for (;cls != null;) {
681                     try {
682                         field = cls.getDeclaredField(fmd.name);
683                         break;
684                     } catch (NoSuchFieldException JavaDoc e) {
685                         cls = cls.getSuperclass();
686                     } catch (SecurityException JavaDoc e) {
687                         e.printStackTrace(System.out);
688                         break;
689                     }
690                 }
691                 if (field != null) {
692                     ClassHelper.get().setAccessible(field, true);
693                     fmd.setPkDefaultValue(field.get(inst));
694                 }
695             }
696         } catch (Exception JavaDoc e) {
697             if (!BindingSupportImpl.getInstance().isOwnException(e)) {
698                 e = BindingSupportImpl.getInstance().runtime(e.getMessage(),e);
699             }
700             cmd.addError((RuntimeException JavaDoc) e, quiet);
701             if (!quiet) {
702                 throw (RuntimeException JavaDoc) e;
703             }
704         }
705     }
706
707
708
709     /**
710      * Create a single JdoRoot from all packages.
711      */

712     private JdoRoot buildSingleJdoRoot(JdoRoot[] roots) {
713
714         // process all the JdoRoot's that are not query only resources
715
HashMap classMap = new HashMap();
716         ArrayList packageList = new ArrayList();
717         int n = roots.length;
718         for (int i = 0; i < n; i++) {
719             JdoRoot root = roots[i];
720             if (root.isQueryMetaData()) continue;
721             for (int k = root.packages.length - 1; k >= 0; k--) {
722                 JdoPackage p = root.packages[k];
723                 boolean addPackage = true;
724                 for (int j = p.classes.length - 1; j >= 0; j--) {
725                     JdoClass cls = p.classes[j];
726                     String JavaDoc qname = cls.getQName();
727                     String JavaDoc other = (String JavaDoc)jmd.classResourceMap.get(qname);
728                     if (other != null) {
729
730
731                         if (other.equals(root.name)) {
732                             throw BindingSupportImpl.getInstance().runtime(
733                                     "Class " + p.classes[j].name +
734                                     " is defined more than once in " + root.name);
735                         } else {
736                             throw BindingSupportImpl.getInstance().runtime(
737                                     "Class " + p.classes[j].name +
738                                     " is defined in " + other + " and " + root.name);
739                         }
740
741                     }
742                     jmd.classResourceMap.put(qname, root.name);
743                     classMap.put(qname, cls);
744                 }
745                 if (addPackage == true)
746                     packageList.add(p);
747             }
748         }
749
750         // move all JdoQuery's to the meta data for the classes they are for
751
for (int i = 0; i < n; i++) {
752             JdoRoot root = roots[i];
753             if (!root.isQueryMetaData()) continue;
754             for (int k = root.packages.length - 1; k >= 0; k--) {
755                 JdoPackage p = root.packages[k];
756                 for (int j = p.classes.length - 1; j >= 0; j--) {
757                     JdoClass cls = p.classes[j];
758                     if (cls.queries == null) continue;
759                     for (Iterator t = cls.queries.iterator(); t.hasNext();) {
760                         JdoQuery q = (JdoQuery)t.next();
761                         String JavaDoc qname = q.getCandidateClass();
762                         JdoClass target = (JdoClass)classMap.get(qname);
763                         if (target == null) {
764                             throw BindingSupportImpl.getInstance().runtime("Candidate class for query is not " +
765                                     "defined in the meta data: " + qname + "\n" +
766                                     q.getContext());
767                         }
768                         target.addJdoQuery(q);
769                     }
770                 }
771             }
772         }
773
774         // make a new single JdoRoot
775
JdoRoot root = new JdoRoot();
776         root.packages = new JdoPackage[packageList.size()];
777         root.name = "combined";
778         packageList.toArray(root.packages);
779         // Note that we have not changed the parent field on each package.
780
// This still points at the original JdoRoot so that error messages
781
// can display the correct filename.
782

783         return root;
784     }
785
786     /**
787      * Load class name using our loader.
788      */

789     private Class JavaDoc loadClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
790         return ClassHelper.get().classForName(name, false, loader);
791     }
792
793     /**
794      * Create tempory meta data for a package. This is used for defaults
795      * for classes etc.
796      */

797     private PackageMetaData createPackageMetaData(JdoPackage p) {
798         PackageMetaData pmd = new PackageMetaData();
799         pmd.nameWithDot = p.name + ".";
800         pmd.jdoPackage = p;
801         return pmd;
802     }
803
804     /**
805      * Create meta data for an interface in package pmd.
806      */

807     private InterfaceMetaData createInterfaceMetaData(PackageMetaData pmd,
808             JdoExtension ext) {
809         String JavaDoc qname = ext.getString();
810         if (qname.indexOf('.') < 0) qname = pmd.nameWithDot + qname;
811         Class JavaDoc cls;
812         try {
813             cls = loadClass(qname);
814         } catch (ClassNotFoundException JavaDoc e) {
815             throw BindingSupportImpl.getInstance().runtime(
816                     "Interface not found: " + qname + "\n" + ext.getContext(),
817                     e);
818         }
819         if (!cls.isInterface()) {
820             throw BindingSupportImpl.getInstance().runtime("Expected interface, found class: " + qname + "\n" +
821                     ext.getContext());
822         }
823         return new InterfaceMetaData(cls);
824     }
825
826     /**
827      * Create basic meta data for class cls in package pmd. This does not
828      * pickup the fields. It loads all classes etc and finds some class
829      * extensions.
830      */

831     private ClassMetaData createMetaData(PackageMetaData pmd, JdoClass jdoCls,
832             boolean quiet) {
833         ClassMetaData cmd = new ClassMetaData(jdoCls, jmd);
834         ClassInfo classInfo = getClassInfo(cmd);
835         classInfo.jdoClass = jdoCls;
836
837         // init defaults before we do anything else
838
cmd.packageNameWithDot = pmd.nameWithDot;
839         classInfo.refsInDefaultFetchGroup = isCdRefsInDefaultFetchGroup();
840         cmd.cacheStrategy = getCdCacheStrategy();
841         cmd.setObjectIdClasssRequired(jdoCls.objectIdClasssRequired);
842
843         // load the PC class
844
try {
845             cmd.cls = loadClass(cmd.qname);
846         } catch (ClassNotFoundException JavaDoc e) {
847             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Class not found: " + cmd.qname + "\n" +
848                     jdoCls.getContext(), e);
849             cmd.addError(x, quiet);
850             return cmd;
851         }
852         if (cmd.cls.isInterface()) {
853             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("Expected class, found interface: " + cmd.qname + "\n" +
854                     jdoCls.getContext());
855             cmd.addError(x, quiet);
856             return cmd;
857         }
858
859         // load its persistence-capable-superclass (if any)
860
String JavaDoc qname = jdoCls.getPCSuperClassQName();
861         if (qname != null) {
862             try {
863                 cmd.pcSuperClass = loadClass(qname);
864             } catch (ClassNotFoundException JavaDoc e) {
865                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("persistence-capable-superclass not found: " +
866                         qname + "\n" +
867                         jdoCls.getContext(), e);
868                 cmd.addError(x, quiet);
869                 return cmd;
870             }
871         }
872
873         checkForHorizontal(jdoCls, cmd);
874         updateIdMetaData(cmd, jdoCls);
875
876         // pickup some class extensions
877
JdoElement[] elements = jdoCls.elements;
878         int n = elements.length;
879         for (int i = 0; i < n; i++) {
880             JdoElement o = elements[i];
881             if (o instanceof JdoExtension) {
882                 JdoExtension e = (JdoExtension)o;
883                 switch (e.key) {
884                     case JdoExtensionKeys.READ_ONLY:
885                         try {
886                             cmd.readOnly = e.getBoolean();
887                         } catch (RuntimeException JavaDoc x) {
888                             cmd.addError(x, quiet);
889                         }
890                         break;
891                     case JdoExtensionKeys.CACHE_STRATEGY:
892                         try {
893                             cmd.cacheStrategy = e.getEnum(MDE.CACHE_ENUM);
894                         } catch (RuntimeException JavaDoc x) {
895                             cmd.addError(x, quiet);
896                         }
897                         break;
898                     case JdoExtensionKeys.DELETE_ORPHANS:
899                         try {
900                             cmd.deleteOrphans = e.getBoolean();
901                         } catch (RuntimeException JavaDoc x) {
902                             cmd.addError(x, quiet);
903                         }
904                         break;
905                     case JdoExtensionKeys.OIDS_IN_DEFAULT_FETCH_GROUP:
906                         try {
907                             getClassInfo(cmd).refsInDefaultFetchGroup = e.getBoolean();
908                         } catch (RuntimeException JavaDoc x) {
909                             cmd.addError(x, quiet);
910                         }
911                         break;
912                     case JdoExtensionKeys.CREATE_OID_AT_MAKE_PERSISTENT:
913                         // ignore - no longer required
914
break;
915                 }
916             }
917         }
918
919         return cmd;
920     }
921
922     private void updateIdMetaData(ClassMetaData cmd, JdoClass jdoCls) {
923         // load its objectid-class (if any)
924
cmd.identityType = jdoCls.identityType;
925         if (cmd.identityType == IDENTITY_TYPE_APPLICATION) {
926             String JavaDoc qname = jdoCls.getObjectIdClassQName();
927             if (qname == null) {
928                 if (cmd.isObjectIdClasssRequired()) {
929                     RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime(
930                             "objectid-class is required for application identity\n" +
931                             jdoCls.getContext());
932                     cmd.addError(x, quiet);
933                 }
934                 return;
935             }
936
937             String JavaDoc pcRootClsName = (String JavaDoc)appIdUniqueMap.get(qname);
938             if (pcRootClsName == null) {
939                 appIdUniqueMap.put(qname, jdoCls.getQName());
940             } else {
941                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().invalidOperation("The objectid-class for " +
942                         jdoCls.getQName() + " has already been used for " +
943                         pcRootClsName + ": " + qname + "\n" +
944                         jdoCls.getContext());
945                 cmd.addError(x, quiet);
946                 return;
947             }
948
949             try {
950                 cmd.objectIdClass = loadClass(qname);
951             } catch (ClassNotFoundException JavaDoc e) {
952                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("objectid-class not found: " + qname + "\n" +
953                         jdoCls.getContext(), e);
954                 cmd.addError(x, quiet);
955                 return;
956             }
957         } else if (cmd.identityType == IDENTITY_TYPE_NONDURABLE) {
958             RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("nondurable identity-type is not supported\n" +
959                     jdoCls.getContext());
960             cmd.addError(x, quiet);
961             return;
962         } else {
963             cmd.identityType = IDENTITY_TYPE_DATASTORE;
964             if (jdoCls.objectIdClass != null) {
965                 RuntimeException JavaDoc x = BindingSupportImpl.getInstance().runtime("objectid-class is only allowed for application identity\n" +
966                         jdoCls.getContext());
967                 cmd.addError(x, quiet);
968                 return;
969             }
970         }
971 // } else {
972
// if (jdoCls.objectIdClass != null) {
973
// RuntimeException x = BindingSupportImpl.getInstance().runtime("objectid-class is not allowed as class " +
974
// "has a persistence-capable-superclass\n" +
975
// jdoCls.getContext());
976
// cmd.addError(x, quiet);
977
// return cmd;
978
// }
979
// }
980
}
981
982     /**
983      * Calculate if this class is horizontally mapped.
984      * @param jdoCls
985      * @param cmd
986      */

987     protected void checkForHorizontal(JdoClass jdoCls, ClassMetaData cmd) {
988     }
989
990     /**
991      * Validate the application id class. This also fills
992      * {@link FieldMetaData#objectidClassField}.
993      */

994     private void validateIDClass(ClassMetaData cmd) {
995         Class JavaDoc idClass = cmd.objectIdClass;
996
997         if (!Modifier.isPublic(idClass.getModifiers())) {
998             throw BindingSupportImpl.getInstance().runtime("Application id class '"
999                     + idClass.getName()
1000                    + "' must be public");
1001        }
1002
1003        if (idClass.isInterface()) {
1004            throw BindingSupportImpl.getInstance().runtime("Application id class '"
1005                    + idClass.getName()
1006                    + "' may not be an interface");
1007        }
1008
1009        FieldMetaData[] pkFields = cmd.pkFields;
1010        for (int i = 0; i < pkFields.length; i++) {
1011            pkFields[i].getObjectidClassField();
1012        }
1013
1014        // Must implement java.io.Serializable
1015
if (!Serializable JavaDoc.class.isAssignableFrom(idClass)) {
1016            throw BindingSupportImpl.getInstance().runtime("Application id class '"
1017                    + idClass.getName()
1018                    + "' does not implement java.io.Serializable");
1019        }
1020
1021        // Must have default constructor
1022
Constructor[] constructors = idClass.getDeclaredConstructors();
1023        boolean foundDefaultCon = false;
1024        for (int i = 0; i < constructors.length; i++) {
1025            Constructor constructor = constructors[i];
1026            if (!Modifier.isPublic(constructor.getModifiers())) continue;
1027            if (constructor.getParameterTypes().length != 0) continue;
1028            foundDefaultCon = true;
1029        }
1030        if (!foundDefaultCon) {
1031            throw BindingSupportImpl.getInstance().runtime("Application id class '"
1032                    + idClass.getName()
1033                    + "' does not have a public no-arg constructor");
1034        }
1035
1036        // Must have String constructor
1037
boolean foundStringCon = false;
1038        for (int i = 0; i < constructors.length; i++) {
1039            Constructor constructor = constructors[i];
1040            if (!Modifier.isPublic(constructor.getModifiers())) continue;
1041            if (constructor.getParameterTypes().length != 1) continue;
1042            if (constructor.getParameterTypes()[0].isAssignableFrom(
1043                    String JavaDoc.class)) {
1044                foundStringCon = true;
1045            }
1046        }
1047        if (!foundStringCon) {
1048            throw BindingSupportImpl.getInstance().runtime(
1049                    "Application id class '"
1050                    + idClass.getName()
1051                    + "' does not have a public constructor that accepts a String parameter");
1052        }
1053
1054        // Must override toString
1055
try {
1056            Method tos = null;
1057            for (Class JavaDoc c = idClass; c != Object JavaDoc.class; c = c.getSuperclass()) {
1058                try {
1059                    tos = c.getDeclaredMethod("toString", null);
1060                    break;
1061                } catch (NoSuchMethodException JavaDoc e) {
1062                    // ignore
1063
}
1064            }
1065            if (tos == null) {
1066                throw BindingSupportImpl.getInstance().runtime("Application id class '"
1067                        + idClass.getName() + "' must override toString");
1068            }
1069        } catch (SecurityException JavaDoc e) {
1070            throw BindingSupportImpl.getInstance().exception(e.getMessage(), e);
1071        }
1072    }
1073
1074    /**
1075     * Create the FieldMetaData for class cmd. This finds all fields using
1076     * reflection and merges this with the JdoField information from the
1077     * .jdo files.
1078     */

1079    private void createFieldMetaData(ClassMetaData cmd, boolean quiet) {
1080        if (cmd.pcSuperMetaData != null) return;
1081        createFieldMetaDataImp(cmd, quiet);
1082    }
1083
1084    protected void createEmbeddeFieldMetaData(ClassMetaData cmd, boolean quiet) {
1085        if (cmd.pcSuperMetaData != null) return;
1086        createEmbeddeFieldMetaDataImp(cmd, quiet);
1087    }
1088
1089    /**
1090     * Update all the jdo metadata for horizontal fields from horizontal superclass.
1091     */

1092    private void updateMDFromHorizontalSuper(ClassMetaData cmd) {
1093        if (cmd.horizontalCMD == null) return;
1094
1095        JdoElement[] ea = cmd.jdoClass.elements;
1096        List newFields = new ArrayList(Arrays.asList(ea));
1097        JdoElement[] hor = cmd.horizontalCMD.jdoClass.elements;
1098        final String JavaDoc prefix = cmd.horizontalCMD.getShortName() + ".";
1099
1100        for (int i = 0; i < hor.length; i++) {
1101            JdoElement o = hor[i];
1102            if (!(o instanceof JdoField)) continue;
1103            JdoField jdoField = (JdoField)o;
1104
1105            /**
1106             * If this field is descibed in subclass then merge it, else just copy
1107             * it to the subclass
1108             */

1109            JdoField extendedField = findJdoFieldIn(ea, prefix + jdoField.name);
1110            if (extendedField == null) {
1111                extendedField = jdoField.createCopy(cmd.jdoClass);
1112                extendedField.name = prefix + extendedField.name;
1113                newFields.add(extendedField);
1114            } else {
1115                extendedField.synchWith(jdoField, Collections.EMPTY_SET, false);
1116            }
1117        }
1118        ea = new JdoElement[newFields.size()];
1119        newFields.toArray(ea);
1120        cmd.jdoClass.elements = ea;
1121    }
1122
1123
1124    private JdoField findJdoFieldIn(JdoElement[] elements, String JavaDoc name) {
1125        for (int i = 0; i < elements.length; i++) {
1126            JdoElement element = elements[i];
1127            if (!(element instanceof JdoField)) continue;
1128            JdoField jdoField = (JdoField) element;
1129            if (jdoField.name.equals(name)) {
1130                return jdoField;
1131            }
1132        }
1133        return null;
1134    }
1135
1136    private void createEmbeddeFieldMetaDataImp(ClassMetaData cmd, boolean quiet) {
1137        ArrayList currentFields = new ArrayList(Arrays.asList(cmd.fields));
1138        HashMap parents = new HashMap(cmd.fields.length * 5);
1139        FieldMetaData fmd = null;
1140        for (int i = 0; i < currentFields.size(); i++) {
1141            fmd = (FieldMetaData) currentFields.get(i);
1142
1143            if (fmd.isEmbeddedRef()) {
1144                ClassMetaData embeddedCmd = fmd.typeMetaData;
1145
1146                Map fieldMap = new HashMap();
1147                createFmdWithReflection(embeddedCmd, fieldMap, fmd.name + "/");
1148
1149                /**
1150                 * Make a copy of the jdoFields as found on the original class
1151                 * Extract info from the extensions found on the embedded ref field
1152                 * and apply it to the relevant jdoFields.
1153                 */

1154                Map copiedJdoFields = new HashMap();
1155                for (int j = 0; j < embeddedCmd.jdoClass.elements.length; j++) {
1156                    JdoElement element = embeddedCmd.jdoClass.elements[j];
1157                    if (!(element instanceof JdoField)) continue;
1158                    JdoField copy = ((JdoField) element).createCopy(cmd.jdoClass);
1159                    //map it to old name
1160
copiedJdoFields.put(copy.name, copy);
1161                    copy.origName = copy.name;
1162                    copy.name = fmd.name + "/" + copy.name;
1163                    if (fmd.jdoField != null && fmd.jdoField.extensions != null) {
1164                        JdoExtension ext = JdoExtension.find(JdoExtensionKeys.FIELD, copy.origName, fmd.jdoField.extensions);
1165                        if (ext != null) copy.applyEmbeddedExtensions(ext.nested);
1166                    } else {
1167                        copy.applyEmbeddedExtensions(null);
1168                    }
1169                }
1170
1171                //create new JdoField's from extensions
1172
if (fmd.jdoField != null && fmd.jdoField.extensions != null) {
1173                    JdoExtension exts[] = fmd.jdoField.extensions;
1174                    Iterator iter = fieldMap.keySet().iterator();
1175                    while (iter.hasNext()) {
1176                        String JavaDoc name = (String JavaDoc) iter.next();
1177                        name = name.substring((fmd.name + "/").length());
1178                        if (copiedJdoFields.containsKey(name)) continue;
1179                        JdoExtension ext = JdoExtension.find(JdoExtensionKeys.FIELD, name, exts);
1180                        if (ext != null) {
1181                            JdoField newJdoField = new JdoField(fmd.jdoField);
1182                            newJdoField.name = fmd.name + "/" + name;
1183                            newJdoField.applyEmbeddedExtensions(ext.nested);
1184                            copiedJdoFields.put(name, newJdoField);
1185                        }
1186                    }
1187                }
1188
1189                JdoElement[] els = new JdoElement[copiedJdoFields.size()];
1190                copiedJdoFields.values().toArray(els);
1191
1192                updateFmdFromMetadata(embeddedCmd, fieldMap, quiet, els);
1193                List fields = updateFmd(fieldMap, cmd, quiet);
1194                findNullIndicationFmd(fields, fmd);
1195
1196                for (int j = 0; j < fields.size(); j++) {
1197                    FieldMetaData fieldMetaData = (FieldMetaData) fields.get(j);
1198                    fieldMetaData.classMetaData = cmd;
1199                    fieldMetaData.fake = true;
1200                    fieldMetaData.origFmd = embeddedCmd.fields[j];
1201                    fieldMetaData.embeddedFakeField = true;
1202
1203                    //look for recursive embedded field
1204
checkRecursiveEmbedded(fieldMetaData, fmd, parents);
1205                }
1206
1207
1208                fmd.embeddedFmds = new FieldMetaData[fields.size()];
1209                fields.toArray(fmd.embeddedFmds);
1210
1211                /**
1212                 * Look for nullIndicator
1213                 * The nullIndicator might be a column or a field of the embedded class.
1214                 * If it is a column then we must check if there is a field with the same
1215                 * column name. If there is then it must be used as the null indicator.
1216                 * Else we must create a fake field with the supplied column name to act as
1217                 * the nullIndicator.
1218                 */

1219                if (fmd.jdoField.extensions != null) {
1220                    for (int j = 0; j < fmd.jdoField.extensions.length; j++) {
1221                        JdoExtension ext = fmd.jdoField.extensions[j];
1222                        if (ext.key == JdoExtensionKeys.NULL_INDICATOR) {
1223                            //must add fake field
1224
}
1225                    }
1226                }
1227                currentFields.addAll(fields);
1228            }
1229        }
1230
1231
1232        cmd.fields = new FieldMetaData[currentFields.size()];
1233        currentFields.toArray(cmd.fields);
1234
1235        for (int j = cmd.fields.length - 1; j >= 0; j--) {
1236            cmd.fields[j].fieldNo = j;
1237        }
1238
1239        updateScoFields(cmd);
1240
1241        if (cmd.pcSubclasses != null) {
1242            for (int i = 0; i < cmd.pcSubclasses.length; i++) {
1243                createEmbeddeFieldMetaDataImp(cmd.pcSubclasses[i], quiet);
1244            }
1245        }
1246    }
1247
1248    private void findNullIndicationFmd(List fields, FieldMetaData fmd) {
1249        for (int j = 0; j < fields.size(); j++) {
1250            FieldMetaData fieldMetaData = (FieldMetaData) fields.get(j);
1251            if (fieldMetaData.jdoField != null && fieldMetaData.jdoField.nullIndicator) {
1252                fmd.setNullIndicatorFmd(fieldMetaData);
1253                break;
1254            }
1255        }
1256    }
1257
1258    private void checkRecursiveEmbedded(FieldMetaData fieldMetaData, FieldMetaData fmd, HashMap parents) {
1259        if (fieldMetaData.jdoField != null && fieldMetaData.isDirectRef()) {
1260            JdoExtension ext = null;
1261            if (fieldMetaData.jdoField.extensions != null) {
1262                ext = JdoExtension.find(JdoExtensionKeys.EMBEDDED, fieldMetaData.jdoField.extensions);
1263            }
1264            if (ext != null) {
1265                if (ext.getBoolean()) {
1266                    fieldMetaData.embedded = true;
1267                } else {
1268                    fieldMetaData.embedded = false;
1269                }
1270            } else {
1271                if (fieldMetaData.embedded) {
1272                    HashSet parentTypes = new HashSet(5);
1273                    FieldMetaData field = fmd;
1274                    while(field != null){
1275                        ClassMetaData refClass = field.typeMetaData;
1276                        while(refClass != null){
1277                            parentTypes.add(refClass);
1278                            refClass = refClass.pcSuperMetaData;
1279                        }
1280                        FieldMetaData tempParent = (FieldMetaData)parents.get(field);
1281                        if(tempParent == null){
1282                            refClass = field.classMetaData;
1283                            while(refClass != null){
1284                                parentTypes.add(refClass);
1285                                refClass = refClass.pcSuperMetaData;
1286                            }
1287                        }
1288                        if(parentTypes.contains(fieldMetaData.typeMetaData)){
1289                            fieldMetaData.embedded = false;
1290                            break;
1291                        }
1292                        field = tempParent;
1293                    }
1294                }
1295            }
1296        }
1297    }
1298
1299    /**
1300     * Create the FieldMetaData for class cmd and recursively all of its
1301     * subclasses.
1302     */

1303    private void createFieldMetaDataImp(ClassMetaData cmd, boolean quiet) {
1304        HashMap fieldMap = new HashMap(); // name -> FieldMetaData
1305
createFmdWithReflection(cmd, fieldMap, "");
1306        updateFmdFromMetadata(cmd, fieldMap, quiet, cmd.jdoClass.elements);
1307        List fields = updateFmd(fieldMap, cmd, quiet);
1308        finalizeFmds(fields, cmd, quiet);
1309    }
1310
1311    /**
1312     * extract all the persistent and transactional fields and fill in more info.
1313     * Return a list of managed fields.
1314     */

1315    private List updateFmd(Map fieldMap, ClassMetaData cmd, boolean quiet) {
1316        ArrayList fields = new ArrayList();
1317        FieldMetaData fmd = null;
1318        for (Iterator i = fieldMap.values().iterator(); i.hasNext();) {
1319            fmd = (FieldMetaData)i.next();
1320            if (fmd.persistenceModifier == PERSISTENCE_MODIFIER_NONE) continue;
1321            JdoField jdoField = fmd.jdoField;
1322            if (Modifier.isTransient(fmd.modifiers)) {
1323                if (jdoField == null) continue;
1324            }
1325            fields.add(fmd);
1326            Class JavaDoc tt = fmd.componentType;
1327            if (tt == null) tt = fmd.type;
1328            fmd.typeMetaData = (ClassMetaData)classMap.get(tt);
1329            if (fmd.category == 0) {
1330                fmd.category = mdutils.getFieldCategory(
1331                        fmd.persistenceModifier,
1332                        fmd.type, classAndInterfaceMap);
1333            }
1334            if (fmd.category == MDStatics.CATEGORY_COLLECTION
1335                    || fmd.category == MDStatics.CATEGORY_ARRAY
1336                    || fmd.category == MDStatics.CATEGORY_MAP) {
1337                fmd.collectionDiffType = true;
1338            }
1339            if ((!fmd.embedded && fmd.category == CATEGORY_REF || fmd.category == CATEGORY_POLYREF)
1340                    && (jdoField == null || jdoField.defaultFetchGroup == NOT_SET)) {
1341                fmd.defaultFetchGroup = getClassInfo(cmd).refsInDefaultFetchGroup;
1342            }
1343            if (fmd.category == MDStatics.CATEGORY_TRANSACTIONAL && fmd.scoField == true) {
1344                fmd.scoField = false;
1345            }
1346            if (fmd.category == MDStatics.CATEGORY_EXTERNALIZED) {
1347                if (fmd.externalizer == null) {
1348                    fmd.externalizer = getExternalizerForType(fmd.type);
1349                }
1350                fmd.scoField = false;
1351            }
1352            if (fmd.category != MDStatics.CATEGORY_TRANSACTIONAL) {
1353                try {
1354                    fillCollectionMetaData(fmd);
1355                } catch (RuntimeException JavaDoc e) {
1356                    fmd.addError(e, quiet);
1357                }
1358            }
1359        }
1360        // sort by field name i.e. into fieldNo order
1361
Collections.sort(fields);
1362        return fields;
1363    }
1364
1365    /**
1366     * Look at all the meta data from .jdo files and merge it in.
1367     */

1368    private void updateFmdFromMetadata(ClassMetaData cmd, Map fieldMap,
1369            boolean quiet, JdoElement[] ea) {
1370        int n = ea.length;
1371        for (int i = 0; i < n; i++) {
1372            JdoElement o = ea[i];
1373            if (!(o instanceof JdoField)) continue;
1374            JdoField jdoField = (JdoField)o;
1375            if (jdoField.persistenceModifier == PERSISTENCE_MODIFIER_NONE) {
1376                fieldMap.remove(jdoField.name);
1377                continue;
1378            }
1379
1380            FieldMetaData fmd = (FieldMetaData)fieldMap.get(jdoField.name);
1381            /**
1382             * The fmd might be null because w
1383             */

1384            if (fmd == null) {
1385                final String JavaDoc prefix = cmd.getShortName() + ".";
1386                if (jdoField.name.startsWith(prefix)) {
1387                    String JavaDoc name = jdoField.name.substring(prefix.length());
1388                    fmd = (FieldMetaData)fieldMap.get(name);
1389                }
1390            }
1391            if (fmd == null) {
1392                int cIndex = jdoField.name.lastIndexOf("/");
1393                if (cIndex != -1) {
1394                    String JavaDoc name = jdoField.name.substring(cIndex);
1395                    fmd = (FieldMetaData)fieldMap.get(name);
1396                }
1397            }
1398            try {
1399                if (fmd == null) {
1400                    if (cmd.horizontal ||
1401                            (cmd.horizontalCMD != null && jdoField.name.startsWith(cmd.horizontalCMD.getShortName() + "."))) {
1402                        //this is a metadata for a horizontal field so ignore it for now
1403
continue;
1404                    }
1405                    throw BindingSupportImpl.getInstance().runtime("Field " +
1406                            jdoField.name + " not found on " + cmd.qname + "\n" +
1407                            jdoField.getContext());
1408                }
1409                if (fmd.jdoField != null) {
1410                    throw BindingSupportImpl.getInstance().runtime("Duplicate meta data for field " + jdoField.name + "\n" +
1411                            jdoField.getContext());
1412                }
1413                fmd.jdoField = jdoField;
1414                fmd.cascadeType = jdoField.cascadeType;
1415
1416                if (jdoField.persistenceModifier != NOT_SET) {
1417                    fmd.persistenceModifier = jdoField.persistenceModifier;
1418                }
1419
1420                // do not allow static or final fields
1421
if (!mdutils.isPersistentModifiers(fmd.modifiers)) {
1422                    throw BindingSupportImpl.getInstance().runtime("Field " + jdoField.name + " is static and/or final\n" +
1423                            jdoField.getContext());
1424                }
1425
1426                // fill in more basic info
1427
fmd.primaryKey = jdoField.primaryKey;
1428                fmd.nullValue = jdoField.nullValue;
1429                if (fetchGroupBuilder.findFetchGroupExt(jdoField.extensions) != null) {
1430                    fmd.defaultFetchGroup = false;
1431                } else if (jdoField.defaultFetchGroup != NOT_SET) {
1432                    fmd.defaultFetchGroup = jdoField.defaultFetchGroup == TRUE;
1433                }
1434                if (jdoField.embedded != NOT_SET) {
1435                    fmd.embedded = jdoField.embedded == TRUE;
1436                }
1437                fmd.jdoCollection = jdoField.collection;
1438                fmd.jdoArray = jdoField.array;
1439                fmd.jdoMap = jdoField.map;
1440
1441                // process extensions
1442
processFieldExtensions(fmd, quiet);
1443
1444                // make sure transactional fields do not end up in the default
1445
// fetch group
1446
if (fmd.persistenceModifier == PERSISTENCE_MODIFIER_TRANSACTIONAL) {
1447                    fmd.defaultFetchGroup = false;
1448                }
1449            } catch (RuntimeException JavaDoc e) {
1450                if (quiet) {
1451                    if (fmd != null) fmd.addError(e, quiet);
1452                    else cmd.addError(e, quiet);
1453                    continue;
1454                } else {
1455                    throw e;
1456                }
1457            }
1458        }
1459    }
1460
1461    /**
1462     * Create metadata using reflection.
1463     * @param cmd
1464     * @param fieldMap
1465     */

1466    private void createFmdWithReflection(ClassMetaData cmd, Map fieldMap, String JavaDoc prefix) {
1467        ClassMetaData.FieldInfo[] fa = cmd.getDeclaredFields();
1468        for (int i = fa.length - 1; i >= 0; i--) {
1469            ClassMetaData.FieldInfo f = fa[i];
1470            String JavaDoc fname = f.getName();
1471
1472            if (mdutils.isEnhancerAddedField(fname)) continue;
1473
1474            FieldMetaData fmd = new FieldMetaData();
1475            fmd.classMetaData = cmd;
1476            fmd.name = prefix + fname;
1477            fmd.origName = fname;
1478            fmd.setType(f.getType());
1479            fmd.setComponentType(fmd.type.getComponentType());
1480            fmd.modifiers = f.getModifiers();
1481            fmd.scoField = mdutils.isMutableType(fmd.type);
1482            if (mdutils.isDefaultPersistentField(f,
1483                    classAndInterfaceMap)) {
1484                fmd.persistenceModifier = PERSISTENCE_MODIFIER_PERSISTENT;
1485            } else {
1486                fmd.persistenceModifier = PERSISTENCE_MODIFIER_NONE;
1487            }
1488            fmd.nullValue = NULL_VALUE_NONE;
1489            fmd.defaultFetchGroupDefault = mdutils.isDefaultFetchGroupType(fmd.type);
1490            fmd.defaultFetchGroup = fmd.defaultFetchGroupDefault;
1491            fmd.embedded = mdutils.isEmbeddedType(fmd.type);
1492            fieldMap.put(fmd.name, fmd);
1493        }
1494    }
1495
1496    /**
1497     * The supplied 'fields' list must be sorted on the natural ordering of fmd's.
1498     */

1499    private void finalizeFmds(List fields, ClassMetaData cmd, boolean quiet) {
1500        // create our fields array
1501
FieldMetaData[] fmda = cmd.fields = new FieldMetaData[fields.size()];
1502        fields.toArray(fmda);
1503        cmd.realFieldCount = fmda.length;
1504
1505        // set the fieldNo for each field
1506
for (int i = fmda.length - 1; i >= 0; i--) {
1507            fmda[i].fieldNo = i;
1508        }
1509
1510        // process all subclasses
1511
ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
1512        if (pcSubclasses != null) {
1513            int len = pcSubclasses.length;
1514            for (int i = 0; i < len; i++) {
1515                createFieldMetaDataImp(pcSubclasses[i], quiet);
1516            }
1517        }
1518        updateScoFields(cmd);
1519    }
1520
1521    private void updateScoFields(ClassMetaData cmd) {
1522        FieldMetaData fmd;
1523        for (int i = 0; i < cmd.fields.length; i++) {
1524            fmd = cmd.fields[i];
1525            if (fmd.category == MDStatics.CATEGORY_ARRAY
1526                    || fmd.category == MDStatics.CATEGORY_COLLECTION
1527                    || fmd.category == MDStatics.CATEGORY_MAP
1528
1529                    || fmd.typeCode == MDStatics.DATE) {
1530                fmd.setScoField(true);
1531                setSCOFactory(fmd);
1532            }
1533        }
1534    }
1535
1536    /**
1537     * Process the extensions for fmd (if any).
1538     */

1539    private void processFieldExtensions(FieldMetaData fmd, boolean quite) {
1540        JdoExtension[] a = fmd.jdoField.extensions;
1541        if (a == null) return;
1542        for (int i = 0; i < a.length; i++) {
1543            JdoExtension e = a[i];
1544            switch (e.key) {
1545                case JdoExtensionKeys.NULL_INDICATOR:
1546                    fmd.embedded = true;
1547                    break;
1548                case JdoExtensionKeys.FIELD:
1549                    fmd.embedded = true;
1550                    break;
1551                case JdoExtensionKeys.EXTERNALIZER:
1552                    fmd.category = MDStatics.CATEGORY_EXTERNALIZED;
1553                    try {
1554                        fmd.externalizer = createExternalizer(fmd.type, e);
1555                    } catch (RuntimeException JavaDoc x) {
1556                        fmd.addError(x, quiet);
1557                    }
1558                    break;
1559                case JdoExtensionKeys.NULL_IF_NOT_FOUND:
1560                    fmd.nullIfNotFound = e.getBoolean();
1561                    break;
1562                case JdoExtensionKeys.DEPENDENT:
1563                    fmd.dependentValues = e.getBoolean();
1564                    break;
1565                case JdoExtensionKeys.KEYS_DEPENDENT:
1566                    fmd.dependentKeys = e.getBoolean();
1567                    break;
1568                case JdoExtensionKeys.AUTOSET:
1569                    int v = e.getEnum(MDE.AUTOSET_ENUM);
1570                    int tc = fmd.typeCode;
1571                    if (v != AUTOSET_NO && tc != DATE && tc != INT
1572                            && tc != SHORT && tc != BYTE ) {
1573                        RuntimeException JavaDoc ex = BindingSupportImpl.getInstance().runtime("The autoset extension " +
1574                                "may only be used on java.util.Date, int, short and byte fields\n" +
1575                                e.getContext());
1576                        fmd.addError(ex, quite);
1577                    }
1578                    fmd.setAutoSet(v);
1579                    break;
1580                case JdoExtensionKeys.NULL_VALUE:
1581                case JdoExtensionKeys.FETCH_GROUP:
1582                case JdoExtensionKeys.VALID_CLASS:
1583                    // these are handled later
1584
break;
1585                case JdoExtensionKeys.SCO_FACTORY:
1586                    try {
1587                        Class JavaDoc factoryClass = ClassHelper.get().classForName
1588                                (e.value, false, loader);
1589                        Object JavaDoc factory = factoryClass.newInstance();
1590                        fmd.setScoFactory(factory);
1591                    } catch (Exception JavaDoc ex) {
1592                        RuntimeException JavaDoc exception = BindingSupportImpl.getInstance().runtime(
1593                                "Unable to add SCO factory mapping:\n" + ex.getMessage(),
1594                                ex);
1595                        fmd.addError(exception, quite);
1596                    }
1597                    break;
1598                default:
1599                    if (e.isCommon()) {
1600                        RuntimeException JavaDoc ex = BindingSupportImpl.getInstance().runtime(
1601                                "Unexpected extension\n " + e.getContext());
1602                        fmd.addError(ex, quite);
1603                    }
1604            }
1605        }
1606    }
1607
1608
1609    /**
1610     * Convert a meta data type name (int, String, Integer, za.co.hemtech.Blah
1611     * etc.) into a Class.
1612     *
1613     * @param name The name as in the meta data (e.g. Blah)
1614     * @param qname The name with package added (e.g. za.co.hemtech.Blah)
1615     * @param descr Description of name for error messages (e.g. value-class)
1616     * @param context Element to supply context for error messages
1617     * @return Class or null if name is not found and qname is null
1618     */

1619    private Class JavaDoc resolveTypeName(String JavaDoc name, String JavaDoc qname, String JavaDoc descr,
1620            JdoElement context) {
1621        Class JavaDoc type = MDStaticUtils.toSimpleClass(name);
1622        if (type == null) {
1623            try {
1624                if (name.endsWith(ARRAY_SUFFIX)) {
1625                    return Array.newInstance(resolveTypeName(
1626                            name.substring(0, name.length() - ARRAY_SUFFIX.length()),
1627                            qname.substring(0, qname.length() - ARRAY_SUFFIX.length()),
1628                            descr, context), 0).getClass();
1629                } else {
1630                    type = loadClass(name);
1631                }
1632            } catch (ClassNotFoundException JavaDoc e) {
1633                //ignore
1634
}
1635            if (qname != null) {
1636                try {
1637                    type = loadClass(qname);
1638                } catch (ClassNotFoundException JavaDoc e) {
1639                    throw BindingSupportImpl.getInstance().runtime(descr + " class not found: " + qname + "\n" +
1640                            context.getContext(), e);
1641                }
1642            }
1643        }
1644        return type;
1645    }
1646
1647    /**
1648     * Fill in collection, array or map related fields for the meta data.
1649     */

1650    private void fillCollectionMetaData(FieldMetaData fmd) {
1651        String JavaDoc msg = null;
1652        JdoExtension[] extensions = null;
1653        Class JavaDoc t;
1654        switch (fmd.category) {
1655            case CATEGORY_COLLECTION:
1656                if (fmd.jdoArray != null) {
1657                    msg = "array";
1658                } else if (fmd.jdoMap != null) msg = "map";
1659                if (msg != null) break;
1660                fmd.ordered = List.class.isAssignableFrom(fmd.type)
1661
1662                        ;
1663                fmd.setElementType(Object JavaDoc.class);
1664                JdoCollection col = fmd.jdoCollection;
1665                if (col != null) {
1666                    extensions = col.extensions;
1667                    t = resolveTypeName(col.elementType,
1668                            col.getElementTypeQName(), "element-type", col);
1669                } else {
1670                    t = null;
1671                    extensions = null;
1672                }
1673                if (t == null) {
1674                    t = MetaDataUtils.getGenericElementType(
1675                            fmd.getReflectField());
1676                    if (t == null) t = Object JavaDoc.class;
1677                }
1678
1679                fmd.setElementType(t);
1680                fmd.elementTypeMetaData = (ClassMetaData)classMap.get(
1681                        fmd.elementType);
1682                if (col != null && col.embeddedElement != NOT_SET) {
1683                    fmd.embeddedElement = col.embeddedElement == TRUE;
1684                }
1685                break;
1686
1687            case CATEGORY_ARRAY:
1688                if (fmd.jdoCollection != null) {
1689                    msg = "collection";
1690                } else if (fmd.jdoMap != null) msg = "map";
1691                if (msg != null) break;
1692                fmd.ordered = true;
1693                fmd.setElementType(fmd.componentType);
1694                fmd.elementTypeMetaData = (ClassMetaData)classMap.get(
1695                        fmd.elementType);
1696                JdoArray ar = fmd.jdoArray;
1697                if (ar != null) {
1698                    extensions = ar.extensions;
1699                    if (ar.embeddedElement != NOT_SET) {
1700                        fmd.embeddedElement = ar.embeddedElement == TRUE;
1701                    }
1702                }
1703                break;
1704
1705            case CATEGORY_MAP:
1706                if (fmd.jdoArray != null) {
1707                    msg = "array";
1708                } else if (fmd.jdoCollection != null) msg = "collection";
1709                if (msg != null) break;
1710                fmd.setElementType(Object JavaDoc.class);
1711                fmd.setKeyType(Object JavaDoc.class);
1712                JdoMap map = fmd.jdoMap;
1713                extensions = map == null ? null : map.extensions;
1714                if (map != null) {
1715                    t = resolveTypeName(map.valueType, map.getValueTypeQName(),
1716                            "value-type", map);
1717                    extensions = map.extensions;
1718                } else {
1719                    t = null;
1720                    extensions = null;
1721                }
1722                if (t == null) {
1723                    t = MetaDataUtils.getGenericValueType(
1724                            fmd.getReflectField());
1725                    if (t == null) t = Object JavaDoc.class;
1726                }
1727                fmd.setElementType(t);
1728                fmd.elementTypeMetaData = (ClassMetaData)classMap.get(
1729                        fmd.elementType);
1730                if (map != null && map.embeddedValue != NOT_SET) {
1731                    fmd.embeddedElement = map.embeddedValue == TRUE;
1732                }
1733                if (map != null) {
1734                    t = resolveTypeName(map.keyType, map.getKeyTypeQName(),
1735                            "key-type", map);
1736                } else {
1737                    t = null;
1738                }
1739                if (t == null) {
1740                    t = MetaDataUtils.getGenericKeyType(fmd.getReflectField());
1741                    if (t == null) t = Object JavaDoc.class;
1742                }
1743                fmd.setKeyType(t);
1744                fmd.keyTypeMetaData = (ClassMetaData)classMap.get(fmd.keyType);
1745                if (map != null && map.embeddedKey != NOT_SET) {
1746                    fmd.embeddedKey = map.embeddedKey == TRUE;
1747                }
1748                break;
1749        }
1750        if (msg != null) {
1751            throw BindingSupportImpl.getInstance().runtime("Element <" + msg + "> is " +
1752                    "not allowed for field " + fmd.name + "\n" +
1753                    fmd.jdoField.getContext());
1754        }
1755
1756        // if field has dependent=true check that this is ok
1757
switch (fmd.category) {
1758            case CATEGORY_COLLECTION:
1759            case CATEGORY_ARRAY:
1760            case CATEGORY_MAP:
1761                // if dependent is true then the element/value must be PC class
1762
if (!fmd.dependentValues || fmd.isElementTypePC()) break;
1763            default:
1764                if (fmd.category == CATEGORY_REF
1765                        || fmd.category == CATEGORY_POLYREF
1766                        || !fmd.dependentValues) {
1767                    break;
1768                }
1769                throw BindingSupportImpl.getInstance().runtime("The dependent extension is only valid for " +
1770                        "references, collections and maps of persistent classes\n" +
1771                        fmd.jdoField.getContext());
1772        }
1773
1774        // if field keys-dependent=true check that this is ok
1775
if (fmd.category == CATEGORY_MAP) {
1776            // if keys-dependent is true then the key must be PC class
1777
if (fmd.dependentKeys && fmd.keyTypeMetaData == null) {
1778                throw BindingSupportImpl.getInstance().runtime("The keys-dependent extension is only valid for " +
1779                        "maps with a persistent class key\n" +
1780                        fmd.jdoField.getContext());
1781            }
1782        } else if (fmd.dependentKeys) {
1783            throw BindingSupportImpl.getInstance().runtime("The keys-dependent extension is only valid for maps\n" +
1784                    fmd.jdoField.getContext());
1785        }
1786
1787        // process extensions
1788
if (extensions != null) {
1789            int n = extensions.length;
1790            JdoExtension ordered = null;
1791            JdoExtension ordering = null;
1792            for (int i = 0; i < n; i++) {
1793                JdoExtension e = extensions[i];
1794                switch (e.key) {
1795                    case JdoExtensionKeys.ORDERED:
1796                        if (!fmd.ordered) {
1797                            throw BindingSupportImpl.getInstance().runtime("The ordered extension is not allowed here\n" +
1798                                    e.getContext());
1799                        }
1800                        ordered = e;
1801                        break;
1802                    case JdoExtensionKeys.ORDERING:
1803                        ordering = e;
1804                        break;
1805                    case JdoExtensionKeys.MANAGED:
1806                        // this is processed later by the JdbcMetaDataBuilder
1807
break;
1808                    default:
1809                        if (e.isCommon()) {
1810                            throw BindingSupportImpl.getInstance().runtime(
1811                                    "Unexpected extension\n" + e.getContext());
1812                        }
1813                }
1814            }
1815            if (ordered != null) fmd.ordered = ordered.getBoolean();
1816            if (ordering != null) {
1817                if (ordered != null && ordered.getBoolean()) {
1818                    throw BindingSupportImpl.getInstance().runtime("You may not specify an ordering if you also have ordered=true\n" +
1819                            ordering.getContext());
1820                }
1821                fmd.ordered = false;
1822                try {
1823                    fmd.ordering = queryParser.parseOrdering(
1824                            fmd.elementTypeMetaData, ordering.getString());
1825                } catch (ParseException e) {
1826                    throw BindingSupportImpl.getInstance().runtime("Invalid ordering extension: " + e.getMessage() + "\n" +
1827                            ordering.getContext() + "\n", e);
1828                }
1829            }
1830        }
1831    }
1832
1833    /**
1834     * Fill in collection, array or map related fields for the meta data.
1835     */

1836    private void setSCOFactory(FieldMetaData fmd) {
1837        if (fmd.checkCustomFactory()) {
1838            return;
1839        }
1840        if (fmd.scoField) {
1841            switch (fmd.category) {
1842                case CATEGORY_SIMPLE:
1843                    if (fmd.simpleSCOFactory == null) {
1844                        fmd.simpleSCOFactory = scoFactoryRegistry.getJdoGenieSCOFactory(
1845                                fmd);
1846                    }
1847                    break;
1848                case CATEGORY_COLLECTION:
1849                    if (fmd.collectionFactory == null) {
1850                        fmd.collectionFactory = scoFactoryRegistry.getJDOGenieSCOCollectionFactory(
1851                                fmd);
1852                    }
1853                    break;
1854                case CATEGORY_MAP:
1855                    if (fmd.mapFactory == null) {
1856                        fmd.mapFactory = scoFactoryRegistry.getJDOGenieSCOMapFactory(
1857                                fmd);
1858                    }
1859                    break;
1860            }
1861        }
1862    }
1863
1864    /**
1865     * Get the ClassInfo for cmd. A new ClassInfo will be created if none
1866     * exists.
1867     */

1868    private ClassInfo getClassInfo(ClassMetaData cmd) {
1869        ClassInfo ans = (ClassInfo)classInfoMap.get(cmd);
1870        if (ans == null) classInfoMap.put(cmd, ans = new ClassInfo());
1871        return ans;
1872    }
1873
1874    /**
1875     * Fill the externalizerMap from the configuration.
1876     */

1877    private void fillExternalizerMap() {
1878        externalizerMap.clear();
1879        int n = config.externalizers.size();
1880        for (int i = 0; i < n; i++) {
1881            ConfigInfo.ExternalizerInfo ei =
1882                    (ConfigInfo.ExternalizerInfo)config.externalizers.get(i);
1883            if (!ei.enabled) continue;
1884            try {
1885                Class JavaDoc key = loadClass(ei.typeName);
1886                Class JavaDoc cls = loadExternalizerClass(ei.externalizerName);
1887                externalizerMap.put(key, createExternalizer(cls, key, ei.args));
1888                mdutils.registerExternalizedType(key);
1889            } catch (Throwable JavaDoc x) {
1890                RuntimeException JavaDoc e;
1891                if (BindingSupportImpl.getInstance().isOwnException(x)) {
1892                    e = (RuntimeException JavaDoc)x;
1893                } else {
1894                    e = BindingSupportImpl.getInstance().runtime("Unable to create Externalizer for '" +
1895                            ei.typeName + "':\n" + x.toString(), x);
1896                }
1897                jmd.addError(e, quiet);
1898            }
1899        }
1900    }
1901
1902    private Externalizer createExternalizer(Class JavaDoc externalizerCls,
1903            Class JavaDoc type, Map props) throws IllegalAccessException JavaDoc,
1904            InstantiationException JavaDoc, InvocationTargetException {
1905        Externalizer externalizer;
1906        try {
1907            Constructor m = externalizerCls.getConstructor(
1908                    new Class JavaDoc[]{Class JavaDoc.class});
1909            externalizer = (Externalizer)m.newInstance(new Object JavaDoc[]{type});
1910        } catch (NoSuchMethodException JavaDoc e) {
1911            externalizer = (Externalizer)externalizerCls.newInstance();
1912        }
1913        BeanUtils.setProperties(externalizer, props);
1914        return externalizer;
1915    }
1916
1917    /**
1918     * Load an externalizer class. This will recognize the short names of
1919     * the built in externalizers and returns the default externalizer for
1920     * a null name.
1921     */

1922    private Class JavaDoc loadExternalizerClass(String JavaDoc name)
1923            throws ClassNotFoundException JavaDoc {
1924        if (name == null || SerializedExternalizer.SHORT_NAME.equals(name)) {
1925            return SerializedExternalizer.class;
1926        } else if (TypeAsBytesExternalizer.SHORT_NAME.equals(name)) {
1927            return TypeAsBytesExternalizer.class;
1928        } else if (TypeAsStringExternalizer.SHORT_NAME.equals(name)) {
1929            return TypeAsStringExternalizer.class;
1930        }
1931        return loadClass(name);
1932    }
1933
1934    /**
1935     * Get a externalizer instance for a field type or Serializing Externalizer if null.
1936     */

1937    private Externalizer getExternalizerForType(Class JavaDoc type) {
1938        Externalizer ex = (Externalizer) externalizerMap.get(type);
1939        if (ex == null && Serializable JavaDoc.class.isAssignableFrom(type)) {
1940            try {
1941                ex = createExternalizer(SerializedExternalizer.class, type, null);
1942            } catch (Exception JavaDoc e) {
1943                throw BindingSupportImpl.getInstance().internal(e.getMessage(), e);
1944            }
1945        }
1946        return ex;
1947    }
1948
1949    /**
1950     * Create a Externalizer instance from an extension. This will return
1951     * instances of standard externalizers if their SHORT_NAME's are used.
1952     */

1953    private Externalizer createExternalizer(Class JavaDoc type, JdoExtension e) {
1954        try {
1955            String JavaDoc cname = e.getString();
1956            Class JavaDoc cls = loadExternalizerClass(cname);
1957            return createExternalizer(cls, type, e.getPropertyMap());
1958        } catch (Throwable JavaDoc x) {
1959            x = BindingSupportImpl.getInstance().findCause(x);
1960            if (BindingSupportImpl.getInstance().isOwnException(x)) {
1961                throw (RuntimeException JavaDoc)x;
1962            }
1963            throw BindingSupportImpl.getInstance().runtime(x.toString(), x);
1964        }
1965    }
1966
1967    /**
1968     * Should the current state information be sent with a request to load
1969     * any fetch group containing secondary fields?
1970     */

1971    public boolean isSendCurrentForFGWithSecFields() {
1972        return false;
1973    }
1974
1975    /**
1976     * When writing to an object must it be completely read first?
1977     */

1978    public boolean isReadObjectBeforeWrite() {
1979        return false;
1980    }
1981}
1982
Popular Tags