0001: /**********************************************************************
0002: Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015: Contributors:
0016: 2004 Kikuchi Kousuke - org.jpox.enhancer.conf.JDOConfigClass
0017: 2004 Marco Schulze (NightLabs) - changed the behaviour to warn only if
0018: an inherited class declares an own objectid and it equals the
0019: one of the superclass.
0020: 2004 Andy Jefferson - Added discriminator/inheritance checks
0021: 2004 Erik Bengtson - changes for application identity
0022: 2004 Andy Jefferson - moved PK class checks out into JDOUtils
0023: 2007 Xuan Baldauf - little reduction in code duplication to anticipate changes regarding issue http://www.jpox.org/servlet/jira/browse/CORE-3272
0024: ...
0025: **********************************************************************/package org.jpox.metadata;
0026:
0027: import java.lang.reflect.Field;
0028: import java.lang.reflect.Method;
0029: import java.lang.reflect.Modifier;
0030: import java.util.ArrayList;
0031: import java.util.Collection;
0032: import java.util.Collections;
0033: import java.util.HashMap;
0034: import java.util.Iterator;
0035: import java.util.List;
0036:
0037: import org.jpox.ClassLoaderResolver;
0038: import org.jpox.exceptions.ClassNotResolvedException;
0039: import org.jpox.exceptions.JPOXException;
0040: import org.jpox.util.ClassUtils;
0041: import org.jpox.util.JPOXLogger;
0042:
0043: /**
0044: * Representation of the MetaData of a class. Extends the abstract definition to include
0045: * implementations, fields, embedded-only tags. Has a parent PackageMetaData that
0046: * can contain the metadata for several classes.
0047: *
0048: * <H3>Lifecycle state</H3>
0049: * This object supports 3 lifecycle states. The first is the raw
0050: * constructed object which represents pure MetaData (maybe from a MetaData
0051: * file). The second is a "populated" object which represents MetaData for a
0052: * Class with the metadata aligned to be appropriate for that Class.
0053: * The third is "initialised" once the internal arrays are created.
0054: * This object, once populated, will represent ALL fields in the class
0055: * (including static, final and transient fields).
0056: *
0057: * <H3>Fields/Properties</H3>
0058: * This object keeps a list of FieldMetaData/PropertyMetaData objects for the fields of this class.
0059: * In addition it has an array of FieldMetaData
0060: * objects representing those that are actually managed by JDO
0061: * ("managedFields"). This second set does not contain things like static, final
0062: * or transient fields since JDO doesn't support those yet.
0063: * <P>Fields are of 2 types. The first are normal fields of this class.
0064: * These have their own "relative" field number, relative to this class.
0065: * The second type are "overriding" fields which override the baseline field
0066: * in a superclass. These fields have no "relative" field number since they are
0067: * relative to this class (and such a relative field number would make no sense).
0068: * Fields are all added through addField() during the parse process, and
0069: * are updated during the populate/initialise process to define their relative field
0070: * numbers. Please refer to FieldMetaData for more details of fields.
0071: *
0072: * <H3>Numbering of fields</H3>
0073: * Fields of the class are numbered in 2 ways. The first way is the numbering
0074: * within a class. In a class, the field 'id's will start at 0. If a class is
0075: * inherited, it will also have a second numbering for its fields - the
0076: * "absolute" numbering. With "absolute" numbering, the fields start at the
0077: * first field in the root superclass which has absolute number 0, and they are
0078: * numbered from there, navigating down the hierarchy. In terms of what is
0079: * stored in the records, the FieldMetaData stores fieldId as the first
0080: * method (relative to the class it is in). The "absolute" numbering is
0081: * always derived from this and the inheritance hierarchy.
0082: *
0083: * <H3>MetaData Element</H3>
0084: * The MetaData Element represented here is as follows
0085: * <PRE>
0086: * <!ELEMENT class (datastore-identity?, implements*, inheritance?, version?, join*,
0087: * foreign-key*, index*, unique*, field*, column*, query*, fetch-group*, extension*)>
0088: * <!ATTLIST class name CDATA #REQUIRED>
0089: * <!ATTLIST class identity-type (application|datastore|nondurable) #IMPLIED>
0090: * <!ATTLIST class catalog CDATA #IMPLIED>
0091: * <!ATTLIST class schema CDATA #IMPLIED>
0092: * <!ATTLIST class table CDATA #IMPLIED>
0093: * <!ATTLIST class persistence-capable-superclass CDATA #IMPLIED>
0094: * <!ATTLIST class objectid-class CDATA #IMPLIED>
0095: * <!ATTLIST class requires-extent (true|false) 'true'>
0096: * <!ATTLIST class detachable (true|false) 'true'>
0097: * <!ATTLIST class embedded-only (true|false) #IMPLIED>
0098: * <!ATTLIST class persistence-modifier
0099: * (persistence-capable|persistence-aware|non-persistent) #IMPLIED>
0100: * </PRE>
0101: *
0102: * @since 1.1
0103: * @version $Revision: 1.136 $
0104: */
0105: public class ClassMetaData extends AbstractClassMetaData {
0106: /** List of implements. */
0107: protected List implementations = new ArrayList();
0108:
0109: // -------------------------------------------------------------------------
0110: // Fields below here are not represented in the output MetaData. They are
0111: // for use internally in the operation of the JDO system. The majority are
0112: // for convenience to save iterating through the fields since the fields
0113: // are fixed once initialised.
0114:
0115: /*** ImplementsMetaData */
0116: protected ImplementsMetaData[] implements MetaData;
0117:
0118: /** is the persistable class abstract. */
0119: protected boolean isAbstractPersistenceCapable;
0120:
0121: /** whether the populate method is running **/
0122: private boolean populating = false;
0123:
0124: // ----------------------------- Constructors ------------------------------
0125:
0126: /**
0127: * Constructor.
0128: * Takes the basic string information found in the MetaData file.
0129: * @param parent The package to which this class belongs
0130: * @param name Name of class
0131: * @param identityType identity-type flag
0132: * @param objectidClass Primary key class name
0133: * @param requiresExtent Whether the class requires an extent
0134: * @param detachable Whether the class can be detached
0135: * @param modifier persistence-modifier tag
0136: * @param embeddedOnly embedded-only tag
0137: * @param persistenceCapableSuperclass Name of PC superclass
0138: * @param catalog Name for catalog
0139: * @param schema Name for schema
0140: * @param table RDBMS table to store the class in
0141: * @param entityName the entity name required by JPA §4.3.1
0142: */
0143: public ClassMetaData(final PackageMetaData parent,
0144: final String name, final String identityType,
0145: final String objectidClass, final String requiresExtent,
0146: final String detachable, final String embeddedOnly,
0147: final String modifier,
0148: final String persistenceCapableSuperclass,
0149: final String catalog, final String schema,
0150: final String table, final String entityName) {
0151: super (parent, name, identityType, objectidClass,
0152: requiresExtent, detachable, embeddedOnly, modifier,
0153: persistenceCapableSuperclass, catalog, schema, table,
0154: entityName);
0155: }
0156:
0157: /**
0158: * Constructor for creating the ClassMetaData for an implementation of a "persistent-interface".
0159: * @param imd MetaData for the "persistent-interface"
0160: * @param implClassName Name of the implementation class
0161: * @param copyFields Whether to copy the fields of the interface too
0162: */
0163: public ClassMetaData(final InterfaceMetaData imd,
0164: String implClassName, boolean copyFields) {
0165: super (imd, implClassName, copyFields);
0166: }
0167:
0168: /**
0169: * Constructor for creating the ClassMetaData for an implementation of a "persistent-abstract-class".
0170: * @param cmd MetaData for the implementation of the "persistent-abstract-class"
0171: * @param implClassName Name of the implementation class
0172: */
0173: public ClassMetaData(final ClassMetaData cmd, String implClassName) {
0174: super (cmd, implClassName);
0175: }
0176:
0177: /**
0178: * Method to provide the details of the class being represented by this
0179: * MetaData. This can be used to firstly provide defaults for attributes
0180: * that aren't specified in the MetaData, and secondly to report any errors
0181: * with attributes that have been specifed that are inconsistent with the
0182: * class being represented.
0183: * <P>
0184: * One possible use of this method would be to take a basic ClassMetaData
0185: * for a class and call this, passing in the users class. This would then
0186: * add FieldMetaData for all fields in this class providing defaults for
0187: * all of these.
0188: *
0189: * @param clr ClassLoaderResolver to use in loading any classes
0190: * @param primary the primary ClassLoader to use (or null)
0191: */
0192: public synchronized void populate(ClassLoaderResolver clr,
0193: ClassLoader primary) {
0194: if (isInitialised() || isPopulated()) {
0195: JPOXLogger.METADATA.error(LOCALISER.msg("044068", name));
0196: throw new JPOXException(LOCALISER.msg("044068", fullName))
0197: .setFatal();
0198: }
0199: if (populating) {
0200: return;
0201: }
0202:
0203: try {
0204: if (JPOXLogger.METADATA.isDebugEnabled()) {
0205: JPOXLogger.METADATA.debug(LOCALISER.msg("044075",
0206: fullName));
0207: }
0208: populating = true;
0209:
0210: Class cls = loadClass(clr, primary);
0211:
0212: isAbstractPersistenceCapable = Modifier.isAbstract(cls
0213: .getModifiers());
0214:
0215: // Load any Annotations definition for this class
0216: if (!isMetaDataComplete()) {
0217: getMetaDataManager().addAnnotationsDataToClass(cls,
0218: this , clr);
0219: }
0220:
0221: // Load any ORM definition for this class
0222: getMetaDataManager().addORMDataToClass(cls, clr);
0223:
0224: // If a class is an inner class and is non-static it is invalid
0225: if (ClassUtils.isInnerClass(fullName)
0226: && !Modifier.isStatic(cls.getModifiers())
0227: && persistenceModifier == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
0228: throw new InvalidMetaDataException(LOCALISER, "044063",
0229: fullName);
0230: }
0231:
0232: if (entityName == null) {
0233: // No entity name given so just default to the name of the class (without package)
0234: this .entityName = name;
0235: }
0236:
0237: determineSuperClassName(clr, cls);
0238:
0239: inheritDetachableSettings();
0240:
0241: inheritIdentity();
0242:
0243: determineIdentity();
0244:
0245: validateUserInputForIdentity();
0246:
0247: addMetaDataForMembersNotInMetaData(cls);
0248:
0249: if (objectidClass == null) {
0250: // No user-defined objectid-class but potentially have SingleFieldIdentity so make sure PK fields are set
0251: populateMemberMetaData(clr, cls, true, primary); // Make sure all PK fields (and superclasses) are set before objectid-class
0252: determineObjectIdClass(clr);
0253: populateMemberMetaData(clr, cls, false, primary); // Populate non-PK fields
0254: } else {
0255: populateMemberMetaData(clr, cls, true, primary);
0256: populateMemberMetaData(clr, cls, false, primary);
0257: determineObjectIdClass(clr);
0258: }
0259:
0260: validateUserInputForInheritanceMetaData();
0261:
0262: determineInheritanceMetaData();
0263:
0264: validateDeprecatedMetaData();
0265:
0266: validateUnmappedColumns();
0267:
0268: // populate the implements
0269: for (int i = 0; i < implementations.size(); i++) {
0270: ((ImplementsMetaData) implementations.get(i))
0271: .populate(clr);
0272: }
0273:
0274: if (persistentInterfaceImplNeedingTableFromSuperclass) {
0275: // Need to go up to next superinterface and make sure its metadata is populated
0276: // until we find the next interface with metadata with inheritance strategy of "new-table".
0277: AbstractClassMetaData acmd = getMetaDataForSuperinterfaceManagingTable(
0278: cls, clr);
0279: if (acmd != null) {
0280: table = acmd.table;
0281: schema = acmd.schema;
0282: catalog = acmd.catalog;
0283: }
0284: persistentInterfaceImplNeedingTableFromSuperclass = false;
0285: } else if (persistentInterfaceImplNeedingTableFromSubclass) {
0286: // TODO Cater for finding the subclass-table that manages our table
0287: persistentInterfaceImplNeedingTableFromSubclass = false;
0288: }
0289:
0290: setPopulated();
0291: } catch (RuntimeException e) {
0292: JPOXLogger.METADATA.debug(e);
0293: throw e;
0294: } finally {
0295: populating = false;
0296: }
0297: }
0298:
0299: /**
0300: * Method to find a superinterface with MetaData that specifies NEW_TABLE inheritance strategy
0301: * @param cls The class
0302: * @param clr ClassLoader resolver
0303: * @return The AbstractClassMetaData for the class managing the table
0304: */
0305: private AbstractClassMetaData getMetaDataForSuperinterfaceManagingTable(
0306: Class cls, ClassLoaderResolver clr) {
0307: Collection super interfaces = ClassUtils.getSuperinterfaces(cls);
0308: Iterator iter = super interfaces.iterator();
0309: while (iter.hasNext()) {
0310: Class super intf = (Class) iter.next();
0311: AbstractClassMetaData acmd = getMetaDataManager()
0312: .getMetaDataForInterface(super intf, clr);
0313: if (acmd != null && acmd.getInheritanceMetaData() != null) {
0314: if (acmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.NEW_TABLE) {
0315: // Found it
0316: return acmd;
0317: } else if (acmd.getInheritanceMetaData()
0318: .getStrategyValue() == InheritanceStrategy.SUPERCLASS_TABLE) {
0319: // Try further up the hierarchy
0320: return getMetaDataForSuperinterfaceManagingTable(
0321: super intf, clr);
0322: }
0323: }
0324: }
0325: return null;
0326: }
0327:
0328: /**
0329: * Add MetaData of fields/properties not declared in MetaData.
0330: * @param cls Class represented by this metadata
0331: */
0332: protected void addMetaDataForMembersNotInMetaData(Class cls) {
0333: // Access API since we treat things differently for JPA and JDO
0334: String api = getMetaDataManager().getOMFContext().getApi();
0335:
0336: // Add fields/properties for the class that don't have MetaData.
0337: // We use Reflection here since JDOImplHelper would only give use info
0338: // for enhanced files (and the enhancer needs unenhanced as well).
0339: // NOTE 1 : We ignore fields/properties in superclasses
0340: // NOTE 2 : We ignore "enhanced" fields/properties (added by the JPOX enhancer)
0341: // NOTE 3 : We ignore inner class fields/properties (containing "$")
0342: // NOTE 4 : We sort the fields/properties into ascending alphabetical order
0343: Collections.sort(members);
0344: try {
0345: // check if we have any persistent properties (JPA)
0346: boolean hasProperties = false;
0347: for (int i = 0; i < members.size(); i++) {
0348: if (((AbstractMemberMetaData) members.get(i))
0349: .isProperty()) {
0350: hasProperties = true;
0351: break;
0352: }
0353: }
0354:
0355: if (hasProperties && api.equalsIgnoreCase("JPA")) {
0356: // JPA : when we are using properties go through and add properties for those not specified.
0357: // Process all (reflected) methods in the populating class
0358: Method[] clsMethods = cls.getDeclaredMethods();
0359: for (int i = 0; i < clsMethods.length; i++) {
0360: // Limit to getter methods in this class, that aren't enhancer-added methods
0361: // that aren't inner class methods, and that aren't static
0362: if (clsMethods[i].getDeclaringClass().getName()
0363: .equals(fullName)
0364: && !clsMethods[i].getName().startsWith(
0365: "jdo")
0366: && (clsMethods[i].getName().startsWith(
0367: "get") || clsMethods[i].getName()
0368: .startsWith("is"))
0369: && !ClassUtils.isInnerClass(clsMethods[i]
0370: .getName())
0371: && !Modifier.isStatic(clsMethods[i]
0372: .getModifiers())) {
0373: // Find if there is metadata for this property
0374: String propertyName = ClassUtils
0375: .getFieldNameForJavaBeanGetter(clsMethods[i]
0376: .getName());
0377: if (Collections.binarySearch(members,
0378: propertyName) < 0) // AbstractMemberMetaData implements Comparable
0379: {
0380: // No field/property of this name - add a default PropertyMetaData for this method
0381: JPOXLogger.METADATA.debug(LOCALISER.msg(
0382: "044060", propertyName, name));
0383: AbstractMemberMetaData fmd = getMetaDataManager()
0384: .getMetaDataFactory()
0385: .newPropertyObject(this ,
0386: propertyName, null, null,
0387: null, null, null, null,
0388: null, null, null, null,
0389: null, null, null, null,
0390: null, null, null, null,
0391: null, null, null);
0392: members.add(fmd);
0393: Collections.sort(members);
0394: } else {
0395: // Field/property exists
0396: }
0397: }
0398: }
0399: }
0400:
0401: // Process all (reflected) fields in the populating class
0402: Field[] clsFields = cls.getDeclaredFields();
0403: for (int i = 0; i < clsFields.length; i++) {
0404: // Limit to fields in this class, that aren't enhancer-added fields
0405: // that aren't inner class fields, and that aren't static
0406: if (clsFields[i].getDeclaringClass().getName().equals(
0407: fullName)
0408: && !clsFields[i].getName().startsWith("jdo")
0409: && !ClassUtils.isInnerClass(clsFields[i]
0410: .getName())
0411: && !Modifier.isStatic(clsFields[i]
0412: .getModifiers())) {
0413: // Find if there is metadata for this field.
0414: // This is possible as FieldMetaData implements Comparable
0415: if (Collections.binarySearch(members, clsFields[i]
0416: .getName()) < 0) {
0417: // No field/property of this name
0418: if (hasProperties
0419: && api.equalsIgnoreCase("JPA")) {
0420: // JPA : Class has properties but field not present, so add as transient field (JPA)
0421: AbstractMemberMetaData fmd = getMetaDataManager()
0422: .getMetaDataFactory()
0423: .newFieldObject(this ,
0424: clsFields[i].getName(),
0425: null, "none", null, null,
0426: null, null, null, null,
0427: null, null, null, null,
0428: null, null, null, null,
0429: null, null, null, null);
0430: members.add(fmd);
0431: Collections.sort(members);
0432: } else {
0433: // Class has fields but field not present, so add as field
0434: JPOXLogger.METADATA.debug(LOCALISER.msg(
0435: "044060", clsFields[i].getName(),
0436: name));
0437: AbstractMemberMetaData fmd = getMetaDataManager()
0438: .getMetaDataFactory()
0439: .newFieldObject(this ,
0440: clsFields[i].getName(),
0441: null, null, null, null,
0442: null, null, null, null,
0443: null, null, null, null,
0444: null, null, null, null,
0445: null, null, null, null);
0446: members.add(fmd);
0447: Collections.sort(members);
0448: }
0449: }
0450: }
0451: }
0452: } catch (Exception e) {
0453: JPOXLogger.METADATA.error(e.getMessage(), e);
0454: throw new RuntimeException(e.getMessage());
0455: }
0456: }
0457:
0458: /**
0459: * Populate MetaData for all members.
0460: * @param clr The ClassLoaderResolver
0461: * @param cls This class
0462: * @param pkMembers Process pk fields/properties (or non-PK if false)
0463: * @param primary the primary ClassLoader to use (or null)
0464: * @throws InvalidMetaDataException if the Class for a declared type in a field cannot be loaded by the <code>clr</code>
0465: * @throws InvalidMetaDataException if a field declared in the MetaData does not exist in the Class
0466: */
0467: protected void populateMemberMetaData(ClassLoaderResolver clr,
0468: Class cls, boolean pkMembers, ClassLoader primary) {
0469: Collections.sort(members);
0470:
0471: // Populate the FieldMetaData with their real field values
0472: // This will populate any containers in these fields also
0473: Iterator fields_iter = members.iterator();
0474: while (fields_iter.hasNext()) {
0475: AbstractMemberMetaData fmd = (AbstractMemberMetaData) fields_iter
0476: .next();
0477: if (pkMembers == fmd.isPrimaryKey()) {
0478: Class fieldCls = cls;
0479: if (fmd.className != null
0480: && fmd.className.equals("#UNKNOWN")) {
0481: // Field is for a superclass but we didnt know which at creation so resolve it
0482: if (pcSuperclassMetaData != null) {
0483: AbstractMemberMetaData super Fmd = pcSuperclassMetaData
0484: .getMetaDataForMember(fmd.getName());
0485: if (super Fmd != null) {
0486: // Field is for a superclass so set its "className"
0487: if (super Fmd.className != null) {
0488: fmd.className = super Fmd.className;
0489: } else {
0490: fmd.className = super Fmd.getClassName();
0491: }
0492: }
0493: } else {
0494: // No superclass so it doenst make sense so assume to be for this class
0495: fmd.className = null;
0496: }
0497: }
0498: if (!fmd.fieldBelongsToClass()) {
0499: // Field overrides a field in a superclass, so find the class
0500: try {
0501: fieldCls = clr.classForName(fmd.getClassName());
0502: } catch (ClassNotResolvedException cnre) {
0503: // Not found at specified location, so try the same package as this class
0504: String fieldClassName = getPackageName() + "."
0505: + fmd.getClassName();
0506: try {
0507: fieldCls = clr.classForName(fieldClassName);
0508: fmd.setClassName(fieldClassName);
0509: } catch (ClassNotResolvedException cnre2) {
0510: JPOXLogger.METADATA.error(LOCALISER.msg(
0511: "044080", fieldClassName));
0512: throw new InvalidMetaDataException(
0513: LOCALISER, "044080", fieldClassName);
0514: }
0515: }
0516: }
0517:
0518: boolean populated = false;
0519: if (fmd.isProperty()) {
0520: // User class must have a getter and setter for this property as per Java Beans
0521: Method getMethod = null;
0522: try {
0523: // Find the getter
0524: // a). Try as a standard form of getter (getXXX)
0525: getMethod = fieldCls.getDeclaredMethod(
0526: ClassUtils.getJavaBeanGetterName(fmd
0527: .getName(), false), null);
0528: } catch (Exception e) {
0529: try {
0530: // b). Try as a boolean form of getter (isXXX)
0531: getMethod = fieldCls.getDeclaredMethod(
0532: ClassUtils.getJavaBeanGetterName(
0533: fmd.getName(), true), null);
0534: } catch (Exception e2) {
0535: }
0536: }
0537: if (getMethod == null
0538: && fmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) {
0539: // Property is persistent yet no getter!
0540: throw new InvalidMetaDataException(LOCALISER,
0541: "044073", fullName, fmd.getName());
0542: }
0543:
0544: Method setMethod = null;
0545: try {
0546: // Find the setter
0547: String setterName = ClassUtils
0548: .getJavaBeanSetterName(fmd.getName());
0549: Method[] methods = fieldCls
0550: .getDeclaredMethods();
0551: for (int i = 0; i < methods.length; i++) {
0552: if (methods[i].getName().equals(setterName)
0553: && methods[i].getParameterTypes() != null
0554: && methods[i].getParameterTypes().length == 1) {
0555: setMethod = methods[i];
0556: }
0557: }
0558: } catch (Exception e) {
0559: }
0560: if (setMethod == null
0561: && fmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) {
0562: // Property is persistent yet no setter!
0563: throw new InvalidMetaDataException(LOCALISER,
0564: "044074", fullName, fmd.getName());
0565: }
0566:
0567: // Populate the property using the getter
0568: if (getMethod != null) {
0569: fmd.populate(clr, null, getMethod, primary);
0570: populated = true;
0571: }
0572: }
0573: // TODO Why is this next block capable of processing things declared as property?
0574: if (!populated) {
0575: Field cls_field = null;
0576: try {
0577: cls_field = fieldCls.getDeclaredField(fmd
0578: .getName());
0579: } catch (Exception e) {
0580: }
0581: if (cls_field != null) {
0582: fmd.populate(clr, cls_field, null, primary);
0583: populated = true;
0584: }
0585: }
0586: if (!populated) {
0587: // MetaData field doesn't exist in the class!
0588: throw new InvalidMetaDataException(LOCALISER,
0589: "044071", fullName, fmd.getFullFieldName());
0590: }
0591: }
0592: }
0593: }
0594:
0595: /**
0596: * Method to initialise the object, creating internal convenience arrays.
0597: * Initialises all sub-objects. populate() should be called BEFORE calling this.
0598: */
0599: public synchronized void initialise() {
0600: if (populating) {
0601: return;
0602: }
0603: checkPopulated();
0604: if (isInitialised()) {
0605: return;
0606: }
0607:
0608: if (pcSuperclassMetaData != null) {
0609: // We need our superclass to be initialised before us because we rely on information there
0610: if (!pcSuperclassMetaData.isInitialised()) {
0611: pcSuperclassMetaData.initialise();
0612: }
0613: }
0614:
0615: if (JPOXLogger.METADATA.isDebugEnabled()) {
0616: JPOXLogger.METADATA
0617: .debug(LOCALISER.msg("044076", fullName));
0618: }
0619:
0620: // Count the fields/properties of the relevant category
0621: Iterator membersIter = members.iterator();
0622: int numManaged = 0;
0623: int numOverridden = 0;
0624: while (membersIter.hasNext()) {
0625: AbstractMemberMetaData fmd = (AbstractMemberMetaData) membersIter
0626: .next();
0627:
0628: // Initialise the FieldMetaData (and its sub-objects)
0629: fmd.initialise();
0630: if (fmd.isJdoField()) {
0631: if (fmd.fieldBelongsToClass()) {
0632: numManaged++;
0633: } else {
0634: numOverridden++;
0635: }
0636: }
0637: }
0638:
0639: // Generate the "managed members" list
0640: managedMembers = new AbstractMemberMetaData[numManaged];
0641: overriddenMembers = new AbstractMemberMetaData[numOverridden];
0642:
0643: membersIter = members.iterator();
0644: int field_id = 0;
0645: int overridden_field_id = 0;
0646: memberPositionsByName = new HashMap();
0647: while (membersIter.hasNext()) {
0648: AbstractMemberMetaData mmd = (AbstractMemberMetaData) membersIter
0649: .next();
0650: if (mmd.isJdoField()) {
0651: if (mmd.fieldBelongsToClass()) {
0652: mmd.setFieldId(field_id);
0653: managedMembers[field_id] = mmd;
0654: memberPositionsByName.put(mmd.getName(),
0655: new Integer(field_id));
0656: field_id++;
0657: } else {
0658: overriddenMembers[overridden_field_id++] = mmd;
0659: AbstractMemberMetaData super Fmd = pcSuperclassMetaData
0660: .getMemberBeingOverridden(mmd.getName());
0661: if (super Fmd != null) {
0662: // Merge in any additional info not specified in the overridden field
0663: if (super Fmd.isPrimaryKey()) {
0664: mmd.setPrimaryKey();
0665: }
0666: // TODO Add on any more info not present
0667: }
0668: }
0669: }
0670: }
0671:
0672: if (pcSuperclassMetaData != null) {
0673: if (!pcSuperclassMetaData.isInitialised()) {
0674: pcSuperclassMetaData.initialise();
0675: }
0676: noOfInheritedManagedMembers = pcSuperclassMetaData
0677: .getNoOfInheritedManagedMembers()
0678: + pcSuperclassMetaData.getNoOfManagedMembers();
0679: }
0680:
0681: // Set up the various convenience arrays of field numbers
0682: int total_field_count = noOfInheritedManagedMembers
0683: + managedMembers.length;
0684: this .memberCount = total_field_count;
0685: allMemberPositions = new int[total_field_count];
0686: dfgMemberFlags = new boolean[total_field_count];
0687: scoMutableMemberFlags = new boolean[total_field_count];
0688: nonPkMemberFlags = new boolean[total_field_count];
0689: int pk_field_count = 0;
0690: int dfg_field_count = 0;
0691: int scm_field_count = 0;
0692: int pc_field_count = 0;
0693: for (int i = 0; i < total_field_count; i++) {
0694: allMemberPositions[i] = i;
0695:
0696: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
0697: if (fmd.isPrimaryKey()) {
0698: pk_field_count++;
0699: } else {
0700: nonPkMemberFlags[i] = true;
0701: }
0702: if (fmd.isDefaultFetchGroup()) {
0703: dfgMemberFlags[i] = true;
0704: dfg_field_count++;
0705: }
0706: if (fmd.calcIsSecondClassMutable()) {
0707: scoMutableMemberFlags[i] = true;
0708: scm_field_count++;
0709: }
0710: if (fmd.isFieldTypePersistable()) {
0711: pc_field_count++;
0712: }
0713: }
0714:
0715: if (pk_field_count > 0
0716: && identityType != IdentityType.APPLICATION) {
0717: // primary key fields found, but not using application identity
0718: throw new InvalidMetaDataException(LOCALISER, "044078",
0719: fullName, new Integer(pk_field_count), identityType);
0720: } else if (pk_field_count > 0) {
0721: pkMemberPositions = new int[pk_field_count];
0722: for (int i = 0, pk_num = 0; i < total_field_count; i++) {
0723: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
0724: if (fmd.isPrimaryKey()) {
0725: pkMemberPositions[pk_num++] = i;
0726: }
0727: }
0728: } else if (pk_field_count == 0
0729: && identityType == IdentityType.APPLICATION) {
0730: // No primary key fields found
0731: throw new InvalidMetaDataException(LOCALISER, "044077",
0732: fullName, objectidClass);
0733: }
0734:
0735: nonPkMemberPositions = new int[total_field_count
0736: - pk_field_count];
0737: persistenceCapableMemberPositions = new int[pc_field_count];
0738: for (int i = 0, npkf = 0, npc = 0; i < total_field_count; i++) {
0739: AbstractMemberMetaData fmd = getMetaDataForManagedMemberAtAbsolutePositionInternal(i);
0740: if (!fmd.isPrimaryKey()) {
0741: nonPkMemberPositions[npkf++] = i;
0742: }
0743: if (fmd.isFieldTypePersistable()) {
0744: persistenceCapableMemberPositions[npc++] = i;
0745: }
0746: }
0747:
0748: dfgMemberPositions = new int[dfg_field_count];
0749: scoMutableMemberPositions = new int[scm_field_count];
0750: for (int i = 0, dfg_num = 0, scm_num = 0; i < total_field_count; i++) {
0751: if (dfgMemberFlags[i]) {
0752: dfgMemberPositions[dfg_num++] = i;
0753: }
0754: if (scoMutableMemberFlags[i]) {
0755: scoMutableMemberPositions[scm_num++] = i;
0756: }
0757: }
0758:
0759: // Initialise any sub-objects
0760: implements MetaData = new ImplementsMetaData[implementations
0761: .size()];
0762: for (int i = 0; i < implementations.size(); i++) {
0763: implements MetaData[i] = (ImplementsMetaData) implementations
0764: .get(i);
0765: implements MetaData[i].initialise();
0766: }
0767: joinMetaData = new JoinMetaData[joins.size()];
0768: for (int i = 0; i < joinMetaData.length; i++) {
0769: joinMetaData[i] = (JoinMetaData) joins.get(i);
0770: joinMetaData[i].initialise();
0771: }
0772: indexMetaData = new IndexMetaData[indexes.size()];
0773: for (int i = 0; i < indexMetaData.length; i++) {
0774: indexMetaData[i] = (IndexMetaData) indexes.get(i);
0775: indexMetaData[i].initialise();
0776: }
0777: foreignKeyMetaData = new ForeignKeyMetaData[foreignKeys.size()];
0778: for (int i = 0; i < foreignKeyMetaData.length; i++) {
0779: foreignKeyMetaData[i] = (ForeignKeyMetaData) foreignKeys
0780: .get(i);
0781: foreignKeyMetaData[i].initialise();
0782: }
0783: uniqueMetaData = new UniqueMetaData[uniqueConstraints.size()];
0784: for (int i = 0; i < uniqueMetaData.length; i++) {
0785: uniqueMetaData[i] = (UniqueMetaData) uniqueConstraints
0786: .get(i);
0787: uniqueMetaData[i].initialise();
0788: }
0789:
0790: fetchGroupMetaData = new FetchGroupMetaData[fetchGroups.size()];
0791: fetchGroupMetaDataByName = new HashMap();
0792: for (int i = 0; i < fetchGroupMetaData.length; i++) {
0793: fetchGroupMetaData[i] = (FetchGroupMetaData) fetchGroups
0794: .get(i);
0795: fetchGroupMetaData[i].initialise();
0796: fetchGroupMetaDataByName.put(fetchGroupMetaData[i]
0797: .getName(), fetchGroupMetaData[i]);
0798: }
0799:
0800: // If using datastore id and user hasn't provided the identity element,
0801: // add a defaulted one (using the superclass if available)
0802: if (identityType == IdentityType.DATASTORE
0803: && identityMetaData == null) {
0804: if (pcSuperclassMetaData != null) {
0805: IdentityMetaData super Imd = pcSuperclassMetaData
0806: .getIdentityMetaData();
0807: identityMetaData = new IdentityMetaData(this , super Imd
0808: .getColumnName(), super Imd.getValueStrategy()
0809: .toString(), super Imd.getSequence());
0810: } else {
0811: identityMetaData = new IdentityMetaData(this , null,
0812: null, null);
0813: }
0814: }
0815:
0816: if (primaryKeyMetaData != null) {
0817: primaryKeyMetaData.initialise();
0818: }
0819: if (versionMetaData != null) {
0820: versionMetaData.initialise();
0821: }
0822: if (identityMetaData != null) {
0823: identityMetaData.initialise();
0824: }
0825: if (inheritanceMetaData != null) {
0826: inheritanceMetaData.initialise();
0827: }
0828:
0829: if (identityType == IdentityType.APPLICATION) {
0830: usesSingleFieldIdentityClass = getMetaDataManager()
0831: .getApiAdapter().isSingleFieldIdentityClass(
0832: getObjectidClass());
0833: }
0834:
0835: // Clear out the collections that we used when loading the MetaData
0836: joins.clear();
0837: joins = null;
0838: fetchGroups.clear();
0839: fetchGroups = null;
0840: foreignKeys.clear();
0841: foreignKeys = null;
0842: indexes.clear();
0843: indexes = null;
0844: uniqueConstraints.clear();
0845: uniqueConstraints = null;
0846: implementations.clear();
0847: implementations = null;
0848: setInitialised();
0849: }
0850:
0851: /**
0852: * Utility to add a defaulted FieldMetaData to the class. Provided as a
0853: * method since then any derived classes can override it (e.g
0854: * JDOConfigClass can create a JDOConfigField)
0855: * @param name name of field
0856: * @return the new FieldMetaData
0857: */
0858: protected AbstractMemberMetaData newDefaultedProperty(String name) {
0859: return new FieldMetaData(this , name);
0860: }
0861:
0862: // ------------------------------ Accessors --------------------------------
0863:
0864: /**
0865: * Accessor for the implements MetaData
0866: * @return Returns the implements MetaData.
0867: */
0868: public final ImplementsMetaData[] getImplementsMetaData() {
0869: return implements MetaData;
0870: }
0871:
0872: /**
0873: * Convenience accessor for whether this class implements a specified interface
0874: * @param interfaceName The name of the interface
0875: * @return Whether it implements the interface
0876: */
0877: public boolean implements Interface(String interfaceName) {
0878: if (implements MetaData != null) {
0879: for (int i = 0; i < implements MetaData.length; i++) {
0880: if (implements MetaData[i].getName().equals(
0881: interfaceName)) {
0882: return true;
0883: }
0884: }
0885: }
0886: return false;
0887: }
0888:
0889: /**
0890: * Whether the PersistenceCapable class is abstract
0891: * @return true if the PersistenceCapable class is abstract
0892: */
0893: public boolean isAbstractPersistenceCapable() {
0894: return isAbstractPersistenceCapable;
0895: }
0896:
0897: // ------------------------------- Mutators --------------------------------
0898:
0899: /**
0900: * Method to add an implements to this class.
0901: * @param implmd Meta-Data for the implements
0902: */
0903: public void addImplements(ImplementsMetaData implmd) {
0904: if (implmd == null) {
0905: return;
0906: }
0907:
0908: if (isInitialised()) {
0909: throw new RuntimeException("Already initialised");
0910: }
0911: implementations.add(implmd);
0912: }
0913:
0914: // ------------------------------ Utilities --------------------------------
0915:
0916: /**
0917: * Returns a string representation of the object.
0918: * This can be used as part of a facility to output a MetaData file.
0919: * @param prefix prefix string
0920: * @param indent indent string
0921: * @return a string representation of the object.
0922: */
0923: public String toString(String prefix, String indent) {
0924: StringBuffer sb = new StringBuffer();
0925: sb.append(prefix).append("<class name=\"" + name + "\"\n");
0926: if (identityType != null) {
0927: sb.append(prefix).append(
0928: " identity-type=\"" + identityType + "\"\n");
0929: }
0930: if (objectidClass != null) {
0931: sb.append(prefix)
0932: .append(
0933: " objectid-class=\"" + objectidClass
0934: + "\"\n");
0935: }
0936: if (!requiresExtent) {
0937: sb.append(prefix)
0938: .append(
0939: " requires-extent=\""
0940: + requiresExtent + "\"");
0941: }
0942: if (embeddedOnly) {
0943: sb.append(prefix).append(
0944: " embedded-only=\"" + embeddedOnly + "\"\n");
0945: }
0946: if (persistenceModifier != null) {
0947: sb.append(prefix).append(
0948: " persistence-modifier=\""
0949: + persistenceModifier + "\"\n");
0950: }
0951: if (catalog != null) {
0952: sb.append(prefix).append(
0953: " catalog=\"" + catalog + "\"\n");
0954: }
0955: if (schema != null) {
0956: sb.append(prefix).append(
0957: " schema=\"" + schema + "\"\n");
0958: }
0959: if (table != null) {
0960: sb.append(prefix)
0961: .append(" table=\"" + table + "\"\n");
0962: }
0963: if (detachable) {
0964: sb.append(prefix).append(
0965: " detachable=\"" + detachable + "\">\n");
0966: }
0967:
0968: // Implements
0969: if (implements MetaData != null) {
0970: for (int i = 0; i < implements MetaData.length; i++) {
0971: sb.append(implements MetaData[i].toString(prefix
0972: + indent, indent));
0973: }
0974: }
0975:
0976: // Identity
0977: if (identityMetaData != null) {
0978: sb.append(identityMetaData
0979: .toString(prefix + indent, indent));
0980: }
0981:
0982: // PrimaryKey
0983: if (primaryKeyMetaData != null) {
0984: sb.append(primaryKeyMetaData.toString(prefix + indent,
0985: indent));
0986: }
0987:
0988: // Inheritance
0989: if (inheritanceMetaData != null) {
0990: sb.append(inheritanceMetaData.toString(prefix + indent,
0991: indent));
0992: }
0993:
0994: // Add Version
0995: if (versionMetaData != null) {
0996: sb
0997: .append(versionMetaData.toString(prefix + indent,
0998: indent));
0999: }
1000:
1001: // Add joins
1002: if (joinMetaData != null) {
1003: for (int i = 0; i < joinMetaData.length; i++) {
1004: sb.append(joinMetaData[i].toString(prefix + indent,
1005: indent));
1006: }
1007: }
1008:
1009: // Add foreign-keys
1010: if (foreignKeyMetaData != null) {
1011: for (int i = 0; i < foreignKeyMetaData.length; i++) {
1012: sb.append(foreignKeyMetaData[i].toString(prefix
1013: + indent, indent));
1014: }
1015: }
1016:
1017: // Add indexes
1018: if (indexMetaData != null) {
1019: for (int i = 0; i < indexMetaData.length; i++) {
1020: sb.append(indexMetaData[i].toString(prefix + indent,
1021: indent));
1022: }
1023: }
1024:
1025: // Add unique constraints
1026: if (uniqueMetaData != null) {
1027: for (int i = 0; i < uniqueMetaData.length; i++) {
1028: sb.append(uniqueMetaData[i].toString(prefix + indent,
1029: indent));
1030: }
1031: }
1032:
1033: // Add fields
1034: if (managedMembers != null) {
1035: for (int i = 0; i < managedMembers.length; i++) {
1036: sb.append(managedMembers[i].toString(prefix + indent,
1037: indent));
1038: }
1039: }
1040:
1041: // Add unmapped columns
1042: if (unmappedColumns != null) {
1043: for (int i = 0; i < unmappedColumns.size(); i++) {
1044: ColumnMetaData col = (ColumnMetaData) unmappedColumns
1045: .get(i);
1046: sb.append(col.toString(prefix + indent, indent));
1047: }
1048: }
1049:
1050: // Add queries
1051: if (queries != null) {
1052: Iterator iter = queries.iterator();
1053: while (iter.hasNext()) {
1054: QueryMetaData q = (QueryMetaData) iter.next();
1055: sb.append(q.toString(prefix + indent, indent));
1056: }
1057: }
1058:
1059: // Add fetch-groups
1060: if (fetchGroupMetaData != null) {
1061: for (int i = 0; i < fetchGroupMetaData.length; i++) {
1062: sb.append(fetchGroupMetaData[i].toString(prefix
1063: + indent, indent));
1064: }
1065: }
1066:
1067: // Add extensions
1068: sb.append(super .toString(prefix + indent, indent));
1069:
1070: sb.append(prefix + "</class>\n");
1071: return sb.toString();
1072: }
1073: }
|