0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.metadata;
0012:
0013: import com.versant.core.common.Debug;
0014: import com.versant.core.common.config.ConfigInfo;
0015: import com.versant.core.jdo.QueryDetails;
0016: import com.versant.core.jdo.sco.VersantSCOFactoryRegistry;
0017: import com.versant.core.metadata.parser.*;
0018: import com.versant.core.jdo.query.ParseException;
0019: import com.versant.core.jdo.query.QueryParser;
0020: import com.versant.core.jdo.externalizer.Externalizer;
0021: import com.versant.core.jdo.externalizer.SerializedExternalizer;
0022: import com.versant.core.jdo.externalizer.TypeAsBytesExternalizer;
0023: import com.versant.core.jdo.externalizer.TypeAsStringExternalizer;
0024: import com.versant.core.util.classhelper.ClassHelper;
0025: import com.versant.core.util.BeanUtils;
0026: import com.versant.core.util.IntArray;
0027:
0028: import java.io.Serializable;
0029: import java.lang.reflect.*;
0030: import java.util.*;
0031:
0032: import com.versant.core.common.BindingSupportImpl;
0033:
0034: /**
0035: * This will parse one or more .jdo files and load and analyze the persistent
0036: * classes to produce basic meta data. There are different subclasses for
0037: * different StorageManager's.
0038: */
0039: public class MetaDataBuilder implements MDStatics {
0040:
0041: protected final ConfigInfo config;
0042: protected final ClassLoader loader;
0043: protected final boolean quiet;
0044: protected final ModelMetaData jmd;
0045:
0046: private MetaDataPreProcessor metaDataPreProcessor;
0047:
0048: private final MetaDataEnums MDE = new MetaDataEnums();
0049:
0050: protected final MetaDataUtils mdutils = new MetaDataUtils();
0051: protected final VersantSCOFactoryRegistry scoFactoryRegistry;
0052:
0053: private Map appIdUniqueMap = new HashMap();
0054: private Map externalizerMap = new HashMap();
0055:
0056: private PackageMetaData[] packageMetaData;
0057: private HashMap classMap = new HashMap(); // Class -> ClassMetaData
0058: private HashMap classInfoMap = new HashMap(); // Class -> ClassInfo
0059: private HashMap interfaceMap = new HashMap(); // Class -> InterfaceMetaData
0060: private HashMap classAndInterfaceMap = new HashMap();
0061: // Class -> ClassMetaData/InterfaceMetaData
0062:
0063: private final FetchGroupBuilder fetchGroupBuilder;
0064: private final QueryParser queryParser;
0065: private static final String ARRAY_SUFFIX = "[]";
0066:
0067: /**
0068: * Info extracted from package extensions. These form defaults that
0069: * apply to all classes in the package unless overridden at class level.
0070: */
0071: private static class PackageMetaData {
0072:
0073: public String nameWithDot;
0074: public JdoPackage jdoPackage;
0075: }
0076:
0077: /**
0078: * Info extracted from class extensions that is only needed during
0079: * meta data generation.
0080: */
0081: private static class ClassInfo {
0082:
0083: public boolean refsInDefaultFetchGroup;
0084: public JdoClass jdoClass;
0085: public ArrayList horizontalFields;
0086: }
0087:
0088: public MetaDataBuilder(ConfigInfo config, ClassLoader loader,
0089: boolean quiet) {
0090: this .config = config;
0091: this .loader = loader;
0092: this .quiet = quiet;
0093: jmd = new ModelMetaData();
0094: jmd.testing = config.testing;
0095: fetchGroupBuilder = createFetchGroupBuilder();
0096: queryParser = new QueryParser(jmd);
0097: scoFactoryRegistry = new VersantSCOFactoryRegistry(
0098: config.scoFactoryRegistryMappings, loader);
0099:
0100: if (config.metaDataPreProcessor == null)
0101: config.metaDataPreProcessor = EJB_JDBC_PRE_PROCESSOR;
0102:
0103: if (config.metaDataPreProcessor != null) {
0104: try {
0105: Class cls = loadClass(config.metaDataPreProcessor);
0106: metaDataPreProcessor = (MetaDataPreProcessor) cls
0107: .getConstructor(
0108: new Class[] { ClassLoader.class,
0109: MetaDataUtils.class })
0110: .newInstance(new Object[] { loader, mdutils });
0111: } catch (Throwable e) {
0112: System.out
0113: .println("Unable to load 'metaDataPreProcessor' class '"
0114: + config.metaDataPreProcessor + "'");
0115: }
0116: }
0117: }
0118:
0119: protected FetchGroupBuilder createFetchGroupBuilder() {
0120: return new FetchGroupBuilder(jmd,
0121: isSendCurrentForFGWithSecFields(),
0122: isReadObjectBeforeWrite());
0123: }
0124:
0125: public FetchGroupBuilder getFetchGroupBuilder() {
0126: return fetchGroupBuilder;
0127: }
0128:
0129: /**
0130: * Create meta data for already parsed .jdo meta data.
0131: */
0132: public ModelMetaData buildMetaData(JdoRoot[] roots) {
0133:
0134: // populate the externalizer map
0135: fillExternalizerMap();
0136:
0137: // combine all the meta data into a single structure
0138: JdoRoot jdoRoot = buildSingleJdoRoot(roots);
0139:
0140: // pull out some package level defaults
0141: int n = jdoRoot.packages.length;
0142: packageMetaData = new PackageMetaData[n];
0143: for (int i = 0; i < n; i++) {
0144: packageMetaData[i] = createPackageMetaData(jdoRoot.packages[i]);
0145: }
0146:
0147: // create initial meta data for all classes
0148: if (Debug.DEBUG) {
0149: System.out
0150: .println("MDB: Creating initial meta data for all classes and interfaces");
0151: }
0152:
0153: ClassMetaData[] classes = jmd.classes = new ClassMetaData[jmd.classResourceMap
0154: .size()];
0155: for (int i = 0, c = 0; i < packageMetaData.length; i++) {
0156: PackageMetaData pmd = packageMetaData[i];
0157: JdoClass[] ca = pmd.jdoPackage.classes;
0158: for (int j = 0; j < ca.length; j++) {
0159: if (metaDataPreProcessor != null)
0160: metaDataPreProcessor.process(ca[j]);
0161: ClassMetaData cmd = createMetaData(pmd, ca[j], quiet);
0162: classes[c++] = cmd;
0163: classMap.put(cmd.cls, cmd);
0164: }
0165: JdoExtension[] extensions = pmd.jdoPackage.extensions;
0166: if (extensions != null) {
0167: for (int j = 0; j < extensions.length; j++) {
0168: JdoExtension e = extensions[j];
0169: if (e.key == JdoExtensionKeys.INTERFACE) {
0170: InterfaceMetaData imd = createInterfaceMetaData(
0171: pmd, e);
0172: interfaceMap.put(imd.cls, imd);
0173: }
0174: }
0175: }
0176: }
0177: jmd.buildObjectIdClassMap();
0178: jmd.buildAbstractSchemaNameMap();
0179: classAndInterfaceMap.putAll(classMap);
0180: classAndInterfaceMap.putAll(interfaceMap);
0181:
0182: // sort the classes by name and generate classId's - the name sort
0183: // ensures that the classIds are allocated deterministicly
0184: if (Debug.DEBUG) {
0185: System.out
0186: .println("MDB: Sorting classes by name and generating classIds");
0187: }
0188: Arrays.sort(classes, new Comparator() {
0189: public int compare(Object aa, Object bb) {
0190: ClassMetaData a = (ClassMetaData) aa;
0191: ClassMetaData b = (ClassMetaData) bb;
0192: return a.qname.compareTo(b.qname);
0193: }
0194: });
0195: int clen = classes.length;
0196: for (int i = 0; i < clen; i++) {
0197: ClassMetaData c = classes[i];
0198: c.setClassId(mdutils.generateClassId(c.qname));
0199: }
0200:
0201: // fill in the pcClassMetaData field for all classes
0202: if (Debug.DEBUG)
0203: System.out.println("MDB: Filling pcClassMetaData");
0204: for (int i = 0; i < clen; i++) {
0205: ClassMetaData c = classes[i];
0206: Class pcs = c.pcSuperClass;
0207: if (pcs == null)
0208: continue;
0209: c.pcSuperMetaData = (ClassMetaData) classMap.get(pcs);
0210: if (c.pcSuperMetaData == null) {
0211: RuntimeException x = BindingSupportImpl.getInstance()
0212: .runtime(
0213: "persistence-capable-superclass not declared in meta data: "
0214: + pcs.getName() + "\n"
0215: + c.jdoClass.getContext());
0216: c.addError(x, quiet);
0217: } else if (c.pcSuperMetaData.horizontal) {
0218: c.horizontalCMD = c.pcSuperMetaData;
0219: c.pcSuperMetaData = null;
0220: }
0221: }
0222:
0223: // build the pcSubclasses arrays
0224: if (Debug.DEBUG) {
0225: System.out.println("MDB: Building pcSubclasses arrays");
0226: }
0227: ArrayList a = new ArrayList();
0228: for (int i = 0; i < clen; i++) {
0229: ClassMetaData c = classes[i];
0230: a.clear();
0231: for (int j = 0; j < clen; j++) {
0232: ClassMetaData sc = classes[j];
0233: if (sc.pcSuperMetaData == c)
0234: a.add(sc);
0235: }
0236: int len = a.size();
0237: if (len > 0) {
0238: c.pcSubclasses = new ClassMetaData[len];
0239: a.toArray(c.pcSubclasses);
0240: }
0241: }
0242:
0243: // fill the pcHeirachy field for all classes and copy the identityType
0244: // down the heirachy
0245: if (Debug.DEBUG) {
0246: System.out
0247: .println("MDB: Filling pcHeirachy field + copying identity type down");
0248: }
0249: for (int i = 0; i < clen; i++) {
0250: ClassMetaData cmd = classes[i];
0251: if (cmd.pcSuperMetaData == null) {
0252: cmd.calcPcHeirachy();
0253: if (config.hyperdrive) {
0254: cmd.oidClassName = MDStatics.GEN_OID_START
0255: + cmd.qname.replace('.', '_').replace('$',
0256: '_');
0257: }
0258: }
0259: }
0260:
0261: // name the hyperdrive OID and State classes
0262: if (config.hyperdrive) {
0263: for (int i = 0; i < clen; i++) {
0264: ClassMetaData cmd = classes[i];
0265: if (cmd.pcSuperMetaData != null) {
0266: cmd.oidClassName = cmd.top.oidClassName;
0267: }
0268: cmd.stateClassName = MDStatics.GEN_STATE_START
0269: + cmd.qname.replace('.', '_').replace('$', '_');
0270: }
0271: }
0272:
0273: // create meta data for all fields of all classes
0274: if (Debug.DEBUG)
0275: System.out.println("MDB: Creating field meta data");
0276: ClassMetaData[] ca = jmd.classes;
0277: for (int j = 0; j < ca.length; j++) {
0278: ClassMetaData cmd = ca[j];
0279: try {
0280: createFieldMetaData(cmd, quiet);
0281: } catch (RuntimeException e) {
0282: cmd.addError(e, quiet);
0283: }
0284: }
0285:
0286: doEmbedded(ca);
0287: doHorizontal(ca);
0288:
0289: // extract all the primary key fields for all classes
0290: if (Debug.DEBUG) {
0291: System.out.println("MDB: Extracting primary key fields");
0292: }
0293: for (int i = 0; i < clen; i++) {
0294: extractPrimaryKeyFields(classes[i], quiet);
0295: }
0296:
0297: // sort the classes array by classId so we can do a binary search
0298: // and fill the index fields on all classes
0299: if (Debug.DEBUG)
0300: System.out.println("MDB: Sorting classes by classId");
0301: Arrays.sort(jmd.classes);
0302: for (int i = 0; i < clen; i++)
0303: classes[i].index = i;
0304:
0305: // call init1 on all our DataStore's - they may add additional store
0306: // specific meta data at this time
0307: if (Debug.DEBUG) {
0308: System.out.println("MDB: Calling preBuildFetchGroupsHook");
0309: }
0310: preBuildFetchGroupsHook();
0311:
0312: // create all the fetch groups
0313: if (Debug.DEBUG)
0314: System.out.println("MDB: Creating fetch groups");
0315: fetchGroupBuilder.buildFetchGroups(quiet);
0316:
0317: calcSuperCounts(classes);
0318:
0319: // resolve all ordering fields
0320: if (Debug.DEBUG) {
0321: System.out
0322: .println("MDB: Resolving collection field ordering");
0323: }
0324: for (int i = 0; i < clen; i++)
0325: resolveOrdering(classes[i], quiet);
0326:
0327: // give all DataStore's a chance to do extra stuff now that
0328: // superFieldCount and superFetchGroupCount are filled in
0329: if (Debug.DEBUG) {
0330: System.out.println("MDB: Calling postAllFieldsCreatedHook");
0331: }
0332: postAllFieldsCreatedHook();
0333:
0334: // fill in the maxFieldsLength value
0335: if (Debug.DEBUG) {
0336: System.out.println("MDB: Calculating maxFieldsLength");
0337: }
0338: int max = 0;
0339: for (int i = 0; i < clen; i++) {
0340: ClassMetaData c = classes[i];
0341: int len = c.stateFields == null ? 0 : c.stateFields.length;
0342: if (len > max)
0343: max = len;
0344: }
0345: jmd.maxFieldsLength = max;
0346:
0347: // collect all the pass 2 fields for each class
0348: if (Debug.DEBUG) {
0349: System.out
0350: .println("MDB: Creating pass2Fields[] for each class");
0351: }
0352: IntArray ia = new IntArray();
0353: for (int i = 0; i < clen; i++) {
0354: ClassMetaData cmd = classes[i];
0355: ia.clear();
0356: FieldMetaData[] fields = cmd.fields;
0357: if (fields == null)
0358: continue; // possible if previous errors
0359: int fc = fields.length;
0360: for (int j = 0; j < fc; j++) {
0361: FieldMetaData fmd = fields[j];
0362: if (fmd.secondaryField)
0363: ia.add(fmd.fieldNo);
0364: }
0365: cmd.pass2Fields = ia.toArray();
0366: }
0367:
0368: // Fill in the fieldNo arrays now that everything is cool.
0369: if (Debug.DEBUG) {
0370: System.out
0371: .println("MDB: Calling initMDFields() for each class");
0372: }
0373: for (int i = 0; i < classes.length; i++) {
0374: ClassMetaData cmd = classes[i];
0375: if (cmd.pcSuperMetaData == null)
0376: cmd.initMDFields();
0377: }
0378:
0379: for (int i = 0; i < classes.length; i++) {
0380: ClassMetaData cmd = classes[i];
0381: if (cmd.pcSuperMetaData == null)
0382: cmd.initMDFields2();
0383: }
0384:
0385: /**
0386: * Set flags in the meta data for the client to correctly handle
0387: * bidirectional relationships.
0388: */
0389: setMasterDetailFlags(classes);
0390:
0391: /**
0392: * Update the totalSubClassCount
0393: */
0394: for (int i = 0; i < classes.length; i++) {
0395: ClassMetaData cmd = classes[i];
0396: cmd.totalNoOfSubClasses = getSubClassCount(cmd);
0397: }
0398:
0399: for (int i = 0; i < classes.length; i++) {
0400: ClassMetaData aClass = classes[i];
0401: if (aClass.objectIdClass != null) {
0402: try {
0403: validateIDClass(aClass);
0404: } catch (RuntimeException e) {
0405: aClass.addError(e, quiet);
0406: }
0407: }
0408: }
0409:
0410: // finish initialization of fetch groups
0411: if (Debug.DEBUG) {
0412: System.out
0413: .println("MDB: Finishing fetch group initialization");
0414: }
0415: for (int i = 0; i < clen; i++)
0416: classes[i].finishFetchGroups();
0417: for (int i = 0; i < clen; i++)
0418: classes[i].finishFetchGroups2();
0419: for (int i = 0; i < clen; i++) {
0420: ClassMetaData cmd = classes[i];
0421: if (cmd.pcSuperMetaData == null)
0422: checkHeirachy(cmd);
0423: }
0424: for (int i = 0; i < clen; i++) {
0425: ClassMetaData cmd = classes[i];
0426: FieldMetaData[] fmds = cmd.fields;
0427: if (fmds == null)
0428: continue; // possible if previous error
0429: for (int j = 0; j < fmds.length; j++) {
0430: FieldMetaData.setStateMethodName(fmds[j]);
0431: }
0432: }
0433:
0434: // This must be done after all fmd's have been finished.
0435: // This must set the inverseFieldNos
0436: // and null the inverseFieldMetaData
0437: for (int i = 0; i < classes.length; i++) {
0438: ClassMetaData cmd = classes[i];
0439: FieldMetaData[] fmds = cmd.stateFields;
0440: if (fmds == null)
0441: continue; // possible if previous error
0442: for (int j = 0; j < fmds.length; j++) {
0443: FieldMetaData fmd = fmds[j];
0444: if (fmd.inverseFieldMetaData != null) {
0445: fmd.inverseFieldNo = fmd.inverseFieldMetaData.managedFieldNo;
0446: }
0447: }
0448: }
0449:
0450: // Update the cacheStrat of the subclasses to that of the base class.
0451: for (int i = 0; i < clen; i++) {
0452: ClassMetaData cmd = classes[i];
0453: if (cmd.pcSuperMetaData == null)
0454: cmd.overRideCacheStrategy();
0455: }
0456:
0457: // create QueryParam's for all the named queries
0458: for (int i = 0; i < clen; i++) {
0459: ClassMetaData cmd = classes[i];
0460: try {
0461: createQueryParamsForNamedQueries(cmd);
0462: } catch (RuntimeException e) {
0463: cmd.addError(e, quiet);
0464: }
0465: }
0466:
0467: appIdUniqueMap = null;
0468:
0469: // cleanup data structures not required after meta data generation
0470: jmd.cleanupAfterMetaDataGeneration();
0471:
0472: // check the meta data for consistency
0473: jmd.validate();
0474:
0475: return jmd;
0476: }
0477:
0478: protected void doHorizontal(ClassMetaData[] ca) {
0479: }
0480:
0481: protected void doEmbedded(ClassMetaData[] ca) {
0482: }
0483:
0484: protected void createHorizontalFieldMetaData(ClassMetaData cmd,
0485: boolean quiet) {
0486: ClassMetaData super Cmd = cmd.horizontalCMD;
0487: if (super Cmd == null)
0488: return;
0489: if (super Cmd.fields == null)
0490: return;
0491:
0492: Map fieldMap = new HashMap();
0493: createFmdWithReflection(cmd.horizontalCMD, fieldMap, super Cmd
0494: .getShortName()
0495: + ".");
0496: updateMDFromHorizontalSuper(cmd);
0497: updateFmdFromMetadata(cmd.horizontalCMD, fieldMap, quiet,
0498: cmd.jdoClass.elements);
0499: List fields = updateFmd(fieldMap, cmd, quiet);
0500:
0501: for (int i = 0; i < fields.size(); i++) {
0502: FieldMetaData fieldMetaData = (FieldMetaData) fields.get(i);
0503: fieldMetaData.classMetaData = cmd;
0504: fieldMetaData.fake = true;
0505: fieldMetaData.origFmd = super Cmd.fields[i];
0506: fieldMetaData.horizontalFakeField = true;
0507: }
0508:
0509: cmd.horizontalFields = new FieldMetaData[fields.size()];
0510: fields.toArray(cmd.horizontalFields);
0511:
0512: fields.addAll(0, Arrays.asList(cmd.fields));
0513: cmd.fields = new FieldMetaData[fields.size()];
0514: fields.toArray(cmd.fields);
0515:
0516: for (int i = cmd.fields.length - 1; i >= 0; i--) {
0517: cmd.fields[i].fieldNo = i;
0518: }
0519:
0520: updateScoFields(cmd);
0521: }
0522:
0523: /**
0524: * Fill the superFieldCount and superFetchGroupCount values and
0525: * build the stateFields arrays.
0526: */
0527: protected void calcSuperCounts(ClassMetaData[] classes) {
0528: if (Debug.DEBUG) {
0529: System.out
0530: .println("MDB: Calcing superFieldCount and superFetchGroupCount "
0531: + "filling stateFields");
0532: }
0533: int clen = classes.length;
0534: for (int i = 0; i < clen; i++) {
0535: ClassMetaData cmd = classes[i];
0536: if (cmd.pcSuperMetaData == null) {
0537: cmd.calcSuperCounts();
0538: }
0539: }
0540: }
0541:
0542: /**
0543: * Set flags in the meta data for the client to correctly handle
0544: * bidirectional relationships.
0545: */
0546: protected void setMasterDetailFlags(ClassMetaData[] classes) {
0547: // nothing to do
0548: }
0549:
0550: /**
0551: * This is invoked before fetch groups are built. Subclasses can override
0552: * this to do additional processing.
0553: */
0554: protected void preBuildFetchGroupsHook() {
0555: }
0556:
0557: /**
0558: * Second hook called late in the build process after all fields have
0559: * been created and fetch groups have been built.
0560: */
0561: protected void postAllFieldsCreatedHook() {
0562: }
0563:
0564: private void createQueryParamsForNamedQueries(ClassMetaData cmd) {
0565: JdoClass jdoClass = getClassInfo(cmd).jdoClass;
0566: ArrayList queries = jdoClass.queries;
0567: if (queries == null || queries.isEmpty())
0568: return;
0569: int n = queries.size();
0570: for (int i = 0; i < n; i++) {
0571: JdoQuery q = (JdoQuery) queries.get(i);
0572: if (cmd.getNamedQuery(q.name) != null) {
0573: throw BindingSupportImpl.getInstance().runtime(
0574: "There " + "is already a query called '"
0575: + q.name + "' on " + cmd.qname + "\n"
0576: + q.getContext());
0577: }
0578: cmd.addNamedQuery(q.name, new QueryDetails(cmd, q));
0579: }
0580: }
0581:
0582: /**
0583: * What is the default cache strategy?
0584: */
0585: public int getCdCacheStrategy() {
0586: return MDStatics.CACHE_STRATEGY_YES;
0587: }
0588:
0589: /**
0590: * Are references placed in the default fetch group?
0591: */
0592: public boolean isCdRefsInDefaultFetchGroup() {
0593: return false;
0594: }
0595:
0596: /**
0597: * Recursive method to calculate the total subclass count.
0598: */
0599: private int getSubClassCount(ClassMetaData cmd) {
0600: if (cmd.pcSubclasses == null)
0601: return 0;
0602: int count = cmd.pcSubclasses.length;
0603: for (int i = 0; i < cmd.pcSubclasses.length; i++) {
0604: count += getSubClassCount(cmd.pcSubclasses[i]);
0605: }
0606: return count;
0607: }
0608:
0609: /**
0610: * Resolve the ordering nodes for all collection fields with ordering.
0611: */
0612: protected void resolveOrdering(ClassMetaData cmd, boolean quiet) {
0613: FieldMetaData[] fields = cmd.fields;
0614: if (fields == null)
0615: return; // possible if previous errors
0616: int flen = fields.length;
0617: for (int i = 0; i < flen; i++) {
0618: FieldMetaData fmd = fields[i];
0619: if (fmd.ordering != null) {
0620: int len = fmd.ordering.length;
0621: try {
0622: for (int j = 0; j < len; j++) {
0623: fmd.ordering[j].resolve(queryParser,
0624: fmd.elementTypeMetaData, false);
0625: }
0626: } catch (Exception e) {
0627: if (BindingSupportImpl.getInstance()
0628: .isOwnFatalUserException(e)) {
0629: fmd.addError((RuntimeException) e, quiet);
0630: } else {
0631: RuntimeException jfue = BindingSupportImpl
0632: .getInstance().runtime(
0633: "Invalid ordering extension for field "
0634: + fmd.getQName(), e);
0635: fmd.addError(jfue, quiet);
0636: }
0637: }
0638: }
0639: }
0640: }
0641:
0642: private void checkHeirachy(ClassMetaData cmd) {
0643: if (cmd.pcSubclasses == null)
0644: return;
0645: ClassMetaData[] pcSubs = cmd.pcSubclasses;
0646: FetchGroup[] super FG = cmd.fetchGroups;
0647: for (int i = 0; i < pcSubs.length; i++) {
0648: ClassMetaData pcSub = pcSubs[i];
0649: for (int j = 0; j < super FG.length; j++) {
0650: FetchGroup fetchGroup = super FG[j];
0651: FetchGroup subFG = pcSub.getFetchGroup(fetchGroup.name);
0652: if (subFG == null
0653: || subFG.super FetchGroup != fetchGroup) {
0654: throw BindingSupportImpl.getInstance().internal(
0655: "FG hierachy broken");
0656: }
0657: }
0658: checkHeirachy(pcSub);
0659: }
0660: }
0661:
0662: /**
0663: * Throw a JDOFatalUserException for an unexpected extension.
0664: */
0665: public static void throwUnexpectedExtension(JdoExtension e) {
0666: throw BindingSupportImpl.getInstance().runtime(
0667: "Extension not allowed here: " + e + "\n"
0668: + e.getContext());
0669: }
0670:
0671: /**
0672: * Find all the primary key fields for cmd.
0673: */
0674: private void extractPrimaryKeyFields(ClassMetaData cmd,
0675: boolean quiet) {
0676: // collect pk fields in meta data order
0677: ArrayList a = new ArrayList(4);
0678: JdoElement[] ea = cmd.jdoClass.elements;
0679: int n = ea.length;
0680: for (int i = 0; i < n; i++) {
0681: JdoElement o = ea[i];
0682: if (!(o instanceof JdoField))
0683: continue;
0684: FieldMetaData fmd = cmd
0685: .getFieldMetaData(((JdoField) o).name);
0686: if (fmd != null && fmd.primaryKey)
0687: a.add(fmd);
0688: }
0689:
0690: boolean appid = cmd.identityType == IDENTITY_TYPE_APPLICATION;
0691: if (a.isEmpty()) {
0692: if (appid && cmd.pcSuperClass == null) {
0693: RuntimeException e = BindingSupportImpl
0694: .getInstance()
0695: .runtime(
0696: "No primary key fields found for class with identity-type "
0697: + "application and no persistence-capable-superclass\n"
0698: + cmd.jdoClass.getContext());
0699: cmd.addError(e, quiet);
0700: return;
0701: }
0702: } else {
0703: if (!appid && !cmd.horizontal) {
0704: RuntimeException e = BindingSupportImpl.getInstance()
0705: .runtime(
0706: "Only classes with identity-type application may have "
0707: + "primary-key fields\n"
0708: + cmd.jdoClass.getContext());
0709: cmd.addError(e, quiet);
0710: }
0711: cmd.pkFields = new FieldMetaData[a.size()];
0712: a.toArray(cmd.pkFields);
0713:
0714: initPkFieldDefaultValue(cmd, a, quiet);
0715: }
0716: }
0717:
0718: private void initPkFieldDefaultValue(ClassMetaData cmd,
0719: ArrayList a, boolean quiet) {
0720: if (cmd.identityType != IDENTITY_TYPE_APPLICATION)
0721: return;
0722: try {
0723: Object inst = null;
0724: if (cmd.objectIdClass != null) {
0725: inst = cmd.objectIdClass.newInstance();
0726: } else {
0727: inst = cmd.cls.newInstance();
0728:
0729: }
0730: for (int i = 0; i < a.size(); i++) {
0731: FieldMetaData fmd = (FieldMetaData) a.get(i);
0732: Field field = null;
0733: Class cls = inst.getClass();
0734: for (; cls != null;) {
0735: try {
0736: field = cls.getDeclaredField(fmd.name);
0737: break;
0738: } catch (NoSuchFieldException e) {
0739: cls = cls.getSuperclass();
0740: } catch (SecurityException e) {
0741: e.printStackTrace(System.out);
0742: break;
0743: }
0744: }
0745: if (field != null) {
0746: ClassHelper.get().setAccessible(field, true);
0747: fmd.setPkDefaultValue(field.get(inst));
0748: }
0749: }
0750: } catch (Exception e) {
0751: if (!BindingSupportImpl.getInstance().isOwnException(e)) {
0752: e = BindingSupportImpl.getInstance().runtime(
0753: e.getMessage(), e);
0754: }
0755: cmd.addError((RuntimeException) e, quiet);
0756: if (!quiet) {
0757: throw (RuntimeException) e;
0758: }
0759: }
0760: }
0761:
0762: /**
0763: * Create a single JdoRoot from all packages.
0764: */
0765: private JdoRoot buildSingleJdoRoot(JdoRoot[] roots) {
0766:
0767: // process all the JdoRoot's that are not query only resources
0768: HashMap classMap = new HashMap();
0769: ArrayList packageList = new ArrayList();
0770: int n = roots.length;
0771: for (int i = 0; i < n; i++) {
0772: JdoRoot root = roots[i];
0773: if (root.isQueryMetaData())
0774: continue;
0775: for (int k = root.packages.length - 1; k >= 0; k--) {
0776: JdoPackage p = root.packages[k];
0777: boolean addPackage = true;
0778: for (int j = p.classes.length - 1; j >= 0; j--) {
0779: JdoClass cls = p.classes[j];
0780: String qname = cls.getQName();
0781: String other = (String) jmd.classResourceMap
0782: .get(qname);
0783: if (other != null) {
0784:
0785: if (other.equals(root.name)) {
0786: throw BindingSupportImpl
0787: .getInstance()
0788: .runtime(
0789: "Class "
0790: + p.classes[j].name
0791: + " is defined more than once in "
0792: + root.name);
0793: } else {
0794: throw BindingSupportImpl.getInstance()
0795: .runtime(
0796: "Class "
0797: + p.classes[j].name
0798: + " is defined in "
0799: + other + " and "
0800: + root.name);
0801: }
0802:
0803: }
0804: jmd.classResourceMap.put(qname, root.name);
0805: classMap.put(qname, cls);
0806: }
0807: if (addPackage == true)
0808: packageList.add(p);
0809: }
0810: }
0811:
0812: // move all JdoQuery's to the meta data for the classes they are for
0813: for (int i = 0; i < n; i++) {
0814: JdoRoot root = roots[i];
0815: if (!root.isQueryMetaData())
0816: continue;
0817: for (int k = root.packages.length - 1; k >= 0; k--) {
0818: JdoPackage p = root.packages[k];
0819: for (int j = p.classes.length - 1; j >= 0; j--) {
0820: JdoClass cls = p.classes[j];
0821: if (cls.queries == null)
0822: continue;
0823: for (Iterator t = cls.queries.iterator(); t
0824: .hasNext();) {
0825: JdoQuery q = (JdoQuery) t.next();
0826: String qname = q.getCandidateClass();
0827: JdoClass target = (JdoClass) classMap
0828: .get(qname);
0829: if (target == null) {
0830: throw BindingSupportImpl
0831: .getInstance()
0832: .runtime(
0833: "Candidate class for query is not "
0834: + "defined in the meta data: "
0835: + qname + "\n"
0836: + q.getContext());
0837: }
0838: target.addJdoQuery(q);
0839: }
0840: }
0841: }
0842: }
0843:
0844: // make a new single JdoRoot
0845: JdoRoot root = new JdoRoot();
0846: root.packages = new JdoPackage[packageList.size()];
0847: root.name = "combined";
0848: packageList.toArray(root.packages);
0849: // Note that we have not changed the parent field on each package.
0850: // This still points at the original JdoRoot so that error messages
0851: // can display the correct filename.
0852:
0853: return root;
0854: }
0855:
0856: /**
0857: * Load class name using our loader.
0858: */
0859: private Class loadClass(String name) throws ClassNotFoundException {
0860: return ClassHelper.get().classForName(name, false, loader);
0861: }
0862:
0863: /**
0864: * Create tempory meta data for a package. This is used for defaults
0865: * for classes etc.
0866: */
0867: private PackageMetaData createPackageMetaData(JdoPackage p) {
0868: PackageMetaData pmd = new PackageMetaData();
0869: pmd.nameWithDot = p.name + ".";
0870: pmd.jdoPackage = p;
0871: return pmd;
0872: }
0873:
0874: /**
0875: * Create meta data for an interface in package pmd.
0876: */
0877: private InterfaceMetaData createInterfaceMetaData(
0878: PackageMetaData pmd, JdoExtension ext) {
0879: String qname = ext.getString();
0880: if (qname.indexOf('.') < 0)
0881: qname = pmd.nameWithDot + qname;
0882: Class cls;
0883: try {
0884: cls = loadClass(qname);
0885: } catch (ClassNotFoundException e) {
0886: throw BindingSupportImpl.getInstance().runtime(
0887: "Interface not found: " + qname + "\n"
0888: + ext.getContext(), e);
0889: }
0890: if (!cls.isInterface()) {
0891: throw BindingSupportImpl.getInstance().runtime(
0892: "Expected interface, found class: " + qname + "\n"
0893: + ext.getContext());
0894: }
0895: return new InterfaceMetaData(cls);
0896: }
0897:
0898: /**
0899: * Create basic meta data for class cls in package pmd. This does not
0900: * pickup the fields. It loads all classes etc and finds some class
0901: * extensions.
0902: */
0903: private ClassMetaData createMetaData(PackageMetaData pmd,
0904: JdoClass jdoCls, boolean quiet) {
0905: ClassMetaData cmd = new ClassMetaData(jdoCls, jmd);
0906: ClassInfo classInfo = getClassInfo(cmd);
0907: classInfo.jdoClass = jdoCls;
0908:
0909: // init defaults before we do anything else
0910: cmd.packageNameWithDot = pmd.nameWithDot;
0911: classInfo.refsInDefaultFetchGroup = isCdRefsInDefaultFetchGroup();
0912: cmd.cacheStrategy = getCdCacheStrategy();
0913: cmd.setObjectIdClasssRequired(jdoCls.objectIdClasssRequired);
0914:
0915: // load the PC class
0916: try {
0917: cmd.cls = loadClass(cmd.qname);
0918: } catch (ClassNotFoundException e) {
0919: RuntimeException x = BindingSupportImpl.getInstance()
0920: .runtime(
0921: "Class not found: " + cmd.qname + "\n"
0922: + jdoCls.getContext(), e);
0923: cmd.addError(x, quiet);
0924: return cmd;
0925: }
0926: if (cmd.cls.isInterface()) {
0927: RuntimeException x = BindingSupportImpl.getInstance()
0928: .runtime(
0929: "Expected class, found interface: "
0930: + cmd.qname + "\n"
0931: + jdoCls.getContext());
0932: cmd.addError(x, quiet);
0933: return cmd;
0934: }
0935:
0936: // load its persistence-capable-superclass (if any)
0937: String qname = jdoCls.getPCSuperClassQName();
0938: if (qname != null) {
0939: try {
0940: cmd.pcSuperClass = loadClass(qname);
0941: } catch (ClassNotFoundException e) {
0942: RuntimeException x = BindingSupportImpl.getInstance()
0943: .runtime(
0944: "persistence-capable-superclass not found: "
0945: + qname + "\n"
0946: + jdoCls.getContext(), e);
0947: cmd.addError(x, quiet);
0948: return cmd;
0949: }
0950: }
0951:
0952: checkForHorizontal(jdoCls, cmd);
0953: updateIdMetaData(cmd, jdoCls);
0954:
0955: // pickup some class extensions
0956: JdoElement[] elements = jdoCls.elements;
0957: int n = elements.length;
0958: for (int i = 0; i < n; i++) {
0959: JdoElement o = elements[i];
0960: if (o instanceof JdoExtension) {
0961: JdoExtension e = (JdoExtension) o;
0962: switch (e.key) {
0963: case JdoExtensionKeys.READ_ONLY:
0964: try {
0965: cmd.readOnly = e.getBoolean();
0966: } catch (RuntimeException x) {
0967: cmd.addError(x, quiet);
0968: }
0969: break;
0970: case JdoExtensionKeys.CACHE_STRATEGY:
0971: try {
0972: cmd.cacheStrategy = e.getEnum(MDE.CACHE_ENUM);
0973: } catch (RuntimeException x) {
0974: cmd.addError(x, quiet);
0975: }
0976: break;
0977: case JdoExtensionKeys.DELETE_ORPHANS:
0978: try {
0979: cmd.deleteOrphans = e.getBoolean();
0980: } catch (RuntimeException x) {
0981: cmd.addError(x, quiet);
0982: }
0983: break;
0984: case JdoExtensionKeys.OIDS_IN_DEFAULT_FETCH_GROUP:
0985: try {
0986: getClassInfo(cmd).refsInDefaultFetchGroup = e
0987: .getBoolean();
0988: } catch (RuntimeException x) {
0989: cmd.addError(x, quiet);
0990: }
0991: break;
0992: case JdoExtensionKeys.CREATE_OID_AT_MAKE_PERSISTENT:
0993: // ignore - no longer required
0994: break;
0995: }
0996: }
0997: }
0998:
0999: return cmd;
1000: }
1001:
1002: private void updateIdMetaData(ClassMetaData cmd, JdoClass jdoCls) {
1003: // load its objectid-class (if any)
1004: cmd.identityType = jdoCls.identityType;
1005: if (cmd.identityType == IDENTITY_TYPE_APPLICATION) {
1006: String qname = jdoCls.getObjectIdClassQName();
1007: if (qname == null) {
1008: if (cmd.isObjectIdClasssRequired()) {
1009: RuntimeException x = BindingSupportImpl
1010: .getInstance().runtime(
1011: "objectid-class is required for application identity\n"
1012: + jdoCls.getContext());
1013: cmd.addError(x, quiet);
1014: }
1015: return;
1016: }
1017:
1018: String pcRootClsName = (String) appIdUniqueMap.get(qname);
1019: if (pcRootClsName == null) {
1020: appIdUniqueMap.put(qname, jdoCls.getQName());
1021: } else {
1022: RuntimeException x = BindingSupportImpl.getInstance()
1023: .invalidOperation(
1024: "The objectid-class for "
1025: + jdoCls.getQName()
1026: + " has already been used for "
1027: + pcRootClsName + ": " + qname
1028: + "\n" + jdoCls.getContext());
1029: cmd.addError(x, quiet);
1030: return;
1031: }
1032:
1033: try {
1034: cmd.objectIdClass = loadClass(qname);
1035: } catch (ClassNotFoundException e) {
1036: RuntimeException x = BindingSupportImpl
1037: .getInstance()
1038: .runtime(
1039: "objectid-class not found: " + qname
1040: + "\n" + jdoCls.getContext(), e);
1041: cmd.addError(x, quiet);
1042: return;
1043: }
1044: } else if (cmd.identityType == IDENTITY_TYPE_NONDURABLE) {
1045: RuntimeException x = BindingSupportImpl.getInstance()
1046: .runtime(
1047: "nondurable identity-type is not supported\n"
1048: + jdoCls.getContext());
1049: cmd.addError(x, quiet);
1050: return;
1051: } else {
1052: cmd.identityType = IDENTITY_TYPE_DATASTORE;
1053: if (jdoCls.objectIdClass != null) {
1054: RuntimeException x = BindingSupportImpl.getInstance()
1055: .runtime(
1056: "objectid-class is only allowed for application identity\n"
1057: + jdoCls.getContext());
1058: cmd.addError(x, quiet);
1059: return;
1060: }
1061: }
1062: // } else {
1063: // if (jdoCls.objectIdClass != null) {
1064: // RuntimeException x = BindingSupportImpl.getInstance().runtime("objectid-class is not allowed as class " +
1065: // "has a persistence-capable-superclass\n" +
1066: // jdoCls.getContext());
1067: // cmd.addError(x, quiet);
1068: // return cmd;
1069: // }
1070: // }
1071: }
1072:
1073: /**
1074: * Calculate if this class is horizontally mapped.
1075: * @param jdoCls
1076: * @param cmd
1077: */
1078: protected void checkForHorizontal(JdoClass jdoCls, ClassMetaData cmd) {
1079: }
1080:
1081: /**
1082: * Validate the application id class. This also fills
1083: * {@link FieldMetaData#objectidClassField}.
1084: */
1085: private void validateIDClass(ClassMetaData cmd) {
1086: Class idClass = cmd.objectIdClass;
1087:
1088: if (!Modifier.isPublic(idClass.getModifiers())) {
1089: throw BindingSupportImpl.getInstance().runtime(
1090: "Application id class '" + idClass.getName()
1091: + "' must be public");
1092: }
1093:
1094: if (idClass.isInterface()) {
1095: throw BindingSupportImpl.getInstance().runtime(
1096: "Application id class '" + idClass.getName()
1097: + "' may not be an interface");
1098: }
1099:
1100: FieldMetaData[] pkFields = cmd.pkFields;
1101: for (int i = 0; i < pkFields.length; i++) {
1102: pkFields[i].getObjectidClassField();
1103: }
1104:
1105: // Must implement java.io.Serializable
1106: if (!Serializable.class.isAssignableFrom(idClass)) {
1107: throw BindingSupportImpl
1108: .getInstance()
1109: .runtime(
1110: "Application id class '"
1111: + idClass.getName()
1112: + "' does not implement java.io.Serializable");
1113: }
1114:
1115: // Must have default constructor
1116: Constructor[] constructors = idClass.getDeclaredConstructors();
1117: boolean foundDefaultCon = false;
1118: for (int i = 0; i < constructors.length; i++) {
1119: Constructor constructor = constructors[i];
1120: if (!Modifier.isPublic(constructor.getModifiers()))
1121: continue;
1122: if (constructor.getParameterTypes().length != 0)
1123: continue;
1124: foundDefaultCon = true;
1125: }
1126: if (!foundDefaultCon) {
1127: throw BindingSupportImpl
1128: .getInstance()
1129: .runtime(
1130: "Application id class '"
1131: + idClass.getName()
1132: + "' does not have a public no-arg constructor");
1133: }
1134:
1135: // Must have String constructor
1136: boolean foundStringCon = false;
1137: for (int i = 0; i < constructors.length; i++) {
1138: Constructor constructor = constructors[i];
1139: if (!Modifier.isPublic(constructor.getModifiers()))
1140: continue;
1141: if (constructor.getParameterTypes().length != 1)
1142: continue;
1143: if (constructor.getParameterTypes()[0]
1144: .isAssignableFrom(String.class)) {
1145: foundStringCon = true;
1146: }
1147: }
1148: if (!foundStringCon) {
1149: throw BindingSupportImpl
1150: .getInstance()
1151: .runtime(
1152: "Application id class '"
1153: + idClass.getName()
1154: + "' does not have a public constructor that accepts a String parameter");
1155: }
1156:
1157: // Must override toString
1158: try {
1159: Method tos = null;
1160: for (Class c = idClass; c != Object.class; c = c
1161: .getSuperclass()) {
1162: try {
1163: tos = c.getDeclaredMethod("toString", null);
1164: break;
1165: } catch (NoSuchMethodException e) {
1166: // ignore
1167: }
1168: }
1169: if (tos == null) {
1170: throw BindingSupportImpl.getInstance().runtime(
1171: "Application id class '" + idClass.getName()
1172: + "' must override toString");
1173: }
1174: } catch (SecurityException e) {
1175: throw BindingSupportImpl.getInstance().exception(
1176: e.getMessage(), e);
1177: }
1178: }
1179:
1180: /**
1181: * Create the FieldMetaData for class cmd. This finds all fields using
1182: * reflection and merges this with the JdoField information from the
1183: * .jdo files.
1184: */
1185: private void createFieldMetaData(ClassMetaData cmd, boolean quiet) {
1186: if (cmd.pcSuperMetaData != null)
1187: return;
1188: createFieldMetaDataImp(cmd, quiet);
1189: }
1190:
1191: protected void createEmbeddeFieldMetaData(ClassMetaData cmd,
1192: boolean quiet) {
1193: if (cmd.pcSuperMetaData != null)
1194: return;
1195: createEmbeddeFieldMetaDataImp(cmd, quiet);
1196: }
1197:
1198: /**
1199: * Update all the jdo metadata for horizontal fields from horizontal superclass.
1200: */
1201: private void updateMDFromHorizontalSuper(ClassMetaData cmd) {
1202: if (cmd.horizontalCMD == null)
1203: return;
1204:
1205: JdoElement[] ea = cmd.jdoClass.elements;
1206: List newFields = new ArrayList(Arrays.asList(ea));
1207: JdoElement[] hor = cmd.horizontalCMD.jdoClass.elements;
1208: final String prefix = cmd.horizontalCMD.getShortName() + ".";
1209:
1210: for (int i = 0; i < hor.length; i++) {
1211: JdoElement o = hor[i];
1212: if (!(o instanceof JdoField))
1213: continue;
1214: JdoField jdoField = (JdoField) o;
1215:
1216: /**
1217: * If this field is descibed in subclass then merge it, else just copy
1218: * it to the subclass
1219: */
1220: JdoField extendedField = findJdoFieldIn(ea, prefix
1221: + jdoField.name);
1222: if (extendedField == null) {
1223: extendedField = jdoField.createCopy(cmd.jdoClass);
1224: extendedField.name = prefix + extendedField.name;
1225: newFields.add(extendedField);
1226: } else {
1227: extendedField.synchWith(jdoField,
1228: Collections.EMPTY_SET, false);
1229: }
1230: }
1231: ea = new JdoElement[newFields.size()];
1232: newFields.toArray(ea);
1233: cmd.jdoClass.elements = ea;
1234: }
1235:
1236: private JdoField findJdoFieldIn(JdoElement[] elements, String name) {
1237: for (int i = 0; i < elements.length; i++) {
1238: JdoElement element = elements[i];
1239: if (!(element instanceof JdoField))
1240: continue;
1241: JdoField jdoField = (JdoField) element;
1242: if (jdoField.name.equals(name)) {
1243: return jdoField;
1244: }
1245: }
1246: return null;
1247: }
1248:
1249: private void createEmbeddeFieldMetaDataImp(ClassMetaData cmd,
1250: boolean quiet) {
1251: ArrayList currentFields = new ArrayList(Arrays
1252: .asList(cmd.fields));
1253: HashMap parents = new HashMap(cmd.fields.length * 5);
1254: FieldMetaData fmd = null;
1255: for (int i = 0; i < currentFields.size(); i++) {
1256: fmd = (FieldMetaData) currentFields.get(i);
1257:
1258: if (fmd.isEmbeddedRef()) {
1259: ClassMetaData embeddedCmd = fmd.typeMetaData;
1260:
1261: Map fieldMap = new HashMap();
1262: createFmdWithReflection(embeddedCmd, fieldMap, fmd.name
1263: + "/");
1264:
1265: /**
1266: * Make a copy of the jdoFields as found on the original class
1267: * Extract info from the extensions found on the embedded ref field
1268: * and apply it to the relevant jdoFields.
1269: */
1270: Map copiedJdoFields = new HashMap();
1271: for (int j = 0; j < embeddedCmd.jdoClass.elements.length; j++) {
1272: JdoElement element = embeddedCmd.jdoClass.elements[j];
1273: if (!(element instanceof JdoField))
1274: continue;
1275: JdoField copy = ((JdoField) element)
1276: .createCopy(cmd.jdoClass);
1277: //map it to old name
1278: copiedJdoFields.put(copy.name, copy);
1279: copy.origName = copy.name;
1280: copy.name = fmd.name + "/" + copy.name;
1281: if (fmd.jdoField != null
1282: && fmd.jdoField.extensions != null) {
1283: JdoExtension ext = JdoExtension.find(
1284: JdoExtensionKeys.FIELD, copy.origName,
1285: fmd.jdoField.extensions);
1286: if (ext != null)
1287: copy.applyEmbeddedExtensions(ext.nested);
1288: } else {
1289: copy.applyEmbeddedExtensions(null);
1290: }
1291: }
1292:
1293: //create new JdoField's from extensions
1294: if (fmd.jdoField != null
1295: && fmd.jdoField.extensions != null) {
1296: JdoExtension exts[] = fmd.jdoField.extensions;
1297: Iterator iter = fieldMap.keySet().iterator();
1298: while (iter.hasNext()) {
1299: String name = (String) iter.next();
1300: name = name
1301: .substring((fmd.name + "/").length());
1302: if (copiedJdoFields.containsKey(name))
1303: continue;
1304: JdoExtension ext = JdoExtension.find(
1305: JdoExtensionKeys.FIELD, name, exts);
1306: if (ext != null) {
1307: JdoField newJdoField = new JdoField(
1308: fmd.jdoField);
1309: newJdoField.name = fmd.name + "/" + name;
1310: newJdoField
1311: .applyEmbeddedExtensions(ext.nested);
1312: copiedJdoFields.put(name, newJdoField);
1313: }
1314: }
1315: }
1316:
1317: JdoElement[] els = new JdoElement[copiedJdoFields
1318: .size()];
1319: copiedJdoFields.values().toArray(els);
1320:
1321: updateFmdFromMetadata(embeddedCmd, fieldMap, quiet, els);
1322: List fields = updateFmd(fieldMap, cmd, quiet);
1323: findNullIndicationFmd(fields, fmd);
1324:
1325: for (int j = 0; j < fields.size(); j++) {
1326: FieldMetaData fieldMetaData = (FieldMetaData) fields
1327: .get(j);
1328: fieldMetaData.classMetaData = cmd;
1329: fieldMetaData.fake = true;
1330: fieldMetaData.origFmd = embeddedCmd.fields[j];
1331: fieldMetaData.embeddedFakeField = true;
1332:
1333: //look for recursive embedded field
1334: checkRecursiveEmbedded(fieldMetaData, fmd, parents);
1335: }
1336:
1337: fmd.embeddedFmds = new FieldMetaData[fields.size()];
1338: fields.toArray(fmd.embeddedFmds);
1339:
1340: /**
1341: * Look for nullIndicator
1342: * The nullIndicator might be a column or a field of the embedded class.
1343: * If it is a column then we must check if there is a field with the same
1344: * column name. If there is then it must be used as the null indicator.
1345: * Else we must create a fake field with the supplied column name to act as
1346: * the nullIndicator.
1347: */
1348: if (fmd.jdoField.extensions != null) {
1349: for (int j = 0; j < fmd.jdoField.extensions.length; j++) {
1350: JdoExtension ext = fmd.jdoField.extensions[j];
1351: if (ext.key == JdoExtensionKeys.NULL_INDICATOR) {
1352: //must add fake field
1353: }
1354: }
1355: }
1356: currentFields.addAll(fields);
1357: }
1358: }
1359:
1360: cmd.fields = new FieldMetaData[currentFields.size()];
1361: currentFields.toArray(cmd.fields);
1362:
1363: for (int j = cmd.fields.length - 1; j >= 0; j--) {
1364: cmd.fields[j].fieldNo = j;
1365: }
1366:
1367: updateScoFields(cmd);
1368:
1369: if (cmd.pcSubclasses != null) {
1370: for (int i = 0; i < cmd.pcSubclasses.length; i++) {
1371: createEmbeddeFieldMetaDataImp(cmd.pcSubclasses[i],
1372: quiet);
1373: }
1374: }
1375: }
1376:
1377: private void findNullIndicationFmd(List fields, FieldMetaData fmd) {
1378: for (int j = 0; j < fields.size(); j++) {
1379: FieldMetaData fieldMetaData = (FieldMetaData) fields.get(j);
1380: if (fieldMetaData.jdoField != null
1381: && fieldMetaData.jdoField.nullIndicator) {
1382: fmd.setNullIndicatorFmd(fieldMetaData);
1383: break;
1384: }
1385: }
1386: }
1387:
1388: private void checkRecursiveEmbedded(FieldMetaData fieldMetaData,
1389: FieldMetaData fmd, HashMap parents) {
1390: if (fieldMetaData.jdoField != null
1391: && fieldMetaData.isDirectRef()) {
1392: JdoExtension ext = null;
1393: if (fieldMetaData.jdoField.extensions != null) {
1394: ext = JdoExtension.find(JdoExtensionKeys.EMBEDDED,
1395: fieldMetaData.jdoField.extensions);
1396: }
1397: if (ext != null) {
1398: if (ext.getBoolean()) {
1399: fieldMetaData.embedded = true;
1400: } else {
1401: fieldMetaData.embedded = false;
1402: }
1403: } else {
1404: if (fieldMetaData.embedded) {
1405: HashSet parentTypes = new HashSet(5);
1406: FieldMetaData field = fmd;
1407: while (field != null) {
1408: ClassMetaData refClass = field.typeMetaData;
1409: while (refClass != null) {
1410: parentTypes.add(refClass);
1411: refClass = refClass.pcSuperMetaData;
1412: }
1413: FieldMetaData tempParent = (FieldMetaData) parents
1414: .get(field);
1415: if (tempParent == null) {
1416: refClass = field.classMetaData;
1417: while (refClass != null) {
1418: parentTypes.add(refClass);
1419: refClass = refClass.pcSuperMetaData;
1420: }
1421: }
1422: if (parentTypes
1423: .contains(fieldMetaData.typeMetaData)) {
1424: fieldMetaData.embedded = false;
1425: break;
1426: }
1427: field = tempParent;
1428: }
1429: }
1430: }
1431: }
1432: }
1433:
1434: /**
1435: * Create the FieldMetaData for class cmd and recursively all of its
1436: * subclasses.
1437: */
1438: private void createFieldMetaDataImp(ClassMetaData cmd, boolean quiet) {
1439: HashMap fieldMap = new HashMap(); // name -> FieldMetaData
1440: createFmdWithReflection(cmd, fieldMap, "");
1441: updateFmdFromMetadata(cmd, fieldMap, quiet,
1442: cmd.jdoClass.elements);
1443: List fields = updateFmd(fieldMap, cmd, quiet);
1444: finalizeFmds(fields, cmd, quiet);
1445: }
1446:
1447: /**
1448: * extract all the persistent and transactional fields and fill in more info.
1449: * Return a list of managed fields.
1450: */
1451: private List updateFmd(Map fieldMap, ClassMetaData cmd,
1452: boolean quiet) {
1453: ArrayList fields = new ArrayList();
1454: FieldMetaData fmd = null;
1455: for (Iterator i = fieldMap.values().iterator(); i.hasNext();) {
1456: fmd = (FieldMetaData) i.next();
1457: if (fmd.persistenceModifier == PERSISTENCE_MODIFIER_NONE)
1458: continue;
1459: JdoField jdoField = fmd.jdoField;
1460: if (Modifier.isTransient(fmd.modifiers)) {
1461: if (jdoField == null)
1462: continue;
1463: }
1464: fields.add(fmd);
1465: Class tt = fmd.componentType;
1466: if (tt == null)
1467: tt = fmd.type;
1468: fmd.typeMetaData = (ClassMetaData) classMap.get(tt);
1469: if (fmd.category == 0) {
1470: fmd.category = mdutils.getFieldCategory(
1471: fmd.persistenceModifier, fmd.type,
1472: classAndInterfaceMap);
1473: }
1474: if (fmd.category == MDStatics.CATEGORY_COLLECTION
1475: || fmd.category == MDStatics.CATEGORY_ARRAY
1476: || fmd.category == MDStatics.CATEGORY_MAP) {
1477: fmd.collectionDiffType = true;
1478: }
1479: if ((!fmd.embedded && fmd.category == CATEGORY_REF || fmd.category == CATEGORY_POLYREF)
1480: && (jdoField == null || jdoField.defaultFetchGroup == NOT_SET)) {
1481: fmd.defaultFetchGroup = getClassInfo(cmd).refsInDefaultFetchGroup;
1482: }
1483: if (fmd.category == MDStatics.CATEGORY_TRANSACTIONAL
1484: && fmd.scoField == true) {
1485: fmd.scoField = false;
1486: }
1487: if (fmd.category == MDStatics.CATEGORY_EXTERNALIZED) {
1488: if (fmd.externalizer == null) {
1489: fmd.externalizer = getExternalizerForType(fmd.type);
1490: }
1491: fmd.scoField = false;
1492: }
1493: if (fmd.category != MDStatics.CATEGORY_TRANSACTIONAL) {
1494: try {
1495: fillCollectionMetaData(fmd);
1496: } catch (RuntimeException e) {
1497: fmd.addError(e, quiet);
1498: }
1499: }
1500: }
1501: // sort by field name i.e. into fieldNo order
1502: Collections.sort(fields);
1503: return fields;
1504: }
1505:
1506: /**
1507: * Look at all the meta data from .jdo files and merge it in.
1508: */
1509: private void updateFmdFromMetadata(ClassMetaData cmd, Map fieldMap,
1510: boolean quiet, JdoElement[] ea) {
1511: int n = ea.length;
1512: for (int i = 0; i < n; i++) {
1513: JdoElement o = ea[i];
1514: if (!(o instanceof JdoField))
1515: continue;
1516: JdoField jdoField = (JdoField) o;
1517: if (jdoField.persistenceModifier == PERSISTENCE_MODIFIER_NONE) {
1518: fieldMap.remove(jdoField.name);
1519: continue;
1520: }
1521:
1522: FieldMetaData fmd = (FieldMetaData) fieldMap
1523: .get(jdoField.name);
1524: /**
1525: * The fmd might be null because w
1526: */
1527: if (fmd == null) {
1528: final String prefix = cmd.getShortName() + ".";
1529: if (jdoField.name.startsWith(prefix)) {
1530: String name = jdoField.name.substring(prefix
1531: .length());
1532: fmd = (FieldMetaData) fieldMap.get(name);
1533: }
1534: }
1535: if (fmd == null) {
1536: int cIndex = jdoField.name.lastIndexOf("/");
1537: if (cIndex != -1) {
1538: String name = jdoField.name.substring(cIndex);
1539: fmd = (FieldMetaData) fieldMap.get(name);
1540: }
1541: }
1542: try {
1543: if (fmd == null) {
1544: if (cmd.horizontal
1545: || (cmd.horizontalCMD != null && jdoField.name
1546: .startsWith(cmd.horizontalCMD
1547: .getShortName()
1548: + "."))) {
1549: //this is a metadata for a horizontal field so ignore it for now
1550: continue;
1551: }
1552: throw BindingSupportImpl.getInstance().runtime(
1553: "Field " + jdoField.name + " not found on "
1554: + cmd.qname + "\n"
1555: + jdoField.getContext());
1556: }
1557: if (fmd.jdoField != null) {
1558: throw BindingSupportImpl.getInstance().runtime(
1559: "Duplicate meta data for field "
1560: + jdoField.name + "\n"
1561: + jdoField.getContext());
1562: }
1563: fmd.jdoField = jdoField;
1564: fmd.cascadeType = jdoField.cascadeType;
1565:
1566: if (jdoField.persistenceModifier != NOT_SET) {
1567: fmd.persistenceModifier = jdoField.persistenceModifier;
1568: }
1569:
1570: // do not allow static or final fields
1571: if (!mdutils.isPersistentModifiers(fmd.modifiers)) {
1572: throw BindingSupportImpl.getInstance().runtime(
1573: "Field " + jdoField.name
1574: + " is static and/or final\n"
1575: + jdoField.getContext());
1576: }
1577:
1578: // fill in more basic info
1579: fmd.primaryKey = jdoField.primaryKey;
1580: fmd.nullValue = jdoField.nullValue;
1581: if (fetchGroupBuilder
1582: .findFetchGroupExt(jdoField.extensions) != null) {
1583: fmd.defaultFetchGroup = false;
1584: } else if (jdoField.defaultFetchGroup != NOT_SET) {
1585: fmd.defaultFetchGroup = jdoField.defaultFetchGroup == TRUE;
1586: }
1587: if (jdoField.embedded != NOT_SET) {
1588: fmd.embedded = jdoField.embedded == TRUE;
1589: }
1590: fmd.jdoCollection = jdoField.collection;
1591: fmd.jdoArray = jdoField.array;
1592: fmd.jdoMap = jdoField.map;
1593:
1594: // process extensions
1595: processFieldExtensions(fmd, quiet);
1596:
1597: // make sure transactional fields do not end up in the default
1598: // fetch group
1599: if (fmd.persistenceModifier == PERSISTENCE_MODIFIER_TRANSACTIONAL) {
1600: fmd.defaultFetchGroup = false;
1601: }
1602: } catch (RuntimeException e) {
1603: if (quiet) {
1604: if (fmd != null)
1605: fmd.addError(e, quiet);
1606: else
1607: cmd.addError(e, quiet);
1608: continue;
1609: } else {
1610: throw e;
1611: }
1612: }
1613: }
1614: }
1615:
1616: /**
1617: * Create metadata using reflection.
1618: * @param cmd
1619: * @param fieldMap
1620: */
1621: private void createFmdWithReflection(ClassMetaData cmd,
1622: Map fieldMap, String prefix) {
1623: ClassMetaData.FieldInfo[] fa = cmd.getDeclaredFields();
1624: for (int i = fa.length - 1; i >= 0; i--) {
1625: ClassMetaData.FieldInfo f = fa[i];
1626: String fname = f.getName();
1627:
1628: if (mdutils.isEnhancerAddedField(fname))
1629: continue;
1630:
1631: FieldMetaData fmd = new FieldMetaData();
1632: fmd.classMetaData = cmd;
1633: fmd.name = prefix + fname;
1634: fmd.origName = fname;
1635: fmd.setType(f.getType());
1636: fmd.setComponentType(fmd.type.getComponentType());
1637: fmd.modifiers = f.getModifiers();
1638: fmd.scoField = mdutils.isMutableType(fmd.type);
1639: if (mdutils.isDefaultPersistentField(f,
1640: classAndInterfaceMap)) {
1641: fmd.persistenceModifier = PERSISTENCE_MODIFIER_PERSISTENT;
1642: } else {
1643: fmd.persistenceModifier = PERSISTENCE_MODIFIER_NONE;
1644: }
1645: fmd.nullValue = NULL_VALUE_NONE;
1646: fmd.defaultFetchGroupDefault = mdutils
1647: .isDefaultFetchGroupType(fmd.type);
1648: fmd.defaultFetchGroup = fmd.defaultFetchGroupDefault;
1649: fmd.embedded = mdutils.isEmbeddedType(fmd.type);
1650: fieldMap.put(fmd.name, fmd);
1651: }
1652: }
1653:
1654: /**
1655: * The supplied 'fields' list must be sorted on the natural ordering of fmd's.
1656: */
1657: private void finalizeFmds(List fields, ClassMetaData cmd,
1658: boolean quiet) {
1659: // create our fields array
1660: FieldMetaData[] fmda = cmd.fields = new FieldMetaData[fields
1661: .size()];
1662: fields.toArray(fmda);
1663: cmd.realFieldCount = fmda.length;
1664:
1665: // set the fieldNo for each field
1666: for (int i = fmda.length - 1; i >= 0; i--) {
1667: fmda[i].fieldNo = i;
1668: }
1669:
1670: // process all subclasses
1671: ClassMetaData[] pcSubclasses = cmd.pcSubclasses;
1672: if (pcSubclasses != null) {
1673: int len = pcSubclasses.length;
1674: for (int i = 0; i < len; i++) {
1675: createFieldMetaDataImp(pcSubclasses[i], quiet);
1676: }
1677: }
1678: updateScoFields(cmd);
1679: }
1680:
1681: private void updateScoFields(ClassMetaData cmd) {
1682: FieldMetaData fmd;
1683: for (int i = 0; i < cmd.fields.length; i++) {
1684: fmd = cmd.fields[i];
1685: if (fmd.category == MDStatics.CATEGORY_ARRAY
1686: || fmd.category == MDStatics.CATEGORY_COLLECTION
1687: || fmd.category == MDStatics.CATEGORY_MAP
1688:
1689: || fmd.typeCode == MDStatics.DATE) {
1690: fmd.setScoField(true);
1691: setSCOFactory(fmd);
1692: }
1693: }
1694: }
1695:
1696: /**
1697: * Process the extensions for fmd (if any).
1698: */
1699: private void processFieldExtensions(FieldMetaData fmd, boolean quite) {
1700: JdoExtension[] a = fmd.jdoField.extensions;
1701: if (a == null)
1702: return;
1703: for (int i = 0; i < a.length; i++) {
1704: JdoExtension e = a[i];
1705: switch (e.key) {
1706: case JdoExtensionKeys.NULL_INDICATOR:
1707: fmd.embedded = true;
1708: break;
1709: case JdoExtensionKeys.FIELD:
1710: fmd.embedded = true;
1711: break;
1712: case JdoExtensionKeys.EXTERNALIZER:
1713: fmd.category = MDStatics.CATEGORY_EXTERNALIZED;
1714: try {
1715: fmd.externalizer = createExternalizer(fmd.type, e);
1716: } catch (RuntimeException x) {
1717: fmd.addError(x, quiet);
1718: }
1719: break;
1720: case JdoExtensionKeys.NULL_IF_NOT_FOUND:
1721: fmd.nullIfNotFound = e.getBoolean();
1722: break;
1723: case JdoExtensionKeys.DEPENDENT:
1724: fmd.dependentValues = e.getBoolean();
1725: break;
1726: case JdoExtensionKeys.KEYS_DEPENDENT:
1727: fmd.dependentKeys = e.getBoolean();
1728: break;
1729: case JdoExtensionKeys.AUTOSET:
1730: int v = e.getEnum(MDE.AUTOSET_ENUM);
1731: int tc = fmd.typeCode;
1732: if (v != AUTOSET_NO && tc != DATE && tc != INT
1733: && tc != SHORT && tc != BYTE) {
1734: RuntimeException ex = BindingSupportImpl
1735: .getInstance()
1736: .runtime(
1737: "The autoset extension "
1738: + "may only be used on java.util.Date, int, short and byte fields\n"
1739: + e.getContext());
1740: fmd.addError(ex, quite);
1741: }
1742: fmd.setAutoSet(v);
1743: break;
1744: case JdoExtensionKeys.NULL_VALUE:
1745: case JdoExtensionKeys.FETCH_GROUP:
1746: case JdoExtensionKeys.VALID_CLASS:
1747: // these are handled later
1748: break;
1749: case JdoExtensionKeys.SCO_FACTORY:
1750: try {
1751: Class factoryClass = ClassHelper.get()
1752: .classForName(e.value, false, loader);
1753: Object factory = factoryClass.newInstance();
1754: fmd.setScoFactory(factory);
1755: } catch (Exception ex) {
1756: RuntimeException exception = BindingSupportImpl
1757: .getInstance().runtime(
1758: "Unable to add SCO factory mapping:\n"
1759: + ex.getMessage(), ex);
1760: fmd.addError(exception, quite);
1761: }
1762: break;
1763: default:
1764: if (e.isCommon()) {
1765: RuntimeException ex = BindingSupportImpl
1766: .getInstance().runtime(
1767: "Unexpected extension\n "
1768: + e.getContext());
1769: fmd.addError(ex, quite);
1770: }
1771: }
1772: }
1773: }
1774:
1775: /**
1776: * Convert a meta data type name (int, String, Integer, za.co.hemtech.Blah
1777: * etc.) into a Class.
1778: *
1779: * @param name The name as in the meta data (e.g. Blah)
1780: * @param qname The name with package added (e.g. za.co.hemtech.Blah)
1781: * @param descr Description of name for error messages (e.g. value-class)
1782: * @param context Element to supply context for error messages
1783: * @return Class or null if name is not found and qname is null
1784: */
1785: private Class resolveTypeName(String name, String qname,
1786: String descr, JdoElement context) {
1787: Class type = MDStaticUtils.toSimpleClass(name);
1788: if (type == null) {
1789: try {
1790: if (name.endsWith(ARRAY_SUFFIX)) {
1791: return Array.newInstance(
1792: resolveTypeName(name.substring(0, name
1793: .length()
1794: - ARRAY_SUFFIX.length()), qname
1795: .substring(0, qname.length()
1796: - ARRAY_SUFFIX.length()),
1797: descr, context), 0).getClass();
1798: } else {
1799: type = loadClass(name);
1800: }
1801: } catch (ClassNotFoundException e) {
1802: //ignore
1803: }
1804: if (qname != null) {
1805: try {
1806: type = loadClass(qname);
1807: } catch (ClassNotFoundException e) {
1808: throw BindingSupportImpl.getInstance().runtime(
1809: descr + " class not found: " + qname + "\n"
1810: + context.getContext(), e);
1811: }
1812: }
1813: }
1814: return type;
1815: }
1816:
1817: /**
1818: * Fill in collection, array or map related fields for the meta data.
1819: */
1820: private void fillCollectionMetaData(FieldMetaData fmd) {
1821: String msg = null;
1822: JdoExtension[] extensions = null;
1823: Class t;
1824: switch (fmd.category) {
1825: case CATEGORY_COLLECTION:
1826: if (fmd.jdoArray != null) {
1827: msg = "array";
1828: } else if (fmd.jdoMap != null)
1829: msg = "map";
1830: if (msg != null)
1831: break;
1832: fmd.ordered = List.class.isAssignableFrom(fmd.type)
1833:
1834: ;
1835: fmd.setElementType(Object.class);
1836: JdoCollection col = fmd.jdoCollection;
1837: if (col != null) {
1838: extensions = col.extensions;
1839: t = resolveTypeName(col.elementType, col
1840: .getElementTypeQName(), "element-type", col);
1841: } else {
1842: t = null;
1843: extensions = null;
1844: }
1845: if (t == null) {
1846: t = MetaDataUtils.getGenericElementType(fmd
1847: .getReflectField());
1848: if (t == null)
1849: t = Object.class;
1850: }
1851:
1852: fmd.setElementType(t);
1853: fmd.elementTypeMetaData = (ClassMetaData) classMap
1854: .get(fmd.elementType);
1855: if (col != null && col.embeddedElement != NOT_SET) {
1856: fmd.embeddedElement = col.embeddedElement == TRUE;
1857: }
1858: break;
1859:
1860: case CATEGORY_ARRAY:
1861: if (fmd.jdoCollection != null) {
1862: msg = "collection";
1863: } else if (fmd.jdoMap != null)
1864: msg = "map";
1865: if (msg != null)
1866: break;
1867: fmd.ordered = true;
1868: fmd.setElementType(fmd.componentType);
1869: fmd.elementTypeMetaData = (ClassMetaData) classMap
1870: .get(fmd.elementType);
1871: JdoArray ar = fmd.jdoArray;
1872: if (ar != null) {
1873: extensions = ar.extensions;
1874: if (ar.embeddedElement != NOT_SET) {
1875: fmd.embeddedElement = ar.embeddedElement == TRUE;
1876: }
1877: }
1878: break;
1879:
1880: case CATEGORY_MAP:
1881: if (fmd.jdoArray != null) {
1882: msg = "array";
1883: } else if (fmd.jdoCollection != null)
1884: msg = "collection";
1885: if (msg != null)
1886: break;
1887: fmd.setElementType(Object.class);
1888: fmd.setKeyType(Object.class);
1889: JdoMap map = fmd.jdoMap;
1890: extensions = map == null ? null : map.extensions;
1891: if (map != null) {
1892: t = resolveTypeName(map.valueType, map
1893: .getValueTypeQName(), "value-type", map);
1894: extensions = map.extensions;
1895: } else {
1896: t = null;
1897: extensions = null;
1898: }
1899: if (t == null) {
1900: t = MetaDataUtils.getGenericValueType(fmd
1901: .getReflectField());
1902: if (t == null)
1903: t = Object.class;
1904: }
1905: fmd.setElementType(t);
1906: fmd.elementTypeMetaData = (ClassMetaData) classMap
1907: .get(fmd.elementType);
1908: if (map != null && map.embeddedValue != NOT_SET) {
1909: fmd.embeddedElement = map.embeddedValue == TRUE;
1910: }
1911: if (map != null) {
1912: t = resolveTypeName(map.keyType, map.getKeyTypeQName(),
1913: "key-type", map);
1914: } else {
1915: t = null;
1916: }
1917: if (t == null) {
1918: t = MetaDataUtils.getGenericKeyType(fmd
1919: .getReflectField());
1920: if (t == null)
1921: t = Object.class;
1922: }
1923: fmd.setKeyType(t);
1924: fmd.keyTypeMetaData = (ClassMetaData) classMap
1925: .get(fmd.keyType);
1926: if (map != null && map.embeddedKey != NOT_SET) {
1927: fmd.embeddedKey = map.embeddedKey == TRUE;
1928: }
1929: break;
1930: }
1931: if (msg != null) {
1932: throw BindingSupportImpl.getInstance().runtime(
1933: "Element <" + msg + "> is "
1934: + "not allowed for field " + fmd.name
1935: + "\n" + fmd.jdoField.getContext());
1936: }
1937:
1938: // if field has dependent=true check that this is ok
1939: switch (fmd.category) {
1940: case CATEGORY_COLLECTION:
1941: case CATEGORY_ARRAY:
1942: case CATEGORY_MAP:
1943: // if dependent is true then the element/value must be PC class
1944: if (!fmd.dependentValues || fmd.isElementTypePC())
1945: break;
1946: default:
1947: if (fmd.category == CATEGORY_REF
1948: || fmd.category == CATEGORY_POLYREF
1949: || !fmd.dependentValues) {
1950: break;
1951: }
1952: throw BindingSupportImpl
1953: .getInstance()
1954: .runtime(
1955: "The dependent extension is only valid for "
1956: + "references, collections and maps of persistent classes\n"
1957: + fmd.jdoField.getContext());
1958: }
1959:
1960: // if field keys-dependent=true check that this is ok
1961: if (fmd.category == CATEGORY_MAP) {
1962: // if keys-dependent is true then the key must be PC class
1963: if (fmd.dependentKeys && fmd.keyTypeMetaData == null) {
1964: throw BindingSupportImpl.getInstance().runtime(
1965: "The keys-dependent extension is only valid for "
1966: + "maps with a persistent class key\n"
1967: + fmd.jdoField.getContext());
1968: }
1969: } else if (fmd.dependentKeys) {
1970: throw BindingSupportImpl.getInstance().runtime(
1971: "The keys-dependent extension is only valid for maps\n"
1972: + fmd.jdoField.getContext());
1973: }
1974:
1975: // process extensions
1976: if (extensions != null) {
1977: int n = extensions.length;
1978: JdoExtension ordered = null;
1979: JdoExtension ordering = null;
1980: for (int i = 0; i < n; i++) {
1981: JdoExtension e = extensions[i];
1982: switch (e.key) {
1983: case JdoExtensionKeys.ORDERED:
1984: if (!fmd.ordered) {
1985: throw BindingSupportImpl.getInstance().runtime(
1986: "The ordered extension is not allowed here\n"
1987: + e.getContext());
1988: }
1989: ordered = e;
1990: break;
1991: case JdoExtensionKeys.ORDERING:
1992: ordering = e;
1993: break;
1994: case JdoExtensionKeys.MANAGED:
1995: // this is processed later by the JdbcMetaDataBuilder
1996: break;
1997: default:
1998: if (e.isCommon()) {
1999: throw BindingSupportImpl.getInstance().runtime(
2000: "Unexpected extension\n"
2001: + e.getContext());
2002: }
2003: }
2004: }
2005: if (ordered != null)
2006: fmd.ordered = ordered.getBoolean();
2007: if (ordering != null) {
2008: if (ordered != null && ordered.getBoolean()) {
2009: throw BindingSupportImpl.getInstance().runtime(
2010: "You may not specify an ordering if you also have ordered=true\n"
2011: + ordering.getContext());
2012: }
2013: fmd.ordered = false;
2014: try {
2015: fmd.ordering = queryParser.parseOrdering(
2016: fmd.elementTypeMetaData, ordering
2017: .getString());
2018: } catch (ParseException e) {
2019: throw BindingSupportImpl.getInstance().runtime(
2020: "Invalid ordering extension: "
2021: + e.getMessage() + "\n"
2022: + ordering.getContext() + "\n", e);
2023: }
2024: }
2025: }
2026: }
2027:
2028: /**
2029: * Fill in collection, array or map related fields for the meta data.
2030: */
2031: private void setSCOFactory(FieldMetaData fmd) {
2032: if (fmd.checkCustomFactory()) {
2033: return;
2034: }
2035: if (fmd.scoField) {
2036: switch (fmd.category) {
2037: case CATEGORY_SIMPLE:
2038: if (fmd.simpleSCOFactory == null) {
2039: fmd.simpleSCOFactory = scoFactoryRegistry
2040: .getJdoGenieSCOFactory(fmd);
2041: }
2042: break;
2043: case CATEGORY_COLLECTION:
2044: if (fmd.collectionFactory == null) {
2045: fmd.collectionFactory = scoFactoryRegistry
2046: .getJDOGenieSCOCollectionFactory(fmd);
2047: }
2048: break;
2049: case CATEGORY_MAP:
2050: if (fmd.mapFactory == null) {
2051: fmd.mapFactory = scoFactoryRegistry
2052: .getJDOGenieSCOMapFactory(fmd);
2053: }
2054: break;
2055: }
2056: }
2057: }
2058:
2059: /**
2060: * Get the ClassInfo for cmd. A new ClassInfo will be created if none
2061: * exists.
2062: */
2063: private ClassInfo getClassInfo(ClassMetaData cmd) {
2064: ClassInfo ans = (ClassInfo) classInfoMap.get(cmd);
2065: if (ans == null)
2066: classInfoMap.put(cmd, ans = new ClassInfo());
2067: return ans;
2068: }
2069:
2070: /**
2071: * Fill the externalizerMap from the configuration.
2072: */
2073: private void fillExternalizerMap() {
2074: externalizerMap.clear();
2075: int n = config.externalizers.size();
2076: for (int i = 0; i < n; i++) {
2077: ConfigInfo.ExternalizerInfo ei = (ConfigInfo.ExternalizerInfo) config.externalizers
2078: .get(i);
2079: if (!ei.enabled)
2080: continue;
2081: try {
2082: Class key = loadClass(ei.typeName);
2083: Class cls = loadExternalizerClass(ei.externalizerName);
2084: externalizerMap.put(key, createExternalizer(cls, key,
2085: ei.args));
2086: mdutils.registerExternalizedType(key);
2087: } catch (Throwable x) {
2088: RuntimeException e;
2089: if (BindingSupportImpl.getInstance().isOwnException(x)) {
2090: e = (RuntimeException) x;
2091: } else {
2092: e = BindingSupportImpl.getInstance().runtime(
2093: "Unable to create Externalizer for '"
2094: + ei.typeName + "':\n"
2095: + x.toString(), x);
2096: }
2097: jmd.addError(e, quiet);
2098: }
2099: }
2100: }
2101:
2102: private Externalizer createExternalizer(Class externalizerCls,
2103: Class type, Map props) throws IllegalAccessException,
2104: InstantiationException, InvocationTargetException {
2105: Externalizer externalizer;
2106: try {
2107: Constructor m = externalizerCls
2108: .getConstructor(new Class[] { Class.class });
2109: externalizer = (Externalizer) m
2110: .newInstance(new Object[] { type });
2111: } catch (NoSuchMethodException e) {
2112: externalizer = (Externalizer) externalizerCls.newInstance();
2113: }
2114: BeanUtils.setProperties(externalizer, props);
2115: return externalizer;
2116: }
2117:
2118: /**
2119: * Load an externalizer class. This will recognize the short names of
2120: * the built in externalizers and returns the default externalizer for
2121: * a null name.
2122: */
2123: private Class loadExternalizerClass(String name)
2124: throws ClassNotFoundException {
2125: if (name == null
2126: || SerializedExternalizer.SHORT_NAME.equals(name)) {
2127: return SerializedExternalizer.class;
2128: } else if (TypeAsBytesExternalizer.SHORT_NAME.equals(name)) {
2129: return TypeAsBytesExternalizer.class;
2130: } else if (TypeAsStringExternalizer.SHORT_NAME.equals(name)) {
2131: return TypeAsStringExternalizer.class;
2132: }
2133: return loadClass(name);
2134: }
2135:
2136: /**
2137: * Get a externalizer instance for a field type or Serializing Externalizer if null.
2138: */
2139: private Externalizer getExternalizerForType(Class type) {
2140: Externalizer ex = (Externalizer) externalizerMap.get(type);
2141: if (ex == null && Serializable.class.isAssignableFrom(type)) {
2142: try {
2143: ex = createExternalizer(SerializedExternalizer.class,
2144: type, null);
2145: } catch (Exception e) {
2146: throw BindingSupportImpl.getInstance().internal(
2147: e.getMessage(), e);
2148: }
2149: }
2150: return ex;
2151: }
2152:
2153: /**
2154: * Create a Externalizer instance from an extension. This will return
2155: * instances of standard externalizers if their SHORT_NAME's are used.
2156: */
2157: private Externalizer createExternalizer(Class type, JdoExtension e) {
2158: try {
2159: String cname = e.getString();
2160: Class cls = loadExternalizerClass(cname);
2161: return createExternalizer(cls, type, e.getPropertyMap());
2162: } catch (Throwable x) {
2163: x = BindingSupportImpl.getInstance().findCause(x);
2164: if (BindingSupportImpl.getInstance().isOwnException(x)) {
2165: throw (RuntimeException) x;
2166: }
2167: throw BindingSupportImpl.getInstance().runtime(
2168: x.toString(), x);
2169: }
2170: }
2171:
2172: /**
2173: * Should the current state information be sent with a request to load
2174: * any fetch group containing secondary fields?
2175: */
2176: public boolean isSendCurrentForFGWithSecFields() {
2177: return false;
2178: }
2179:
2180: /**
2181: * When writing to an object must it be completely read first?
2182: */
2183: public boolean isReadObjectBeforeWrite() {
2184: return false;
2185: }
2186: }
|