KickJava   Java API By Example, From Geeks To Geeks.

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


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.*;
15 import com.versant.core.util.classhelper.ClassHelper;
16 import com.versant.core.util.IntArray;
17
18 import javax.jdo.spi.JDOImplHelper;
19 import java.io.PrintStream JavaDoc;
20 import java.io.Serializable JavaDoc;
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.util.*;
24
25 import com.versant.core.common.BindingSupportImpl;
26 import com.versant.core.jdo.VersantOid;
27
28 /**
29  * This holds the meta data for all persistent classes.
30  */

31 public final class ModelMetaData implements Serializable JavaDoc {
32
33     /**
34      * This is only used for refFields. If the ref Field is not found then return a null
35      * instead of a VersantObjectNotFoundException. This is a projectLevel setting
36      */

37     public boolean returnNullForRowNotFound;
38     /**
39      * The persistent classes.
40      */

41     public ClassMetaData[] classes;
42     /**
43      * This maps objectid-class'es to their ClassMetaData. It is used to
44      * find the class for an application identity class.
45      */

46     private final HashMap objectIdClassMap = new HashMap();
47     /**
48      * The max size of the fields array for any class in this meta data.
49      */

50     public int maxFieldsLength;
51     /**
52      * Extra JDBC specific meta data (null if no JdbcDataStore is in use).
53      */

54     public transient Object JavaDoc jdbcMetaData;
55     /**
56      * Extra VDS specific meta data (null if no VdsDataStore is in use).
57      */

58     public transient Object JavaDoc vdsModel;
59     /**
60      * When an instance is deleted should its state (if available) be included
61      * in the DeletePacket? If this is false then only the OID is included.
62      */

63     public boolean sendStateOnDelete;
64     /**
65      * Maps class name to resource name for the enhancer.
66      */

67     public HashMap classResourceMap = new HashMap();
68
69     private transient RuntimeException JavaDoc error;
70
71     /**
72      * This is true if we are running unit tests. Some checks are relaxed
73      * so the same model can be used for JDBC and VDS (e.g. if this is true
74      * then collections with no element-type set are allowed).
75      */

76     public transient boolean testing;
77
78
79     private static final ThreadLocal JavaDoc META_DATA = new ThreadLocal JavaDoc();
80
81     private final Class JavaDoc[] EMPTY_CLASS_ARRAY = new Class JavaDoc[0];
82
83     private Map abstractSchemaNameMap; // abstract schema name -> ClassMetaData
84

85     private StateAndOIDFactory untypedOIDFactory = new StateAndOIDFactory() {
86         public OID createOID(ClassMetaData cmd, boolean resolved) {
87             throw BindingSupportImpl.getInstance().internal("");
88         }
89         public State createState(ClassMetaData cmd) {
90             throw BindingSupportImpl.getInstance().internal("");
91         }
92         public NewObjectOID createNewObjectOID(ClassMetaData cmd) {
93             throw BindingSupportImpl.getInstance().internal("");
94         }
95         public OID createUntypedOID() {
96             throw BindingSupportImpl.getInstance().unsupported(
97                     "Untyped OIDs are not supported by the datastore");
98         }
99     };
100
101     public ModelMetaData() {
102     }
103
104     /**
105      * Get the JDOMetaData instance associated with the current thread. This
106      * is used during deserialization.
107      */

108     public static ModelMetaData getThreadMetaData() {
109
110         return (ModelMetaData)META_DATA.get();
111
112
113     }
114
115     /**
116      * Associate meta data with the current thread. This is used during
117      * deserialization.
118      */

119
120     public static void setThreadMetaData(ModelMetaData jmd) {
121         META_DATA.set(jmd);
122     }
123
124
125     /**
126      * Get meta data for the class by classId or null if not found.
127      */

128     public ClassMetaData getClassMetaData(int classId) {
129         // do a binary search since classes is sorted by classId
130
int low = 0;
131         int high = classes.length - 1;
132         while (low <= high) {
133             int mid = (low + high) / 2;
134             ClassMetaData midVal = classes[mid];
135             int midValClassId = midVal.classId;
136             if (midValClassId < classId) {
137                 low = mid + 1;
138             } else if (midValClassId > classId) {
139                 high = mid - 1;
140             } else {
141                 return midVal;
142             }
143         }
144         return null;
145     }
146
147     /**
148      * Get the meta data for the class or null if not found.
149      */

150     public ClassMetaData getClassMetaData(Class JavaDoc cls) {
151         for (int i = classes.length - 1; i >= 0; i--) {
152             ClassMetaData cmd = classes[i];
153             if (cmd.cls == cls) return cmd;
154         }
155         return null;
156     }
157
158     /**
159      * Get the meta data for the class or null if not found.
160      *
161      * @param qname Fully qualified class name
162      */

163     public ClassMetaData getClassMetaData(String JavaDoc qname) {
164         for (int i = classes.length - 1; i >= 0; i--) {
165             ClassMetaData cmd = classes[i];
166             if (cmd.qname.equals(qname)) return cmd;
167         }
168         return null;
169     }
170
171     /**
172      * Get the meta data for the class relative to package of base or null if
173      * not found.
174      */

175     public ClassMetaData getClassMetaData(ClassMetaData base, String JavaDoc className) {
176         ClassMetaData ans = getClassMetaData(className);
177         if (ans == null) {
178             ans = getClassMetaData(base.packageNameWithDot + className);
179         }
180         return ans;
181     }
182
183     /**
184      * Build the abstract schema name -> ClassMetaData map. This throws
185      * a JDOUserException if there are any duplicates.
186      */

187     public void buildAbstractSchemaNameMap() {
188         abstractSchemaNameMap = new HashMap();
189         for (int i = classes.length - 1; i >= 0; i--) {
190             ClassMetaData cmd = classes[i];
191             ClassMetaData dup = (ClassMetaData)abstractSchemaNameMap.put(
192                     cmd.abstractSchemaName, cmd);
193             if (dup != null) {
194                 // todo HACK ignore for now
195
// throw BindingSupportImpl.getInstance().invalidOperation(
196
// cmd.qname + " and " + dup.qname +
197
// " have same abstract schema name '" +
198
// cmd.abstractSchemaName + "'");
199
}
200         }
201     }
202
203     /**
204      * Lookup a class by its abstract schema name or null if not found.
205      */

206     public ClassMetaData getClassMetaByASN(String JavaDoc abstractSchemaName) {
207         return (ClassMetaData)abstractSchemaNameMap.get(abstractSchemaName);
208     }
209
210
211
212
213     public void dump() {
214         dump(Debug.OUT, "");
215     }
216
217     public void dump(PrintStream JavaDoc out, String JavaDoc indent) {
218         out.println(indent + this);
219         String JavaDoc is = indent + " ";
220         for (int i = 0; i < classes.length; i++) {
221             classes[i].dump(out, is);
222         }
223     }
224
225     /**
226      * Return a list of all classes that are not PersistenceCapable i.e.
227      * that have not been enhanced.
228      */

229     public List findNonPCClassNames() {
230         ArrayList a = new ArrayList();
231         int n = classes.length;
232         for (int i = 0; i < n; i++) {
233             if (!classes[i].isPersistenceCapable()) a.add(classes[i].qname);
234         }
235         return a;
236     }
237
238     /**
239      * Check for classes that are not PersistenceCapable i.e. that have not
240      * been enhanced and throw a JDOFatalUserException if there are any.
241      */

242     public void checkForNonPCClasses() {
243         List nonPCList = findNonPCClassNames();
244         if (!nonPCList.isEmpty()) {
245             StringBuffer JavaDoc s = new StringBuffer JavaDoc();
246             s.append("One or more classes in the JDO meta data have not " +
247                     "been enhanced:");
248             int n = nonPCList.size();
249             int i;
250             for (i = 0; i < n && i < 10; i++) {
251                 s.append('\n');
252                 s.append(nonPCList.get(i));
253             }
254             if (i < n) s.append("\n...");
255             throw BindingSupportImpl.getInstance().runtime(s.toString());
256         }
257     }
258
259     /**
260      * Build objectIdClassMap mapping objectid-class'es to the corresponding
261      * ClassMetaData.
262      *
263      * @see #getClassMetaDataForObjectIdClass
264      */

265     public void buildObjectIdClassMap() {
266         int n = classes.length;
267         for (int i = 0; i < n; i++) {
268             ClassMetaData cmd = classes[i];
269             if (cmd.pcSuperMetaData == null && cmd.objectIdClass != null) {
270                 objectIdClassMap.put(cmd.objectIdClass, cmd);
271             }
272         }
273     }
274
275     /**
276      * Get the ClassMetaData for an objectid-class or null if none.
277      *
278      * @see #buildObjectIdClassMap
279      */

280     public ClassMetaData getClassMetaDataForObjectIdClass(Class JavaDoc cls) {
281         return (ClassMetaData)objectIdClassMap.get(cls);
282     }
283
284     /**
285      * Make sure all persistent classes are registered for JDO by creating
286      * an instance of each one. Errors are silently ignored.
287      */

288     public void forceClassRegistration() {
289         HashMap classMap = new HashMap();
290         ClassMetaData[] cmds = classes;
291         for (int i = 0; i < cmds.length; i++) {
292             ClassMetaData cmd = cmds[i];
293             if (cmd.pcSuperClass == null){
294                 initClass(cmd.cls);
295                 classMap.put(cmd.cls, null);
296                 ClassMetaData[] subCmds = cmd.pcSubclasses;
297                 if (subCmds != null){
298                     for (int j = 0; j < subCmds.length; j++) {
299                         initClass(subCmds[j].cls);
300                         classMap.put(subCmds[j].cls, null);
301                     }
302                 }
303             }
304         }
305         for (int i = 0; i < cmds.length; i++) {
306             if (!classMap.containsKey(cmds[i].cls)){
307                 initClass(cmds[i].cls);
308             }
309         }
310     }
311
312     /**
313      * Create a instance of the given class
314      */

315     private void initClass(Class JavaDoc cls){
316         try {
317             if (!Modifier.isAbstract(cls.getModifiers())) {
318                 Constructor JavaDoc cons = cls.getDeclaredConstructor(null);
319                 ClassHelper.get().setAccessible(cons, true);
320                 cons.newInstance(null);
321             }
322         } catch (Exception JavaDoc e) {
323             //ignore
324
}
325     }
326
327     /**
328      * Convert an array of classes into an array of class indexes.
329      *
330      * @throws javax.jdo.JDOUserException if any classes are not persistent
331      */

332     public int[] convertToClassIndexes(Class JavaDoc[] classes,
333             boolean includeSubclasses) {
334         if (includeSubclasses) {
335             int n = classes.length;
336             IntArray a = new IntArray(16);
337             for (int i = 0; i < n; i++) {
338                 ClassMetaData cmd = getClassMetaData(classes[i]);
339                 if (cmd == null) {
340                     throw BindingSupportImpl.getInstance().invalidOperation("Not a persistent class: " +
341                             classes[i].getName());
342                 }
343                 cmd.findHeirachyIndexes(a);
344             }
345             return a.toArray();
346         } else {
347             int n = classes.length;
348             int[] a = new int[n];
349             for (int i = 0; i < n; i++) {
350                 ClassMetaData cmd = getClassMetaData(classes[i]);
351                 if (cmd == null) {
352                     throw BindingSupportImpl.getInstance().invalidOperation("Not a persistent class: " +
353                             classes[i].getName());
354                 }
355                 a[i] = cmd.index;
356             }
357             return a;
358         }
359     }
360
361     /**
362      * Convert an array of class indexes into an array of classes.
363      *
364      * @throws javax.jdo.JDOUserException if any class indexes are invalid
365      */

366     public Class JavaDoc[] convertFromClassIndexes(int[] classIndexes) {
367         int n = classIndexes.length;
368         Class JavaDoc[] ans = new Class JavaDoc[n];
369         int max = classes.length;
370         for (int i = 0; i < n; i++) {
371             int ci = classIndexes[i];
372             if (ci < 0 || ci >= max) {
373                 throw BindingSupportImpl.getInstance().invalidOperation("Invalid class index: " + ci);
374             }
375             ans[i] = classes[ci].cls;
376         }
377         return ans;
378     }
379
380     /**
381      * Check the consistency of the meta data. This will try and validate parts
382      * of the data structure against other parts to find bugs.
383      */

384     public void validate() {
385         for (int i = 0; i < classes.length; i++) {
386             classes[i].validate();
387         }
388     }
389
390     /**
391      * Cleanup any data structures not needed after meta data generation.
392      */

393     public void cleanupAfterMetaDataGeneration() {
394         for (int i = 0; i < classes.length; i++) {
395             classes[i].cleanupAfterMetaDataGeneration();
396         }
397     }
398
399     /**
400      * Create an OID instance from a datastore identity String.
401      *
402      * @param resolved Mark the OID as resolved (exact class known) or not
403      */

404     public OID newOIDFromIDString(String JavaDoc value, boolean resolved) {
405         try {
406             char c = value.charAt(0);
407             int cid = c - '0';
408             int i = 1;
409             for (; ;) {
410                 c = value.charAt(i++);
411                 if (c == MDStatics.OID_CHAR_SEPERATOR) break;
412                 cid = cid * 10 + (c - '0');
413             }
414             ClassMetaData cmd = getClassMetaData(cid);
415             if (cmd == null) {
416                 throw BindingSupportImpl.getInstance().invalidOperation("Invalid OID String (bad class ID): '" +
417                         value + "'");
418             }
419             if (cmd.identityType != MDStatics.IDENTITY_TYPE_DATASTORE) {
420                 throw BindingSupportImpl.getInstance().invalidOperation("Class " + cmd.qname + " for class-id " + cid +
421                         " does not use datastore identity");
422             }
423             resolved = (resolved || (cmd.pcSubclasses == null));
424             OID oid = cmd.createOID(resolved || cmd.pcSubclasses == null);
425             oid.fillFromIDString(value, i);
426             return oid;
427         } catch (RuntimeException JavaDoc e) {
428             if( BindingSupportImpl.getInstance().isOwnException(e) )
429             {
430                 throw e;
431             }
432             else
433             {
434                 throw BindingSupportImpl.getInstance().invalidOperation("Invalid OID String: '" +
435                     value + "'", e);
436             }
437         }
438     }
439
440     /**
441      * Convert an internal OID to a String that can be parsed by
442      * {@link #newOIDFromExternalString(java.lang.String)}. This works for
443      * datastore and application identity classes.
444      */

445     public String JavaDoc toExternalString(OID oid) {
446         StringBuffer JavaDoc b = new StringBuffer JavaDoc();
447         ClassMetaData cmd = oid.getAvailableClassMetaData();
448         if (cmd.top.objectIdClass == null) {
449             b.append('d');
450             b.append(' ');
451             b.append(oid);
452         } else {
453             b.append('a');
454             b.append(' ');
455             b.append(cmd.classIdString);
456             b.append(' ');
457             Object JavaDoc o;
458             try {
459                 o = cmd.top.objectIdClass.newInstance();
460             } catch (Exception JavaDoc e) {
461                 throw BindingSupportImpl.getInstance().internal(e.toString(),
462                         e);
463             }
464             oid.populateObjectIdClassInstance(o);
465             b.append(o);
466         }
467         String JavaDoc ans = b.toString();
468         if (Debug.DEBUG) {
469             OID oid2 = newOIDFromExternalString(ans);
470             if (!oid.equals(oid2)) {
471                 throw BindingSupportImpl.getInstance().internal(
472                         "string does not parse properly for " + oid);
473             }
474         }
475         return ans;
476     }
477
478     /**
479      * Create an internal OID from a String previously created with
480      * {@link #toExternalString(com.versant.core.common.OID)}.
481      */

482     public OID newOIDFromExternalString(String JavaDoc s) {
483         switch (s.charAt(0)) {
484             case 'd':
485                 return newOIDFromIDString(s.substring(2), true);
486             case 'a':
487                 int i = s.indexOf(' ', 2);
488                 int cid = Integer.parseInt(s.substring(2, i));
489                 ClassMetaData cmd = getClassMetaData(cid);
490                 if (cmd == null) {
491                     throw BindingSupportImpl.getInstance().invalidOperation("Invalid string: '" + s + "', no class found for " +
492                             "class-id: " + cid);
493                 }
494                 Object JavaDoc o;
495                 try {
496                     o = JDOImplHelper.getInstance().newObjectIdInstance(
497                             cmd.cls, s.substring(i + 1));
498                 } catch (Exception JavaDoc e) {
499                     if( BindingSupportImpl.getInstance().isOwnException(e) )
500                     {
501                         throw (RuntimeException JavaDoc) e;
502                     }
503                     else
504                     {
505                         throw BindingSupportImpl.getInstance().invalidOperation(
506                             "Invalid string: '" + s + "': " + e, e);
507                     }
508                 }
509                 OID oid = cmd.createOID(true);
510                 oid.fillFromPK(o);
511                 return oid;
512             default:
513                 throw BindingSupportImpl.getInstance().invalidOperation("Invalid string: '" + s + "'");
514         }
515     }
516
517     public void addError(RuntimeException JavaDoc e, boolean quiet) {
518         if (error == null) {
519             error = e;
520             try {
521                 Thread.sleep(1);
522             } catch (InterruptedException JavaDoc e1) {
523                 // ignore
524
}
525         }
526         if (!quiet) throw e;
527     }
528
529     public boolean hasErrors() {
530         if (error != null) {
531             return true;
532         }
533         for (int i = 0; i < classes.length; i++) {
534             if (classes[i].hasErrors()) return true;
535         }
536         return false;
537     }
538
539     public RuntimeException JavaDoc getFirstError() {
540         if (error != null) {
541             return error;
542         }
543         for (int i = 0; i < classes.length; i++) {
544             RuntimeException JavaDoc e = classes[i].getFirstError();
545             if (e != null) return e;
546         }
547         return null;
548     }
549
550     /**
551      * Convert an instance of an application identity class into an OID.
552      */

553     public OID convertFromAppIdToOID(Object JavaDoc appId) {
554         OID oid = null;
555         ClassMetaData cmd = getClassMetaDataForObjectIdClass(appId.getClass());
556         if (cmd == null) {
557             throw BindingSupportImpl.getInstance().invalidOperation("Instance is not an objectid-class for any persistent classes: " +
558                     appId.getClass().getName() + ": " + toString(appId));
559         }
560         oid = cmd.createOID(false);
561         oid.fillFromPK(appId);
562         return oid;
563     }
564
565     /**
566      * Converts an instance of a VersantOid to an internal OID.
567      */

568     public OID convertJDOGenieOIDtoOID(VersantOid versantOid) {
569         if (versantOid.actualOID != null) {
570             return versantOid.actualOID.getAvailableOID();
571         }
572         ClassMetaData cmd = getClassMetaData(versantOid.classId);
573         if (cmd == null) {
574             throw BindingSupportImpl.getInstance().invalidOperation("Invalid classID in VersantOid: "
575                     + versantOid);
576         }
577         OID oid = cmd.createOID(true);
578         oid.setLongPrimaryKey(versantOid.pk);
579         return versantOid.actualOID = oid;
580     }
581
582     /**
583      * Converts an instance of a JDOGenieOID, application identity
584      * ID instance or internal OID to an internal OID.
585      */

586     public OID convertToOID(Object JavaDoc oid) {
587         if (oid instanceof VersantOid) {
588             return convertJDOGenieOIDtoOID((VersantOid)oid);
589         } else if (oid instanceof OID) {
590             return (OID)oid;
591         } else {
592             return convertFromAppIdToOID(oid);
593         }
594     }
595
596     /**
597      * Converts an array of a JDOGenieOID's, application identity ID
598      * instances or OIDs to an array of internal OID.
599      */

600     public OID[] convertToOID(Object JavaDoc[] oids, int n) {
601         OID[] a = new OID[n];
602         for (int i = 0; i < n; i++) {
603             Object JavaDoc oid = oids[i];
604             if (oid instanceof VersantOid) {
605                 a[i] = convertJDOGenieOIDtoOID((VersantOid)oid);
606             } else if (oid instanceof OID) {
607                 a[i] = (OID)oid;
608             } else if (oid != null) {
609                 a[i] = convertFromAppIdToOID(oid);
610             }
611         }
612         return a;
613     }
614
615     /**
616      * Safely do toString on o. If there is an exception then return a message
617      * indicating that toString() failed including the exception.
618      */

619     private static String JavaDoc toString(Object JavaDoc o) {
620         if (o == null) return "null";
621         try {
622             return o.toString();
623         } catch (Exception JavaDoc e) {
624             return o.getClass().getName() + ".toString() failed: " + e;
625         }
626     }
627
628     /**
629      * Get all the ClassMetaData for the heirachy rooted at base.
630      */

631     public ClassMetaData[] getClassMetaDataForHeirachy(ClassMetaData base) {
632         if (base.pcSubclasses == null) {
633             return new ClassMetaData[]{base};
634         }
635         ArrayList a = new ArrayList();
636         getClassMetaDataForHeirachyImp(base, a);
637         ClassMetaData[] ans = new ClassMetaData[a.size()];
638         a.toArray(ans);
639         return ans;
640     }
641
642     private void getClassMetaDataForHeirachyImp(ClassMetaData cmd, ArrayList a) {
643         a.add(cmd);
644         if (cmd.pcSubclasses != null) {
645             for (int i = 0; i < cmd.pcSubclasses.length; i++) {
646                 getClassMetaDataForHeirachyImp(cmd.pcSubclasses[i], a);
647             }
648         }
649     }
650
651     /**
652      * Create an unresolved OID for the class for classIndex.
653      */

654     public OID createUnresolvedOID(int classIndex) {
655         return classes[classIndex].createOID(false);
656     }
657
658    /**
659      * Set the factory used to create untyped OIDs. The default factory throws
660      * an unsupported option exception.
661      */

662     public void setUntypedOIDFactory(StateAndOIDFactory untypedOIDFactory) {
663         this.untypedOIDFactory = untypedOIDFactory;
664     }
665
666     /**
667      * Create a new untyped OID if the store supports this or throw an
668      * unsupported option exception if not.
669      */

670     public OID createUntypedOID() {
671         return untypedOIDFactory.createUntypedOID();
672     }
673
674     private Map candidatesForClsMap = new HashMap();
675
676     /**
677      * Return all the root candidate classes for persistent heirarchies that implement
678      * or extend the supplied class.
679      */

680     public synchronized Class JavaDoc[] getQueryCandidatesFor(Class JavaDoc cls) {
681         //if class is an interface and the interface is not a persistent interface
682
//then find all the persistent classes that implement it.
683
//for each of these classes we must find there root persistent object.
684
Class JavaDoc[] clsArray = (Class JavaDoc[]) candidatesForClsMap.get(cls);
685         if (clsArray == null) {
686             ClassMetaData cmd = getClassMetaData(cls);
687             if (cmd != null && !cmd.horizontal) {
688                 //this class is persistent and is not horizontally mapped so just return intself
689
clsArray = new Class JavaDoc[] {cls};
690             } else {
691                 Set result = new HashSet();
692                 if (cls.isInterface()) {
693                     throw BindingSupportImpl.getInstance().unsupported(
694                             "Query by interface is not currently supported");
695                 } else {
696                     for (int i = 0; i < classes.length; i++) {
697                         ClassMetaData aClass = classes[i];
698                         if (cls.equals(aClass.cls.getSuperclass())) {
699                             result.add(aClass.cls);
700                         }
701                     }
702                 }
703                 if (result.isEmpty()) {
704                     clsArray = EMPTY_CLASS_ARRAY;
705                 } else {
706                     clsArray = new Class JavaDoc[result.size()];
707                     result.toArray(clsArray);
708                 }
709             }
710             candidatesForClsMap.put(cls, clsArray);
711         }
712         return clsArray;
713     }
714
715 }
716
Popular Tags