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: 2007 Xuan Baldauf - Implement solution for issue CORE-3272
0017: 2007 Xuan Baldauf - allow users to explictly state that an array whose component type is not PC may still have PC elements. See CORE-3274
0018: ...
0019: **********************************************************************/package org.jpox.metadata;
0020:
0021: import java.io.Serializable;
0022: import java.lang.reflect.Field;
0023: import java.lang.reflect.Method;
0024: import java.lang.reflect.Modifier;
0025: import java.util.ArrayList;
0026: import java.util.HashSet;
0027: import java.util.List;
0028: import java.util.Set;
0029:
0030: import org.jpox.ClassLoaderResolver;
0031: import org.jpox.ClassNameConstants;
0032: import org.jpox.api.ApiAdapter;
0033: import org.jpox.exceptions.ClassNotResolvedException;
0034: import org.jpox.exceptions.JPOXUserException;
0035: import org.jpox.sco.SCOUtils;
0036: import org.jpox.store.DatastoreField;
0037: import org.jpox.util.ClassUtils;
0038: import org.jpox.util.JPOXLogger;
0039: import org.jpox.util.JavaUtils;
0040: import org.jpox.util.StringUtils;
0041:
0042: /**
0043: * Abstract representation of MetaData for a field/property of a class/interface.
0044: * The term "member" is used to represent either a field or a method(property). The term
0045: * property is used to represent the name after cutting off any Java-beans style "get" prefix.
0046: * This class is extended for fields (FieldMetaData) and properties (PropertyMetaData) to provide
0047: * the explicit support for those components.
0048: *
0049: * @version $Revision: 1.20 $
0050: */
0051: public abstract class AbstractMemberMetaData extends MetaData implements
0052: Comparable, ColumnMetaDataContainer {
0053: /** Contains the metadata for column(s). */
0054: protected ColumnMetaData[] columnMetaData;
0055:
0056: /** Meta-Data of any container. */
0057: protected ContainerMetaData container;
0058:
0059: /** EmbeddedMetaData. */
0060: protected EmbeddedMetaData embeddedMetaData;
0061:
0062: /** JoinMetaData. */
0063: protected JoinMetaData joinMetaData;
0064:
0065: /** ElementMetaData. */
0066: protected ElementMetaData elementMetaData;
0067:
0068: /** KeyMetaData. */
0069: protected KeyMetaData keyMetaData;
0070:
0071: /** ValueMetaData. */
0072: protected ValueMetaData valueMetaData;
0073:
0074: /** IndexMetaData. */
0075: protected IndexMetaData indexMetaData;
0076:
0077: /** The indexing value */
0078: protected IndexedValue indexed = null;
0079:
0080: /** UniqueMetaData. */
0081: protected UniqueMetaData uniqueMetaData;
0082:
0083: /** Whether to add a unique constraint. */
0084: protected final boolean uniqueConstraint;
0085:
0086: /** OrderMetaData. */
0087: protected OrderMetaData orderMetaData;
0088:
0089: /** ForeignKeyMetaData. */
0090: protected ForeignKeyMetaData foreignKeyMetaData;
0091:
0092: /** default-fetch-group tag value. */
0093: protected Boolean defaultFetchGroup;
0094:
0095: /** column tag value. */
0096: protected String column;
0097:
0098: /** mapped-by tag value. */
0099: protected String mappedBy;
0100:
0101: /** embedded tag value. */
0102: protected Boolean embedded;
0103:
0104: /** Whether this field contains a reference that should be deleted when deleting this field. */
0105: protected Boolean dependent;
0106:
0107: /** serialized tag value. */
0108: protected Boolean serialized;
0109:
0110: /** Whether to persist this relation when persisting the owning object. */
0111: protected boolean cascadePersist;
0112:
0113: /** Whether to update this relation when updating the owning object. */
0114: protected boolean cascadeUpdate;
0115:
0116: /** Whether to delete this relation when deleting the owning object (JPA). TODO Link this to dependent */
0117: protected boolean cascadeDelete;
0118:
0119: /** Whether to refresh this relation when refreshing the owning object (JPA). */
0120: protected boolean cascadeRefresh;
0121:
0122: /** load-fetch-group value. */
0123: protected String loadFetchGroup;
0124:
0125: /** Default recursion-depth according to proposed final draft spec, [12.7.2]. */
0126: public static final int DEFAULT_RECURSION_DEPTH = 1;
0127:
0128: /** Indicates the recursion-depth is not defined. Use default value. */
0129: public static final int UNDEFINED_RECURSION_DEPTH = 0;
0130:
0131: /** recursion-depth value. */
0132: protected int recursionDepth = UNDEFINED_RECURSION_DEPTH;
0133:
0134: /** Field name. */
0135: protected final String name;
0136:
0137: /** null-value tag value (default is NONE). */
0138: protected NullValue nullValue = NullValue.NONE;
0139:
0140: /** persistence-modifier tag value. */
0141: protected FieldPersistenceModifier persistenceModifier = FieldPersistenceModifier.DEFAULT;
0142:
0143: /** primary key tag value. */
0144: protected Boolean primaryKey;
0145:
0146: /** Table name for this field. */
0147: protected String table;
0148:
0149: /** Catalog for the table specified for this field. */
0150: protected String catalog;
0151:
0152: /** Schema for the table specified for this field. */
0153: protected String schema;
0154:
0155: /**
0156: * The value-strategy attribute specifies the strategy used to generate
0157: * values for the field. This attribute has the same values and meaning as
0158: * the strategy attribute in datastoreidentity.
0159: */
0160: protected IdentityStrategy valueStrategy;
0161:
0162: /** Name of a value generator if the user wants to override the default JPOX generator. */
0163: protected String valueGeneratorName;
0164:
0165: /**
0166: * If the value-strategy is sequence, the sequence attribute specifies the
0167: * name of the sequence to use to automatically generate a value for the field.
0168: */
0169: protected String sequence;
0170:
0171: /**
0172: * Name of the class to which this field really belongs. Will be null if the field belongs
0173: * to the parent ClassMetaData, and will have a value if it is an overriding field.
0174: */
0175: protected String className = null;
0176:
0177: /**
0178: * Specification of the possible type(s) that can be stored in this field. This is for the case where the
0179: * field/property is declared as an interface, or Object and hence can contain derived types. This
0180: * provides the restriction to a particular type.
0181: */
0182: protected String[] fieldTypes;
0183:
0184: /** Field type being represented by this MetaData. */
0185: protected Class type;
0186:
0187: /** Field modifiers */
0188: protected int modifiers;
0189:
0190: /**
0191: * Id of the field in its class (only for fields managed by JDO).
0192: * If the value is -1, the field is NOT managed by JDO or the object hasn't
0193: * been populated.
0194: */
0195: protected int fieldId = -1;
0196:
0197: /** The relation type of this field (1-1, 1-N, M-N, N-1). */
0198: protected int relationType = -1;
0199:
0200: /**
0201: * MetaData for the other end of a relation when this member is a bidirectional relation.
0202: * This may be multiple fields if the FK is shared.
0203: */
0204: protected AbstractMemberMetaData[] relatedMemberMetaData = null;
0205:
0206: /** Temporary flag to signify if the field is ordered. */
0207: protected boolean ordered = false;
0208:
0209: // -------------------------------------------------------------------------
0210: // These fields are only used when the MetaData is read by the parser and
0211: // elements are dynamically added to the other elements. At runtime, "columnMetaData"
0212: // should be used.
0213:
0214: /**
0215: * Columns ColumnMetaData
0216: */
0217: protected List columns = new ArrayList();
0218:
0219: /** Name of the target entity (when used with JPA MetaData on OneToOne, OneToMany etc) */
0220: protected String targetClassName = null;
0221:
0222: /** Wrapper for the ugly JPA "lob" so that when being populated we should make this serialised in some way. */
0223: protected boolean storeInLob = false;
0224:
0225: /** JDO Flags for use in enhancement process - see JDO spec 21.14. */
0226: protected byte jdoFieldFlag;
0227:
0228: /**
0229: * Convenience constructor taking defaults
0230: * @param parent Parent component
0231: * @param name Name of the field
0232: */
0233: public AbstractMemberMetaData(MetaData parent, final String name) {
0234: this (parent, name, null, null, null, null, null, null, null,
0235: null, null, null, null, null, null, null, null, null,
0236: null, null, null, null);
0237: }
0238:
0239: /**
0240: * Convenience constructor to copy the specification from the passed field.
0241: * This is used when we have an overriding field and we make a copy of the baseline
0242: * field as a starting point.
0243: * @param parent The parent
0244: * @param fmd The field to copy
0245: */
0246: public AbstractMemberMetaData(MetaData parent,
0247: AbstractMemberMetaData fmd) {
0248: super (parent);
0249:
0250: // Copy the simple field values
0251: this .name = fmd.name;
0252: this .primaryKey = fmd.primaryKey;
0253: this .defaultFetchGroup = fmd.defaultFetchGroup;
0254: this .column = fmd.column;
0255: this .mappedBy = fmd.mappedBy;
0256: this .dependent = fmd.dependent;
0257: this .embedded = fmd.embedded;
0258: this .serialized = fmd.serialized;
0259: this .cascadePersist = fmd.cascadePersist;
0260: this .cascadeUpdate = fmd.cascadeUpdate;
0261: this .cascadeDelete = fmd.cascadeDelete;
0262: this .cascadeRefresh = fmd.cascadeRefresh;
0263: this .nullValue = fmd.nullValue;
0264: this .persistenceModifier = fmd.persistenceModifier;
0265: this .table = fmd.table;
0266: this .indexed = fmd.indexed;
0267: this .valueStrategy = fmd.valueStrategy;
0268: this .valueGeneratorName = fmd.valueGeneratorName;
0269: this .sequence = fmd.sequence;
0270: this .uniqueConstraint = fmd.uniqueConstraint;
0271: this .loadFetchGroup = fmd.loadFetchGroup;
0272: if (fmd.fieldTypes != null) {
0273: this .fieldTypes = new String[fmd.fieldTypes.length];
0274: for (int i = 0; i < fmd.fieldTypes.length; i++) {
0275: this .fieldTypes[i] = fmd.fieldTypes[i];
0276: }
0277: }
0278:
0279: // Create copies of the object fields
0280: if (fmd.joinMetaData != null) {
0281: this .joinMetaData = new JoinMetaData(this , fmd.joinMetaData);
0282: }
0283: if (fmd.elementMetaData != null) {
0284: this .elementMetaData = new ElementMetaData(this ,
0285: fmd.elementMetaData);
0286: }
0287: if (fmd.keyMetaData != null) {
0288: this .keyMetaData = new KeyMetaData(this , fmd.keyMetaData);
0289: }
0290: if (fmd.valueMetaData != null) {
0291: this .valueMetaData = new ValueMetaData(this ,
0292: fmd.valueMetaData);
0293: }
0294: if (fmd.orderMetaData != null) {
0295: this .orderMetaData = new OrderMetaData(this ,
0296: fmd.orderMetaData);
0297: }
0298: if (fmd.indexMetaData != null) {
0299: this .indexMetaData = new IndexMetaData(fmd.indexMetaData);
0300: }
0301: if (fmd.uniqueMetaData != null) {
0302: this .uniqueMetaData = new UniqueMetaData(fmd.uniqueMetaData);
0303: }
0304: if (fmd.foreignKeyMetaData != null) {
0305: this .foreignKeyMetaData = new ForeignKeyMetaData(
0306: fmd.foreignKeyMetaData);
0307: }
0308: if (fmd.embeddedMetaData != null) {
0309: this .embeddedMetaData = new EmbeddedMetaData(this ,
0310: fmd.embeddedMetaData);
0311: }
0312: if (fmd.container != null) {
0313: if (fmd.container instanceof CollectionMetaData) {
0314: container = new CollectionMetaData(this ,
0315: (CollectionMetaData) fmd.container);
0316: } else if (fmd.container instanceof MapMetaData) {
0317: container = new MapMetaData(this ,
0318: (MapMetaData) fmd.container);
0319: } else if (fmd.container instanceof ArrayMetaData) {
0320: container = new ArrayMetaData(this ,
0321: (ArrayMetaData) fmd.container);
0322: }
0323: }
0324: for (int i = 0; i < fmd.columns.size(); i++) {
0325: addColumn(new ColumnMetaData(this ,
0326: (ColumnMetaData) fmd.columns.get(i)));
0327: }
0328: }
0329:
0330: /**
0331: * Constructor. Saves the MetaData with the specified values. The object is
0332: * then in an "unpopulated" state. It can become "populated" by calling the
0333: * <B>populate()</B> method which compares it against the field it is to
0334: * represent and updates any unset attributes and flags up any errors.
0335: * @param parent parent MetaData instance
0336: * @param name field name
0337: * @param pk attribute primary-key value
0338: * @param modifier attribute persistence-modifier value
0339: * @param defaultFetchGroup attribute default-fetch-group value
0340: * @param nullValue attribute null-value value
0341: * @param embedded attribute embedded value
0342: * @param serialized attribute serialized value
0343: * @param dependent attribute dependent value
0344: * @param mappedBy attribute mapped-by value
0345: * @param column attribute column value
0346: * @param table attribute table value
0347: * @param catalog attribute catalog value
0348: * @param schema attribute schema value
0349: * @param deleteAction attribute delete-action value
0350: * @param indexed Whether this is indexed
0351: * @param unique Apply a unique constraint
0352: * @param recursionDepth The depth of fetch to use when recursing
0353: * @param loadFetchGroup Name of the additional fetch group to use when loading
0354: * @param valueStrategy attribute value-strategy value
0355: * @param sequence attribute sequence value
0356: * @param fieldType Implementation type(s) for field.
0357: */
0358: public AbstractMemberMetaData(MetaData parent, final String name,
0359: final String pk, final String modifier,
0360: final String defaultFetchGroup, final String nullValue,
0361: final String embedded, final String serialized,
0362: final String dependent, final String mappedBy,
0363: final String column, final String table,
0364: final String catalog, final String schema,
0365: final String deleteAction, final String indexed,
0366: final String unique, final String recursionDepth,
0367: final String loadFetchGroup, final String valueStrategy,
0368: final String sequence, final String fieldType) {
0369: super (parent);
0370:
0371: if (name == null) {
0372: throw new JPOXUserException(LOCALISER.msg("044041", "name",
0373: getClassName(true), "field"));
0374: }
0375: if (name.indexOf('.') >= 0) {
0376: // TODO Check if this is a valid className
0377: this .className = name.substring(0, name.lastIndexOf('.'));
0378: this .name = name.substring(name.lastIndexOf('.') + 1);
0379: } else {
0380: this .name = name;
0381: }
0382:
0383: // "primary-key"
0384: if (!StringUtils.isWhitespace(pk)) {
0385: if (pk.equalsIgnoreCase("true")) {
0386: this .primaryKey = Boolean.TRUE;
0387: this .defaultFetchGroup = Boolean.TRUE;
0388: } else {
0389: this .primaryKey = Boolean.FALSE;
0390: }
0391: }
0392:
0393: this .column = (StringUtils.isWhitespace(column) ? null : column);
0394: this .mappedBy = (StringUtils.isWhitespace(mappedBy) ? null
0395: : mappedBy);
0396:
0397: // "dependent"
0398: if (dependent != null) {
0399: if (dependent.equalsIgnoreCase("true")) {
0400: this .dependent = Boolean.TRUE;
0401: } else if (dependent.equalsIgnoreCase("false")) {
0402: this .dependent = Boolean.FALSE;
0403: }
0404: }
0405:
0406: // "embedded"
0407: if (embedded != null) {
0408: if (embedded.equalsIgnoreCase("true")) {
0409: this .embedded = Boolean.TRUE;
0410: } else if (embedded.equalsIgnoreCase("false")) {
0411: this .embedded = Boolean.FALSE;
0412: }
0413: }
0414:
0415: // "serialized"
0416: if (serialized != null) {
0417: if (serialized.equalsIgnoreCase("true")) {
0418: this .serialized = Boolean.TRUE;
0419: } else if (serialized.equalsIgnoreCase("false")) {
0420: this .serialized = Boolean.FALSE;
0421: }
0422: }
0423:
0424: // "null-value"
0425: this .nullValue = NullValue.getNullValue(nullValue);
0426:
0427: // "persistence-modifier"
0428: if (!StringUtils.isWhitespace(modifier)) {
0429: persistenceModifier = FieldPersistenceModifier
0430: .getFieldPersistenceModifier(modifier);
0431: }
0432: if (persistenceModifier == null) {
0433: JPOXLogger.METADATA.error(LOCALISER.msg("044117", name,
0434: getClassName(false), modifier,
0435: "persistence-modifier"));
0436: throw new InvalidMetaDataException(LOCALISER, "044117",
0437: name, getClassName(false), modifier,
0438: "persistence-modifier");
0439: }
0440:
0441: // "default-fetch-group"
0442: if (defaultFetchGroup != null) {
0443: if (defaultFetchGroup.equalsIgnoreCase("true")) {
0444: this .defaultFetchGroup = Boolean.TRUE;
0445: } else if (defaultFetchGroup.equalsIgnoreCase("false")) {
0446: this .defaultFetchGroup = Boolean.FALSE;
0447: }
0448: }
0449:
0450: // "table"
0451: this .table = (StringUtils.isWhitespace(table) ? null : table);
0452: this .catalog = (StringUtils.isWhitespace(catalog) ? null
0453: : catalog);
0454: this .schema = (StringUtils.isWhitespace(schema) ? null : schema);
0455:
0456: if (deleteAction != null) {
0457: foreignKeyMetaData = new ForeignKeyMetaData(null, null,
0458: null, null, deleteAction, null);
0459: }
0460:
0461: this .indexed = IndexedValue.getIndexedValue(indexed);
0462:
0463: if (unique != null && unique.equalsIgnoreCase("true")) {
0464: uniqueConstraint = true;
0465: } else {
0466: uniqueConstraint = false;
0467: }
0468:
0469: // Fetch Group
0470: this .loadFetchGroup = (StringUtils.isWhitespace(loadFetchGroup) ? null
0471: : loadFetchGroup);
0472: if (recursionDepth != null) {
0473: try {
0474: this .recursionDepth = (new Integer(recursionDepth))
0475: .intValue();
0476: } catch (Exception e) {
0477: // Not parseable so set as "undefined"
0478: this .recursionDepth = UNDEFINED_RECURSION_DEPTH;
0479: }
0480: }
0481:
0482: this .valueStrategy = valueStrategy == null ? null
0483: : IdentityStrategy.getIdentityStrategy(valueStrategy);
0484: this .sequence = (StringUtils.isWhitespace(sequence) ? null
0485: : sequence);
0486: if (!StringUtils.isWhitespace(fieldType)) {
0487: // Split the passed value assuming that it is comma-separated
0488: this .fieldTypes = MetaDataUtils.getInstance()
0489: .getValuesForCommaSeparatedAttribute(fieldType);
0490: }
0491:
0492: if (getMetaDataManager() != null) {
0493: // Set defaults for cascading
0494: // If we have no MetaDataManager here then we are likely a field for an Index/Unique etc so dont need this
0495: ApiAdapter apiAdapter = getMetaDataManager()
0496: .getOMFContext().getApiAdapter();
0497: this .cascadePersist = apiAdapter
0498: .getDefaultCascadePersistForField();
0499: this .cascadeUpdate = apiAdapter
0500: .getDefaultCascadeUpdateForField();
0501: this .cascadeDelete = apiAdapter
0502: .getDefaultCascadeDeleteForField();
0503: this .cascadeRefresh = apiAdapter
0504: .getDefaultCascadeRefreshForField();
0505: }
0506: }
0507:
0508: /**
0509: * Method to provide the details of the field being represented by this MetaData hence populating
0510: * certain parts of the MetaData. This is used to firstly provide defaults for attributes that aren't
0511: * specified in the MetaData, and secondly to report any errors with attributes that have been specifed
0512: * that are inconsistent with the field being represented.
0513: * Either a field or a method should be passed in (one or the other) depending on what is being represented
0514: * by this "member".
0515: * @param clr ClassLoaderResolver to use for any class loading
0516: * @param field Field that we are representing (if it's a field)
0517: * @param method Method(property) that we are representing (if it's a method).
0518: * @param primary the primary ClassLoader to use (or null)
0519: */
0520: public synchronized void populate(ClassLoaderResolver clr,
0521: Field field, Method method, ClassLoader primary) {
0522: if (isPopulated() || isInitialised()) {
0523: return;
0524: /*JPOXLogger.METADATA.error(LOCALISER.msg("044107",name,getClassName(false)));
0525: throw new InvalidMetaDataException(LOCALISER,
0526: "044107",
0527: name,getClassName(false));*/
0528: }
0529:
0530: if (field == null && method == null) {
0531: JPOXLogger.METADATA.error(LOCALISER.msg("044106", name,
0532: getClassName(false)));
0533: throw new InvalidMetaDataException(LOCALISER,
0534: "MetaData.Field.PopulateWithNullError", name,
0535: getClassName(false));
0536: }
0537:
0538: // No class loader, so use System
0539: if (clr == null) {
0540: JPOXLogger.METADATA.warn(LOCALISER.msg("044067", name,
0541: getClassName(true)));
0542: clr = getAbstractClassMetaData().getMetaDataManager()
0543: .getOMFContext().getClassLoaderResolver(null);
0544: }
0545:
0546: if (field != null) {
0547: this .type = field.getType();
0548: this .modifiers = field.getModifiers();
0549: } else if (method != null) {
0550: this .type = method.getReturnType();
0551: this .modifiers = method.getModifiers();
0552: }
0553:
0554: if (className != null) {
0555: // If the field is overriding a superclass field, check that it is valid
0556: Class this Class = null;
0557: try {
0558: this Class = clr.classForName(getAbstractClassMetaData()
0559: .getPackageName()
0560: + "." + getAbstractClassMetaData().getName());
0561: } catch (ClassNotResolvedException cnre) {
0562: // Do nothing
0563: }
0564:
0565: Class fieldClass = null;
0566: try {
0567: fieldClass = clr.classForName(className);
0568: } catch (ClassNotResolvedException cnre) {
0569: try {
0570: fieldClass = clr
0571: .classForName(getAbstractClassMetaData()
0572: .getPackageName()
0573: + "." + className);
0574: className = getAbstractClassMetaData()
0575: .getPackageName()
0576: + "." + className;
0577: } catch (ClassNotResolvedException cnre2) {
0578: JPOXLogger.METADATA.error(LOCALISER.msg("044113",
0579: getAbstractClassMetaData().getName(), name,
0580: className));
0581: throw new InvalidMetaDataException(LOCALISER,
0582: "044113", getAbstractClassMetaData()
0583: .getName(), name, className);
0584: }
0585: }
0586: if (fieldClass != null
0587: && !fieldClass.isAssignableFrom(this Class)) {
0588: // TODO We could also check if PersistenceCapable, but won't work when enhancing
0589: JPOXLogger.METADATA.error(LOCALISER.msg("044114",
0590: getAbstractClassMetaData().getName(), name,
0591: className));
0592: throw new InvalidMetaDataException(LOCALISER, "044114",
0593: getAbstractClassMetaData().getName(), name,
0594: className);
0595: }
0596: }
0597:
0598: if (primaryKey == null) {
0599: // Primary key not set by user so initialise it to false
0600: primaryKey = Boolean.FALSE;
0601: }
0602:
0603: // Update "embedded" based on type
0604: if (primaryKey == Boolean.FALSE && embedded == null) {
0605: Class element_type = getType();
0606: if (element_type.isArray()) {
0607: element_type = element_type.getComponentType();
0608: if (getMetaDataManager().getOMFContext()
0609: .getTypeManager().isDefaultEmbeddedType(
0610: element_type)) {
0611: embedded = Boolean.TRUE;
0612: }
0613: } else if (getMetaDataManager().getOMFContext()
0614: .getTypeManager().isDefaultEmbeddedType(
0615: element_type)) {
0616: embedded = Boolean.TRUE;
0617: }
0618: }
0619: if (embedded == null) {
0620: embedded = Boolean.FALSE;
0621: }
0622:
0623: // Update "persistence-modifier" according to type etc
0624: if (FieldPersistenceModifier.DEFAULT
0625: .equals(persistenceModifier)) {
0626: boolean isPcClass = getType().isArray() ? isFieldArrayTypePersistable()
0627: : isFieldTypePersistable();
0628: if (!isPcClass) {
0629: if (getType().isArray()
0630: && getType().getComponentType().isInterface()) {
0631: isPcClass = (getAbstractClassMetaData()
0632: .getMetaDataManager()
0633: .getMetaDataForClassInternal(
0634: getType().getComponentType(), clr) != null);
0635: } else if (getType().isInterface()) {
0636: isPcClass = (getAbstractClassMetaData()
0637: .getMetaDataManager()
0638: .getMetaDataForClassInternal(getType(), clr) != null);
0639: }
0640: }
0641:
0642: persistenceModifier = getMetaDataManager().getOMFContext()
0643: .getTypeManager()
0644: .getDefaultFieldPersistenceModifier(getType(),
0645: modifiers, isPcClass);
0646: }
0647:
0648: // TODO If this field is NONE in superclass, make it NONE here too
0649:
0650: // Check for array types supported by JPOX
0651: if (!persistenceModifier.equals(FieldPersistenceModifier.NONE)
0652: && getType().isArray()
0653: && !getAbstractClassMetaData().getMetaDataManager()
0654: .isEnhancing()) {
0655: // JPOX supports simple arrays, arrays of PCs, and arrays of reference types, and anything serialised
0656: ApiAdapter api = getMetaDataManager().getOMFContext()
0657: .getApiAdapter();
0658: if (!getMetaDataManager().getOMFContext().getTypeManager()
0659: .isSupportedType(getType().getName())
0660: && !api.isPersistable(getType().getComponentType())
0661: && !getType().getComponentType().isInterface()
0662: && !getType().getComponentType().getName().equals(
0663: "java.lang.Object")
0664: && serialized != Boolean.TRUE) {
0665: JPOXLogger.METADATA.warn(LOCALISER.msg("044111", name,
0666: getClassName(false)));
0667: throw new InvalidMetaDataException(LOCALISER, "044111",
0668: name, getClassName(false));
0669: }
0670: }
0671:
0672: // Update "default-fetch-group" according to type
0673: if (defaultFetchGroup == null
0674: && persistenceModifier
0675: .equals(FieldPersistenceModifier.NONE)) {
0676: defaultFetchGroup = Boolean.FALSE;
0677: } else if (defaultFetchGroup == null
0678: && persistenceModifier
0679: .equals(FieldPersistenceModifier.TRANSACTIONAL)) {
0680: defaultFetchGroup = Boolean.FALSE;
0681: } else if (defaultFetchGroup == null) {
0682: defaultFetchGroup = Boolean.FALSE;
0683: if (!primaryKey.equals(Boolean.TRUE)
0684: && getMetaDataManager().getOMFContext()
0685: .getTypeManager().isDefaultFetchGroup(
0686: getType())) {
0687: defaultFetchGroup = Boolean.TRUE;
0688: }
0689: }
0690:
0691: // Field is not specified as "persistent" yet has DFG or primary-key !
0692: if (persistenceModifier
0693: .equals(FieldPersistenceModifier.TRANSACTIONAL)
0694: || persistenceModifier
0695: .equals(FieldPersistenceModifier.NONE)) {
0696: if (defaultFetchGroup == Boolean.TRUE
0697: || primaryKey == Boolean.TRUE) {
0698: throw new InvalidMetaDataException(LOCALISER, "044109",
0699: name, getClassName(true), this .getType()
0700: .getName(), persistenceModifier
0701: .toString());
0702: }
0703: }
0704:
0705: if (storeInLob) {
0706: // Set up the serialized flag according to the field type in line with JPA1
0707: boolean useClob = false;
0708: if (type == String.class
0709: || (type.isArray() && type.getComponentType() == Character.class)
0710: || (type.isArray() && type.getComponentType() == char.class)) {
0711: useClob = true;
0712: if (columns == null || columns.size() == 0) {
0713: // Create a CLOB column. What if the RDBMS doesnt support CLOB ?
0714: addColumn(new ColumnMetaData(this , column, null,
0715: null, "CLOB", null, null, null, null, null,
0716: null, null, null, null));
0717: } else {
0718: ColumnMetaData colmd = (ColumnMetaData) columns
0719: .get(0);
0720: colmd.setJdbcType("CLOB");
0721: }
0722: }
0723: if (!useClob) {
0724: serialized = Boolean.TRUE;
0725: }
0726: }
0727:
0728: if (container == null && type.isArray()) {
0729: // User hasnt specifed <array> but the field is an array so infer it
0730: Class arrayCls = type.getComponentType();
0731: ArrayMetaData arrmd = new ArrayMetaData(this , arrayCls
0732: .getName(), null, null, null);
0733: container = arrmd;
0734: }
0735:
0736: if (container == null && targetClassName != null) {
0737: // User has specified target class name (JPA) so generate the <collection> or <map> accordingly
0738: if (java.util.Collection.class.isAssignableFrom(type)) {
0739: CollectionMetaData colmd = new CollectionMetaData(this ,
0740: targetClassName, null, null, null);
0741: container = colmd;
0742: } else if (java.util.Map.class.isAssignableFrom(type)) {
0743: MapMetaData mapmd = new MapMetaData(this , null, null,
0744: null, null, targetClassName, null, null, null);
0745: container = mapmd;
0746: }
0747: }
0748:
0749: if (JavaUtils.isJRE1_5OrAbove()) {
0750: // User hasnt specified collection/map but using JDK1.5 so construct the <collection>, <map> if using generics
0751: if (java.util.Collection.class.isAssignableFrom(type)) {
0752: String elementType = null;
0753: if (field != null) {
0754: elementType = ClassUtils
0755: .getCollectionElementType(field);
0756: } else {
0757: elementType = ClassUtils
0758: .getCollectionElementType(method);
0759: }
0760: if (elementType != null) {
0761: // Use generics information to infer any missing parts
0762: if (container == null) {
0763: // Create <collection element-type="...">
0764: CollectionMetaData colmd = new CollectionMetaData(
0765: this , elementType, null, null, null);
0766: container = colmd;
0767: } else if (getCollection().element.type == null
0768: || getCollection().element.type
0769: .equals(ClassNameConstants.Object)) {
0770: // element-type not set so update element-type
0771: getCollection().element.type = elementType;
0772: }
0773: }
0774: } else if (java.util.Map.class.isAssignableFrom(type)) {
0775: String keyType = null;
0776: String valueType = null;
0777: if (field != null) {
0778: keyType = ClassUtils.getMapKeyType(field);
0779: } else {
0780: keyType = ClassUtils.getMapKeyType(method);
0781: }
0782: if (field != null) {
0783: valueType = ClassUtils.getMapValueType(field);
0784: } else {
0785: valueType = ClassUtils.getMapValueType(method);
0786: }
0787: if (keyType != null && valueType != null) {
0788: // Use generics information to infer any missing parts
0789: if (container == null) {
0790: // Create <map key-type="..." value-type="...">
0791: MapMetaData mapmd = new MapMetaData(this ,
0792: keyType, null, null, null, valueType,
0793: null, null, null);
0794: container = mapmd;
0795: } else {
0796: if (getMap().key.type == null
0797: || getMap().key.type
0798: .equals(ClassNameConstants.Object)) {
0799: // key-type not set so update key-type
0800: getMap().key.type = keyType;
0801: }
0802: if (getMap().value.type == null
0803: || getMap().value.type
0804: .equals(ClassNameConstants.Object)) {
0805: // value-type not set so update value-type
0806: getMap().value.type = valueType;
0807: }
0808: }
0809: }
0810: }
0811: }
0812:
0813: if (hasCollection() && ordered && orderMetaData == null) {
0814: setOrderMetaData(new OrderMetaData("#PK")); // Special value recognised by OrderMetaData
0815: }
0816:
0817: if (elementMetaData == null && !isSerialized() && !isEmbedded()
0818: && columnMetaData != null) {
0819: if (hasCollection() || hasArray()) {
0820: // Collection/Array with column(s) specified on field but not serialising so move to element
0821: ElementMetaData elemmd = new ElementMetaData(this ,
0822: null, null, null, null, null, null);
0823: setElementMetaData(elemmd);
0824: for (int i = 0; i < columnMetaData.length; i++) {
0825: elemmd.addColumn(columnMetaData[i]);
0826: }
0827: }
0828: }
0829: if (valueMetaData == null && hasMap() && !isEmbedded()
0830: && !isSerialized() && columnMetaData != null) {
0831: // Column specified directly but no value and not serialising field so add a value and apply cols there
0832: ValueMetaData valmd = new ValueMetaData(this , null, null,
0833: null, null, null, null);
0834: setValueMetaData(valmd);
0835: for (int i = 0; i < columnMetaData.length; i++) {
0836: valmd.addColumn(columnMetaData[i]);
0837: }
0838: }
0839:
0840: if (this .container != null && this .dependent != null) {
0841: // Check for invalid dependent field specifications
0842: JPOXLogger.METADATA.error(LOCALISER.msg("044110", name,
0843: getClassName(false), ((ClassMetaData) this .parent)
0844: .getName()));
0845: throw new InvalidMetaDataException(LOCALISER, "044110",
0846: name, getClassName(false),
0847: ((ClassMetaData) this .parent).getName());
0848: }
0849:
0850: if (elementMetaData != null) {
0851: // Populate any element object
0852: elementMetaData.populate(clr, primary);
0853: }
0854: if (keyMetaData != null) {
0855: // Populate any key object
0856: keyMetaData.populate(clr, primary);
0857: }
0858: if (valueMetaData != null) {
0859: // Populate any value object
0860: valueMetaData.populate(clr, primary);
0861: }
0862: if (embeddedMetaData != null) {
0863: // Populate any embedded object
0864: embeddedMetaData.populate(clr, primary);
0865: embedded = Boolean.TRUE;
0866: }
0867:
0868: if (elementMetaData != null && elementMetaData.mappedBy != null
0869: && mappedBy == null) {
0870: // User has specified "mapped-by" on element instead of field so pull it up to this level
0871: // <element mapped-by="..."> is the same as <field mapped-by="..."> for a collection field
0872: mappedBy = elementMetaData.mappedBy;
0873: }
0874:
0875: if (container != null
0876: && persistenceModifier == FieldPersistenceModifier.PERSISTENT) {
0877: // Populate any container
0878: if (container instanceof CollectionMetaData) {
0879: if (cascadeDelete) {
0880: // User has set cascade-delete (JPA) so set the element as dependent
0881: getCollection().element.dependent = Boolean.TRUE;
0882: }
0883: getCollection().populate(clr, primary);
0884: } else if (container instanceof MapMetaData) {
0885: if (cascadeDelete) {
0886: // User has set cascade-delete (JPA) so set the value as dependent
0887: getMap().key.dependent = Boolean.TRUE; // Not really necessary but JPA doesnt provide for PC key fields specification
0888: getMap().value.dependent = Boolean.TRUE;
0889: }
0890: getMap().populate(clr, primary);
0891: } else if (container instanceof ArrayMetaData) {
0892: if (cascadeDelete) {
0893: // User has set cascade-delete (JPA) so set the element as dependent
0894: getArray().element.dependent = Boolean.TRUE;
0895: }
0896: getArray().populate(clr, primary);
0897: }
0898: }
0899: if (isFieldTypePersistable() && cascadeDelete) {
0900: setDependent(true);
0901: }
0902:
0903: if (hasExtension("implementation-classes")) {
0904: // Check the validity of the implementation-classes and qualify them where required.
0905: StringBuffer str = new StringBuffer();
0906: String[] implTypes = getValuesForExtension("implementation-classes");
0907: for (int i = 0; i < implTypes.length; i++) {
0908: String implTypeName = ClassUtils.createFullClassName(
0909: getAbstractClassMetaData().getPackageName(),
0910: implTypes[i]);
0911: if (i > 0) {
0912: str.append(",");
0913: }
0914:
0915: try {
0916: clr.classForName(implTypeName);
0917: str.append(implTypeName);
0918: } catch (ClassNotResolvedException cnre) {
0919: try {
0920: // Maybe the user specified a java.lang class without fully-qualifying it
0921: // This is beyond the scope of the JDO spec which expects java.lang cases to be fully-qualified
0922: String langClassName = ClassUtils
0923: .getJavaLangClassForType(implTypeName);
0924: clr.classForName(langClassName);
0925: str.append(langClassName);
0926: } catch (ClassNotResolvedException cnre2) {
0927: // Implementation type not found
0928: throw new InvalidMetaDataException(LOCALISER,
0929: "044116", name, getClassName(false),
0930: implTypes[i]);
0931: }
0932: }
0933: }
0934: addExtension(JPOX_VENDOR_NAME, "implementation-classes",
0935: str.toString()); // Replace with this new value
0936: }
0937:
0938: // Set up JDO flags for enhancement process
0939: byte serializable = 0;
0940: if (Serializable.class.isAssignableFrom(getType())
0941: || getType().isPrimitive()) {
0942: serializable = PersistenceFlags.SERIALIZABLE;
0943: }
0944:
0945: if (FieldPersistenceModifier.NONE.equals(persistenceModifier)) {
0946: jdoFieldFlag = 0;
0947: } else if (FieldPersistenceModifier.TRANSACTIONAL
0948: .equals(persistenceModifier)
0949: && Modifier.isTransient(modifiers)) {
0950: jdoFieldFlag = (byte) (PersistenceFlags.CHECK_WRITE | serializable);
0951: } else if (primaryKey.booleanValue()) {
0952: jdoFieldFlag = (byte) (PersistenceFlags.MEDIATE_WRITE | serializable);
0953: } else if (defaultFetchGroup.booleanValue()) {
0954: jdoFieldFlag = (byte) (PersistenceFlags.CHECK_READ
0955: | PersistenceFlags.CHECK_WRITE | serializable);
0956: } else if (!defaultFetchGroup.booleanValue()) {
0957: jdoFieldFlag = (byte) (PersistenceFlags.MEDIATE_READ
0958: | PersistenceFlags.MEDIATE_WRITE | serializable);
0959: } else {
0960: jdoFieldFlag = 0;
0961: }
0962:
0963: setPopulated();
0964: }
0965:
0966: /**
0967: * Initialisation method. This should be called AFTER using the populate
0968: * method if you are going to use populate. It creates the internal
0969: * convenience arrays etc needed for normal operation.
0970: */
0971: public synchronized void initialise() {
0972: if (persistenceModifier == FieldPersistenceModifier.NONE) {
0973: setInitialised();
0974: return;
0975: }
0976:
0977: // Cater for user specifying column name, or columns
0978: if (columns.size() == 0 && column != null) {
0979: columnMetaData = new ColumnMetaData[1];
0980: columnMetaData[0] = new ColumnMetaData(this , column);
0981: columnMetaData[0].initialise();
0982: } else if (columns.size() == 1 && column != null) {
0983: // Cater for user specifying <column> and <field column="...">
0984: columnMetaData = new ColumnMetaData[1];
0985: columnMetaData[0] = (ColumnMetaData) columns.get(0);
0986: if (columnMetaData[0].getName() == null) {
0987: columnMetaData[0].setName(column);
0988: }
0989: columnMetaData[0].initialise();
0990: } else {
0991: columnMetaData = new ColumnMetaData[columns.size()];
0992: for (int i = 0; i < columnMetaData.length; i++) {
0993: columnMetaData[i] = (ColumnMetaData) columns.get(i);
0994: columnMetaData[i].initialise();
0995: }
0996: }
0997:
0998: // Initialise all sub-objects
0999: if (container != null) {
1000: container.initialise();
1001: }
1002: if (embeddedMetaData != null) {
1003: embeddedMetaData.initialise();
1004: }
1005: if (joinMetaData != null) {
1006: joinMetaData.initialise();
1007: }
1008: if (elementMetaData != null) {
1009: elementMetaData.initialise();
1010: }
1011: if (keyMetaData != null) {
1012: keyMetaData.initialise();
1013: }
1014: if (valueMetaData != null) {
1015: valueMetaData.initialise();
1016: }
1017:
1018: // Interpret the "indexed" value to create our IndexMetaData where it wasn't specified that way
1019: if (indexMetaData == null && columnMetaData != null
1020: && indexed != null && indexed != IndexedValue.FALSE) {
1021: indexMetaData = new IndexMetaData(null, null,
1022: (indexed == IndexedValue.UNIQUE) ? "true" : "false");
1023: for (int i = 0; i < columnMetaData.length; i++) {
1024: indexMetaData.addColumn(columnMetaData[i]);
1025: }
1026: }
1027: if (indexMetaData != null) {
1028: indexMetaData.initialise();
1029: }
1030: if (uniqueMetaData == null && uniqueConstraint) {
1031: uniqueMetaData = new UniqueMetaData(null, column, null);
1032: for (int i = 0; i < columnMetaData.length; i++) {
1033: uniqueMetaData.addColumn(columnMetaData[i]);
1034: }
1035: }
1036: if (uniqueMetaData != null) {
1037: uniqueMetaData.initialise();
1038: }
1039:
1040: if (foreignKeyMetaData != null) {
1041: foreignKeyMetaData.initialise();
1042: }
1043: if (orderMetaData != null) {
1044: orderMetaData.initialise();
1045: }
1046:
1047: if ((hasCollection() && SCOUtils
1048: .collectionHasSerialisedElements(this ))
1049: || (hasMap() && SCOUtils
1050: .mapHasSerialisedKeysAndValues(this ))
1051: || (hasArray() && SCOUtils
1052: .arrayIsStoredInSingleColumn(this ))) {
1053: boolean setSerialised = true;
1054: if (hasArray()
1055: && MetaDataUtils.getInstance()
1056: .arrayStorableAsByteArrayInSingleColumn(
1057: this )) {
1058: // These can be streamed as byte arrays
1059: setSerialised = false;
1060: }
1061:
1062: // Collection/Array with serialised elements or Map with serialised keys and values so set as serialised for later
1063: if (setSerialised) {
1064: serialized = Boolean.TRUE;
1065: }
1066: }
1067:
1068: if (hasExtension("cascade-persist")) {
1069: // JDO doesnt have a metadata attribute for this so we use an extension
1070: String cascadeValue = getValueForExtension("cascade-persist");
1071: if (cascadeValue.equalsIgnoreCase("true")) {
1072: cascadePersist = true;
1073: } else if (cascadeValue.equalsIgnoreCase("false")) {
1074: cascadePersist = false;
1075: }
1076: }
1077: if (hasExtension("cascade-update")) {
1078: // JDO doesnt have a metadata attribute for this so we use an extension
1079: String cascadeValue = getValueForExtension("cascade-update");
1080: if (cascadeValue.equalsIgnoreCase("true")) {
1081: cascadeUpdate = true;
1082: } else if (cascadeValue.equalsIgnoreCase("false")) {
1083: cascadeUpdate = false;
1084: }
1085: }
1086: if (hasExtension("cascade-refresh")) {
1087: // JDO doesnt have a metadata attribute for this so we use an extension
1088: String cascadeValue = getValueForExtension("cascade-refresh");
1089: if (cascadeValue.equalsIgnoreCase("true")) {
1090: cascadeRefresh = true;
1091: } else if (cascadeValue.equalsIgnoreCase("false")) {
1092: cascadeRefresh = false;
1093: }
1094: }
1095:
1096: setInitialised();
1097: }
1098:
1099: /**
1100: * Utility to return if this field is persistable.
1101: * Not valid for use by the enhancer. Must be overridden for that mode.
1102: * @return Whether the field type is persistable.
1103: */
1104: public boolean isFieldTypePersistable() {
1105: if (getAbstractClassMetaData().getMetaDataManager()
1106: .isEnhancing()) {
1107: // Enhancing so return if we have MetaData that is persistable
1108: AbstractClassMetaData cmd = getAbstractClassMetaData()
1109: .getMetaDataManager().readMetaDataForClass(
1110: type.getName());
1111: if (cmd != null
1112: && cmd instanceof ClassMetaData
1113: && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
1114: return true;
1115: }
1116: }
1117: return getMetaDataManager().getOMFContext().getApiAdapter()
1118: .isPersistable(type);
1119: }
1120:
1121: /**
1122: * Utility to return if this array field has elements that are Persistable.
1123: * Not valid for use by the enhancer. Must be overridden for that mode.
1124: * If the field is not an array will return false.
1125: * @return Whether the field type is persistable.
1126: */
1127: public boolean isFieldArrayTypePersistable() {
1128: if (!type.isArray()) {
1129: return false;
1130: }
1131: if (getAbstractClassMetaData().getMetaDataManager()
1132: .isEnhancing()) {
1133: // Enhancing so return if we have MetaData that is PersistenceCapable
1134: AbstractClassMetaData cmd = getAbstractClassMetaData()
1135: .getMetaDataManager().readMetaDataForClass(
1136: type.getComponentType().getName());
1137: if (cmd != null
1138: && cmd instanceof ClassMetaData
1139: && cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
1140: return true;
1141: }
1142: }
1143: return getMetaDataManager().getOMFContext().getApiAdapter()
1144: .isPersistable(type.getComponentType());
1145: }
1146:
1147: /**
1148: * Convenience method to return if this field/property is static.
1149: * When the object is not "populated" always returns false;
1150: * @return Whether the field/property is static
1151: */
1152: public boolean isStatic() {
1153: if (!isPopulated()) {
1154: return false;
1155: }
1156: return Modifier.isStatic(modifiers);
1157: }
1158:
1159: /**
1160: * Convenience method to return if this field/property is final.
1161: * When the object is not "populated" always returns false;
1162: * @return Whether the field is field/property
1163: */
1164: public boolean isFinal() {
1165: if (!isPopulated()) {
1166: return false;
1167: }
1168: return Modifier.isFinal(modifiers);
1169: }
1170:
1171: /**
1172: * Convenience method to return if this field/property is transient.
1173: * When the object is not "populated" always returns false;
1174: * @return Whether the field/property is transient
1175: */
1176: public boolean isTransient() {
1177: if (!isPopulated()) {
1178: return false;
1179: }
1180: return Modifier.isTransient(modifiers);
1181: }
1182:
1183: /**
1184: * Convenience method to return if this field/property is public.
1185: * When the object is not "populated" always returns false;
1186: * @return Whether the field/property is public
1187: */
1188: public boolean isPublic() {
1189: if (!isPopulated()) {
1190: return false;
1191: }
1192: return Modifier.isPublic(modifiers);
1193: }
1194:
1195: /**
1196: * Convenience method to return if this field/property is protected.
1197: * When the object is not "populated" always returns false;
1198: * @return Whether the field/property is protected
1199: */
1200: public boolean isProtected() {
1201: if (!isPopulated()) {
1202: return false;
1203: }
1204: return Modifier.isProtected(modifiers);
1205: }
1206:
1207: /**
1208: * Convenience method to return if this field/property is private.
1209: * When the object is not "populated" always returns false;
1210: * @return Whether the field/property is private
1211: */
1212: public boolean isPrivate() {
1213: if (!isPopulated()) {
1214: return false;
1215: }
1216: return Modifier.isPrivate(modifiers);
1217: }
1218:
1219: /**
1220: * Convenience method to return if this field represents an abstract property.
1221: * @return Whether the property is abstract
1222: */
1223: public boolean isAbstract() {
1224: if (!isPopulated()) {
1225: return false;
1226: }
1227: return Modifier.isAbstract(modifiers);
1228: }
1229:
1230: // ------------------------------- Accessors -------------------------------
1231:
1232: /**
1233: * The value-strategy attribute specifies the strategy used to generate
1234: * values for the field. This attribute has the same values and meaning as
1235: * the strategy attribute in datastoreidentity.
1236: * @return the value strategy
1237: */
1238: public IdentityStrategy getValueStrategy() {
1239: return valueStrategy;
1240: }
1241:
1242: /**
1243: * Name of a (user-provided) value generator to override the default JPOX generator for this strategy.
1244: * @return Name of user provided value generator
1245: */
1246: public String getValueGeneratorName() {
1247: return valueGeneratorName;
1248: }
1249:
1250: /**
1251: * If the value-strategy is sequence, the sequence attribute specifies the
1252: * name of the sequence to use to automatically generate a value for the
1253: * field.
1254: * @return the sequence name
1255: */
1256: public String getSequence() {
1257: return sequence;
1258: }
1259:
1260: /**
1261: * Accessor for the (additional) fetch group for loading this field
1262: * @return Returns the load fetch group
1263: */
1264: public String getLoadFetchGroup() {
1265: return loadFetchGroup;
1266: }
1267:
1268: /**
1269: * Convenience method to set the load fetch group if required after construction.
1270: * @param loadFetchGroup Name of the load fetch group
1271: */
1272: public void setLoadFetchGroup(String loadFetchGroup) {
1273: this .loadFetchGroup = loadFetchGroup;
1274: }
1275:
1276: /**
1277: * Accessor for the depth of the fetch when recursing
1278: * @return Returns the depth of the fetch when recursing
1279: */
1280: public int getRecursionDepth() {
1281: return recursionDepth;
1282: }
1283:
1284: /**
1285: * Convenience method to navigate back through the parents to find the overall
1286: * ClassMetaData handling this object. This is to cater specifically for nested
1287: * embedded fields where you can nest object several levels deep.
1288: * @param metadata The metadata to check
1289: * @return The overall class metadata for this element
1290: */
1291: protected MetaData getOverallParentClassMetaData(MetaData metadata) {
1292: if (metadata == null) {
1293: return null;
1294: } else if (metadata instanceof AbstractClassMetaData) {
1295: return metadata;
1296: } else {
1297: return getOverallParentClassMetaData(metadata.getParent());
1298: }
1299: }
1300:
1301: /**
1302: * Convenience accessor for the MetaData of the parent class.
1303: * @return Returns the MetaData of the parent class.
1304: */
1305: public AbstractClassMetaData getAbstractClassMetaData() {
1306: // TODO Consider replacing this method with the getOverallParentClassMetaData above since its generalised
1307: if (parent == null) {
1308: return null;
1309: } else if (parent instanceof AbstractClassMetaData) {
1310: return (AbstractClassMetaData) parent;
1311: } else if (parent instanceof EmbeddedMetaData) {
1312: // <embedded> is contained in a <field>, <element>, <key>, <value>
1313: // but could be multiple levels deep so adopt a generic strategy for finding the parent class
1314: MetaData parentMd = ((EmbeddedMetaData) parent).getParent();
1315: return (AbstractClassMetaData) getOverallParentClassMetaData(parentMd
1316: .getParent());
1317: }
1318: return null;
1319: }
1320:
1321: /**
1322: * Accessor for orderMetaData
1323: * @return Returns the orderMetaData.
1324: */
1325: public final OrderMetaData getOrderMetaData() {
1326: return orderMetaData;
1327: }
1328:
1329: /**
1330: * Accessor for the field name
1331: * @return field name
1332: */
1333: public String getName() {
1334: return name;
1335: }
1336:
1337: /**
1338: * Accessor for the full field name. This prepends the class name.
1339: * @return full field name.
1340: */
1341: public String getFullFieldName() {
1342: if (className != null) {
1343: return className + "." + name;
1344: }
1345: return getClassName(true) + "." + name;
1346: }
1347:
1348: /**
1349: * Accessor for whether the field is for a superclass, and not for this class.
1350: * @return Whether the field belongs to a superclass
1351: */
1352: public boolean fieldBelongsToClass() {
1353: return (className == null);
1354: }
1355:
1356: /**
1357: * Accessor for the fully-qualified class name owning this field.
1358: * @return The class name
1359: */
1360: public String getClassName() {
1361: return getClassName(true);
1362: }
1363:
1364: /**
1365: * Convenience method so that ClassMetaData can update the name of the superclass
1366: * to which this field belongs.
1367: * @param className Name of the class
1368: */
1369: void setClassName(String className) {
1370: this .className = className;
1371: }
1372:
1373: /**
1374: * Convenience to return the class name that this a field of.
1375: * @param fully_qualified Whether the name should be fully qualified.
1376: * @return Class name
1377: */
1378: public String getClassName(boolean fully_qualified) {
1379: if (className != null) {
1380: return className;
1381: }
1382:
1383: if (parent == null) {
1384: return null;
1385: } else if (parent instanceof AbstractClassMetaData) {
1386: AbstractClassMetaData cmd = (AbstractClassMetaData) parent;
1387: if (fully_qualified) {
1388: return cmd.getFullClassName();
1389: } else {
1390: return cmd.getName();
1391: }
1392: } else if (parent instanceof EmbeddedMetaData) {
1393: // <embedded> is contained in a <field>, <element>, <key>, <value>
1394: MetaData parentMd = ((EmbeddedMetaData) parent).getParent();
1395: if (parentMd instanceof AbstractMemberMetaData) {
1396: return ((AbstractMemberMetaData) parentMd)
1397: .getTypeName();
1398: } else if (parentMd instanceof ElementMetaData) {
1399: AbstractMemberMetaData fmd = (AbstractMemberMetaData) ((ElementMetaData) parentMd)
1400: .getParent();
1401: return fmd.getCollection().getElementType();
1402: } else if (parentMd instanceof KeyMetaData) {
1403: AbstractMemberMetaData fmd = (AbstractMemberMetaData) ((KeyMetaData) parentMd)
1404: .getParent();
1405: return fmd.getMap().getKeyType();
1406: } else if (parentMd instanceof ValueMetaData) {
1407: AbstractMemberMetaData fmd = (AbstractMemberMetaData) ((ValueMetaData) parentMd)
1408: .getParent();
1409: return fmd.getMap().getValueType();
1410: } else {
1411: // Should be impossible
1412: return null;
1413: }
1414: } else if (parent instanceof UniqueMetaData) {
1415: MetaData grandparent = ((UniqueMetaData) parent)
1416: .getParent();
1417: if (grandparent instanceof AbstractClassMetaData) {
1418: return ((AbstractClassMetaData) grandparent)
1419: .getFullClassName();
1420: }
1421: // TODO Cater for other parent options
1422: }
1423: return null;
1424: }
1425:
1426: /**
1427: * Accessor for the null-value tag
1428: * @return null-value tag
1429: */
1430: public NullValue getNullValue() {
1431: return nullValue;
1432: }
1433:
1434: /**
1435: * Accessor for the persistence-modifier tag value
1436: * @return persistence-modifier tag value
1437: */
1438: public FieldPersistenceModifier getPersistenceModifier() {
1439: return persistenceModifier;
1440: }
1441:
1442: /**
1443: * Convenience method to mark this field as not-persistent.
1444: */
1445: public void setNotPersistent() {
1446: persistenceModifier = FieldPersistenceModifier.NONE;
1447: }
1448:
1449: /**
1450: * Convenience method to mark this field as transactional.
1451: */
1452: public void setTransactional() {
1453: persistenceModifier = FieldPersistenceModifier.TRANSACTIONAL;
1454: }
1455:
1456: /**
1457: * Accessor for the default-fetch-group tag value
1458: * @return default-fetch-group tag value
1459: */
1460: public boolean isDefaultFetchGroup() {
1461: if (defaultFetchGroup == null) {
1462: return false;
1463: } else {
1464: return defaultFetchGroup.booleanValue();
1465: }
1466: }
1467:
1468: /**
1469: * Convenience method to set the DFG if needing setting after construction.
1470: * @param dfg DFG string value
1471: */
1472: public void setDefaultFetchGroup(boolean dfg) {
1473: this .defaultFetchGroup = new Boolean(dfg);
1474: }
1475:
1476: /**
1477: * Accessor for the dependent attribute indicates that the field contains a
1478: * reference that is to be deleted from the datastore if the referring
1479: * instance in which the field is declared is deleted, or if the referring
1480: * field is nullified.
1481: *
1482: * @return dependent tag value
1483: */
1484: public boolean isDependent() {
1485: if (dependent == null) {
1486: return false;
1487: } else {
1488: return dependent.booleanValue();
1489: }
1490: }
1491:
1492: /**
1493: * Accessor for the embedded tag value.
1494: * This value is a hint only to the implementation so will not mean
1495: * that the type is certainly embedded.
1496: * @return embedded tag value
1497: */
1498: public boolean isEmbedded() {
1499: if (embedded == null) {
1500: return false;
1501: } else {
1502: return embedded.booleanValue();
1503: }
1504: }
1505:
1506: /**
1507: * Accessor for the serialized tag value
1508: * @return serialized tag value
1509: */
1510: public boolean isSerialized() {
1511: if (serialized == null) {
1512: return false;
1513: } else {
1514: return serialized.booleanValue();
1515: }
1516: }
1517:
1518: /**
1519: * Convenience method to mark this field to be stored serialised.
1520: */
1521: public void setSerialised() {
1522: serialized = Boolean.TRUE;
1523: }
1524:
1525: /**
1526: * Accessor for the whether this field should be cascaded at persist
1527: * @return Whether to cascade at persist
1528: */
1529: public boolean isCascadePersist() {
1530: return cascadePersist;
1531: }
1532:
1533: /**
1534: * Accessor for the whether this field should be cascaded at update
1535: * @return Whether to cascade at update
1536: */
1537: public boolean isCascadeUpdate() {
1538: return cascadeUpdate;
1539: }
1540:
1541: /**
1542: * Accessor for the whether this field should be cascaded at delete
1543: * @return Whether to cascade at delete
1544: */
1545: public boolean isCascadeDelete() {
1546: return cascadeDelete;
1547: }
1548:
1549: /**
1550: * Accessor for the whether this field should be cascaded at refresh
1551: * @return Whether to cascade at refresh
1552: */
1553: public boolean isCascadeRefresh() {
1554: return cascadeRefresh;
1555: }
1556:
1557: /**
1558: * Accessor for the primary-key tag value.
1559: * @return primary-key tag value.
1560: */
1561: public boolean isPrimaryKey() {
1562: if (primaryKey == null) {
1563: return false;
1564: } else {
1565: return primaryKey.booleanValue();
1566: }
1567: }
1568:
1569: /**
1570: * Convenience method to make this field (part of) the primary key.
1571: */
1572: public void setPrimaryKey() {
1573: primaryKey = Boolean.TRUE;
1574: this .defaultFetchGroup = Boolean.TRUE;
1575: }
1576:
1577: /**
1578: * Whether this uses getter/setter accessors (Property) or
1579: * used field based access (Field)
1580: * @return true if this is a property
1581: */
1582: public abstract boolean isProperty();
1583:
1584: /**
1585: * Accessor for the table name
1586: * @return table name
1587: */
1588: public String getTable() {
1589: return table;
1590: }
1591:
1592: /**
1593: * Accessor for the catalog name
1594: * @return catalog name
1595: */
1596: public String getCatalog() {
1597: return catalog;
1598: }
1599:
1600: /**
1601: * Accessor for the schema name
1602: * @return schema name
1603: */
1604: public String getSchema() {
1605: return schema;
1606: }
1607:
1608: /**
1609: * Accessor for the field id.
1610: * Not set when the field is an overriding field.
1611: * @return field id
1612: */
1613: public int getFieldId() {
1614: return fieldId;
1615: }
1616:
1617: /**
1618: * Accessor for the implementation type(s) that can be stored in this field when it is a reference type.
1619: * @return Returns the implementation type(s) for the field.
1620: */
1621: public final String[] getFieldTypes() {
1622: return fieldTypes;
1623: }
1624:
1625: /**
1626: * Accessor for the field id
1627: * @return field id
1628: */
1629: public int getAbsoluteFieldNumber() {
1630: if (className == null) {
1631: // Normal field, parented by its true class
1632: return fieldId
1633: + getAbstractClassMetaData()
1634: .getNoOfInheritedManagedMembers();
1635: } else {
1636: // Overriding field, parented by a foster class
1637: return getAbstractClassMetaData()
1638: .getAbsolutePositionOfMember(name);
1639: }
1640: }
1641:
1642: /**
1643: * Accessor for the field type
1644: * @return Reflection field type
1645: */
1646: public Class getType() {
1647: return type;
1648: }
1649:
1650: /**
1651: * Accessor for the field type name
1652: * @return Reflection field type name
1653: */
1654: public String getTypeName() {
1655: if (type == null) {
1656: return null;
1657: }
1658: return type.getName();
1659: }
1660:
1661: /**
1662: * Accessor for the container for this field.
1663: * @return The MetaData of the container for this field.
1664: **/
1665: public ContainerMetaData getContainer() {
1666: return container;
1667: }
1668:
1669: /**
1670: * Accessor for an array container for this field. Returns null if no array
1671: * attached.
1672: * @return The MetaData of the container for this field if an array
1673: **/
1674: public ArrayMetaData getArray() {
1675: if (container != null && container instanceof ArrayMetaData) {
1676: return (ArrayMetaData) container;
1677: }
1678: return null;
1679: }
1680:
1681: /**
1682: * Accessor for a collection container for this field. Returns null if no
1683: * collection attached.
1684: * @return The MetaData of the container for this field if a Collection.
1685: **/
1686: public CollectionMetaData getCollection() {
1687: if (container != null
1688: && container instanceof CollectionMetaData) {
1689: return (CollectionMetaData) container;
1690: }
1691: return null;
1692: }
1693:
1694: /**
1695: * Accessor for a map container for this field. Returns null if no map
1696: * attached.
1697: * @return The MetaData of the container for this field if a Map.
1698: **/
1699: public MapMetaData getMap() {
1700: if (container != null && container instanceof MapMetaData) {
1701: return (MapMetaData) container;
1702: }
1703: return null;
1704: }
1705:
1706: /**
1707: * Accessor for the column
1708: * @return Returns the column.
1709: */
1710: public final String getColumn() {
1711: return column;
1712: }
1713:
1714: /**
1715: * Accessor for mappedBy
1716: * @return Returns the mappedBy.
1717: */
1718: public final String getMappedBy() {
1719: return mappedBy;
1720: }
1721:
1722: /**
1723: * Acessor for the columns
1724: * @return Returns the columnMetaData.
1725: */
1726: public final ColumnMetaData[] getColumnMetaData() {
1727: return columnMetaData;
1728: }
1729:
1730: /**
1731: * Accessor for elementMetaData
1732: * @return Returns the elementMetaData.
1733: */
1734: public final ElementMetaData getElementMetaData() {
1735: return elementMetaData;
1736: }
1737:
1738: /**
1739: * Accessor for keyMetaData
1740: * @return Returns the keyMetaData.
1741: */
1742: public final KeyMetaData getKeyMetaData() {
1743: return keyMetaData;
1744: }
1745:
1746: /**
1747: * Accessor for valueMetaData
1748: * @return Returns the valueMetaData.
1749: */
1750: public final ValueMetaData getValueMetaData() {
1751: return valueMetaData;
1752: }
1753:
1754: /**
1755: * Accessor for embeddedMetaData
1756: * @return Returns the embeddedMetaData.
1757: */
1758: public final EmbeddedMetaData getEmbeddedMetaData() {
1759: return embeddedMetaData;
1760: }
1761:
1762: /**
1763: * Accessor for foreignKeyMetaData
1764: * @return Returns the foreignKeyMetaData.
1765: */
1766: public final ForeignKeyMetaData getForeignKeyMetaData() {
1767: return foreignKeyMetaData;
1768: }
1769:
1770: /**
1771: * Accessor for indexMetaData
1772: * @return Returns the indexMetaData.
1773: */
1774: public final IndexMetaData getIndexMetaData() {
1775: return indexMetaData;
1776: }
1777:
1778: /**
1779: * Accessor for uniqueMetaData
1780: * @return Returns the uniqueMetaData.
1781: */
1782: public final UniqueMetaData getUniqueMetaData() {
1783: return uniqueMetaData;
1784: }
1785:
1786: /**
1787: * Accessor for joinMetaData
1788: * @return Returns the joinMetaData.
1789: */
1790: public final JoinMetaData getJoinMetaData() {
1791: return joinMetaData;
1792: }
1793:
1794: /**
1795: * Add a new ColumnMetaData element
1796: * @param colmd the ColumnMetaData to add
1797: */
1798: public void addColumn(ColumnMetaData colmd) {
1799: columns.add(colmd);
1800: colmd.parent = this ;
1801: columnMetaData = new ColumnMetaData[columns.size()];
1802: for (int i = 0; i < columnMetaData.length; i++) {
1803: columnMetaData[i] = (ColumnMetaData) columns.get(i);
1804: }
1805: }
1806:
1807: /**
1808: * Accessor for whether the field has a container.
1809: * @return Whether it represents a container.
1810: */
1811: public boolean hasContainer() {
1812: return (container != null);
1813: }
1814:
1815: /**
1816: * Accessor for whether the field has an array
1817: * @return return true if has array
1818: */
1819: public boolean hasArray() {
1820: if (container == null) {
1821: return false;
1822: }
1823: return (container instanceof ArrayMetaData);
1824: }
1825:
1826: /**
1827: * Accessor for whether the field has a collection
1828: * @return return true if has collection
1829: */
1830: public boolean hasCollection() {
1831: if (container == null) {
1832: return false;
1833: }
1834: return (container instanceof CollectionMetaData);
1835: }
1836:
1837: /**
1838: * Accessor for whether the field has a map.
1839: * @return return true if has map
1840: */
1841: public boolean hasMap() {
1842: if (container == null) {
1843: return false;
1844: }
1845: return (container instanceof MapMetaData);
1846: }
1847:
1848: /**
1849: * Accessor for the JDO field flag
1850: * @return JDO Field flag (for enhancing)
1851: */
1852: public byte getJdoFieldFlag() {
1853: return jdoFieldFlag;
1854: }
1855:
1856: /**
1857: * Accessor for whether the field is to be managed by JPOX.
1858: * @return Whether it is JPOX managed
1859: */
1860: public boolean isJdoField() {
1861: if (isPopulated()) {
1862: if (isStatic() || isFinal()) {
1863: return false;
1864: }
1865: }
1866:
1867: if (persistenceModifier == null) {
1868: return false;
1869: } else if (persistenceModifier
1870: .equals(FieldPersistenceModifier.NONE)) {
1871: return false;
1872: }
1873: return true;
1874: }
1875:
1876: // ------------------------------ Mutators ---------------------------------
1877:
1878: /**
1879: * Mutator for whether the collection stored in this field is ordered.
1880: * Only valid until the metadata is initialised.
1881: */
1882: public void setOrdered() {
1883: ordered = true;
1884: }
1885:
1886: /**
1887: * Mutator for the target class name. Only valid until the metadata is initialised.
1888: * @param target Target class name
1889: */
1890: public void setTargetClassName(String target) {
1891: if (!StringUtils.isWhitespace(target)) {
1892: this .targetClassName = target;
1893: }
1894: }
1895:
1896: /**
1897: * Mutator for whetehr to store as a "lob".
1898: */
1899: public void setStoreInLob() {
1900: storeInLob = true;
1901: }
1902:
1903: /**
1904: * Mutator for the cascading of persist operations on this field.
1905: * @param cascade Whether to cascade at persist
1906: */
1907: public void setCascadePersist(boolean cascade) {
1908: this .cascadePersist = cascade;
1909: }
1910:
1911: /**
1912: * Mutator for the cascading of update operations on this field.
1913: * @param cascade Whether to cascade at update
1914: */
1915: public void setCascadeUpdate(boolean cascade) {
1916: this .cascadeUpdate = cascade;
1917: }
1918:
1919: /**
1920: * Mutator for the cascading of delete operations on this field.
1921: * @param cascade Whether to cascade at delete
1922: */
1923: public void setCascadeDelete(boolean cascade) {
1924: this .cascadeDelete = cascade;
1925: }
1926:
1927: /**
1928: * Mutator for the cascading of refresh operations on this field.
1929: * @param cascade Whether to cascade at refresh
1930: */
1931: public void setCascadeRefresh(boolean cascade) {
1932: this .cascadeRefresh = cascade;
1933: }
1934:
1935: /**
1936: * The value-strategy attribute specifies the strategy used to generate
1937: * values for the field. This attribute has the same values and meaning as
1938: * the strategy attribute in datastoreidentity.
1939: * @param valueStrategy the value strategy
1940: */
1941: public void setValueStrategy(IdentityStrategy valueStrategy) {
1942: this .valueStrategy = valueStrategy;
1943: }
1944:
1945: /**
1946: * Mutator for the name of the value generator to use for this strategy.
1947: * @param generator Name of value generator
1948: */
1949: public void setValueGeneratorName(String generator) {
1950: if (StringUtils.isWhitespace(generator)) {
1951: valueGeneratorName = null;
1952: } else {
1953: this .valueGeneratorName = generator;
1954: }
1955: }
1956:
1957: /**
1958: * If the value-strategy is sequence, the sequence attribute specifies the
1959: * name of the sequence to use to automatically generate a value for the field.
1960: * @param sequence the sequence name
1961: */
1962: public void setSequence(String sequence) {
1963: this .sequence = sequence;
1964: }
1965:
1966: /**
1967: * Mutator for dependent attribute.
1968: * @param dependent Whether it is dependent.
1969: */
1970: public void setDependent(boolean dependent) {
1971: this .dependent = new Boolean(dependent);
1972: }
1973:
1974: /**
1975: * Mutator for mappedBy
1976: * @param mappedby The mapped-by field.
1977: */
1978: public void setMappedBy(String mappedby) {
1979: mappedBy = mappedby;
1980: }
1981:
1982: /**
1983: * Method to set the container for this field (if this field represents a
1984: * container (collection, map, array).
1985: * @param conmd The MetaData of the container for this field.
1986: **/
1987: public void setContainer(ContainerMetaData conmd) {
1988: container = conmd;
1989: container.parent = this ;
1990: }
1991:
1992: /**
1993: * Mutator for the element MetaData
1994: * @param elementMetaData The elementMetaData to set.
1995: */
1996: public final void setElementMetaData(ElementMetaData elementMetaData) {
1997: this .elementMetaData = elementMetaData;
1998: this .elementMetaData.parent = this ;
1999: }
2000:
2001: /**
2002: * Mutator for the key MetaData
2003: * @param keyMetaData The keyMetaData to set.
2004: */
2005: public final void setKeyMetaData(KeyMetaData keyMetaData) {
2006: this .keyMetaData = keyMetaData;
2007: this .keyMetaData.parent = this ;
2008: }
2009:
2010: /**
2011: * Mutator for the order MetaData
2012: * @param orderMetaData The orderMetaData to set.
2013: */
2014: public final void setOrderMetaData(OrderMetaData orderMetaData) {
2015: this .orderMetaData = orderMetaData;
2016: this .orderMetaData.parent = this ;
2017: }
2018:
2019: /**
2020: * Mutator for the value MetaData
2021: * @param valueMetaData The valueMetaData to set.
2022: */
2023: public final void setValueMetaData(ValueMetaData valueMetaData) {
2024: this .valueMetaData = valueMetaData;
2025: this .valueMetaData.parent = this ;
2026: }
2027:
2028: /**
2029: * Mutator for the embedded MetaData
2030: * @param embeddedMetaData The embeddedMetaData to set.
2031: */
2032: public final void setEmbeddedMetaData(
2033: EmbeddedMetaData embeddedMetaData) {
2034: this .embeddedMetaData = embeddedMetaData;
2035: this .embeddedMetaData.parent = this ;
2036: }
2037:
2038: /**
2039: * Mutator for the foreignKey MetaData
2040: * @param foreignKeyMetaData The foreignKeyMetaData to set.
2041: */
2042: public final void setForeignKeyMetaData(
2043: ForeignKeyMetaData foreignKeyMetaData) {
2044: this .foreignKeyMetaData = foreignKeyMetaData;
2045: }
2046:
2047: /**
2048: * Mutator for the index MetaData
2049: * @param indexMetaData The indexMetaData to set.
2050: */
2051: public final void setIndexMetaData(IndexMetaData indexMetaData) {
2052: this .indexMetaData = indexMetaData;
2053: }
2054:
2055: /**
2056: * Mutator for the unique MetaData
2057: * @param uniqueMetaData The uniqueMetaData to set.
2058: */
2059: public final void setUniqueMetaData(UniqueMetaData uniqueMetaData) {
2060: this .uniqueMetaData = uniqueMetaData;
2061: }
2062:
2063: /**
2064: * Mutator for the join MetaData
2065: * @param joinMetaData The joinMetaData to set.
2066: */
2067: public final void setJoinMetaData(JoinMetaData joinMetaData) {
2068: this .joinMetaData = joinMetaData;
2069: this .joinMetaData.parent = this ;
2070: }
2071:
2072: /**
2073: * Mutator for the field id.
2074: * Given package access since updated by ClassMetaData typically.
2075: * Only used when the field is not an overriding field.
2076: * @param field_id Id of the field
2077: */
2078: void setFieldId(int field_id) {
2079: fieldId = field_id;
2080: }
2081:
2082: /**
2083: * Mutator for the table name
2084: * @param table The table name
2085: */
2086: public void setTable(String table) {
2087: this .table = (StringUtils.isWhitespace(table) ? null : table);
2088: }
2089:
2090: /**
2091: * Mutator for the catalog name
2092: * @param catalog The catalog name
2093: */
2094: public void setCatalog(String catalog) {
2095: this .catalog = (StringUtils.isWhitespace(catalog) ? null
2096: : catalog);
2097: }
2098:
2099: /**
2100: * Mutator for the schema name
2101: * @param schema The schema name
2102: */
2103: public void setSchema(String schema) {
2104: this .schema = (StringUtils.isWhitespace(schema) ? null : schema);
2105: }
2106:
2107: // ------------------------------ Utilities --------------------------------
2108:
2109: /**
2110: * Convenience method that sets up the relation type of this field, and the reference to
2111: * any related field when it is bidirectional. If the relation is bidirectional then will also
2112: * set the other side of the relation (to relate to this side).
2113: * @param clr ClassLoader resolver
2114: * @throws JPOXUserException If mapped-by doesnt exist at other side
2115: */
2116: protected void setRelation(ClassLoaderResolver clr) {
2117: if (getPersistenceModifier() != FieldPersistenceModifier.PERSISTENT) {
2118: // Only of use for persistent fields
2119: relationType = Relation.NONE;
2120: relatedMemberMetaData = null;
2121: return;
2122: }
2123:
2124: // Find the metadata for the field object
2125: AbstractClassMetaData otherCmd = null;
2126: if (hasCollection()) {
2127: otherCmd = getCollection().getElementClassMetaData();
2128: if (otherCmd == null) {
2129: // Maybe a reference field
2130: Class elementCls = clr.classForName(getCollection()
2131: .getElementType());
2132: if (getMetaDataManager().getOMFContext()
2133: .getTypeManager().isReferenceType(elementCls)) {
2134: try {
2135: String[] implNames = MetaDataUtils
2136: .getInstance()
2137: .getImplementationNamesForReferenceField(
2138: this ,
2139: DatastoreField.ROLE_COLLECTION_ELEMENT,
2140: clr);
2141: if (implNames != null && implNames.length > 0) {
2142: // Take the first implementation
2143: otherCmd = getMetaDataManager()
2144: .getMetaDataForClass(implNames[0],
2145: clr);
2146: }
2147: } catch (JPOXUserException jpe) {
2148: if (!getCollection().isSerializedElement()
2149: && mappedBy != null) {
2150: // Non-serialised, and with mapped-by so we need implementation classes
2151: throw jpe;
2152: } else {
2153: // Serialised element with no implementation types so ignore it
2154: JPOXLogger.METADATA
2155: .info("Field "
2156: + getFullFieldName()
2157: + " is a collection of elements of reference type yet no implementation-classes are provided."
2158: + " Assuming they arent PersistenceCapable");
2159: }
2160: }
2161: }
2162: }
2163: } else if (hasMap()) {
2164: otherCmd = ((MapMetaData) container)
2165: .getValueClassMetaData();
2166: //TODO [CORE-2585] valueCMD may be null because its type is an interface (non persistent interface),
2167: //so we should handle the implementation classes
2168: if (otherCmd == null) {
2169: // Value not PC so use the Key if it is specified
2170: otherCmd = ((MapMetaData) container)
2171: .getKeyClassMetaData();
2172: }
2173: if (otherCmd == null) {
2174: // Maybe a reference key/value
2175: }
2176: } else if (hasArray()) {
2177: otherCmd = ((ArrayMetaData) container)
2178: .getElementClassMetaData();
2179: } else {
2180: if (getType().isInterface()) {
2181: // Reference field - take the metadata of the first implementation
2182: String[] implNames = MetaDataUtils.getInstance()
2183: .getImplementationNamesForReferenceField(this ,
2184: DatastoreField.ROLE_FIELD, clr);
2185: if (implNames != null && implNames.length > 0) {
2186: otherCmd = getMetaDataManager()
2187: .getMetaDataForClass(implNames[0], clr);
2188: }
2189: /*otherCmd = getMetaDataManager().getMetaDataForImplementationOfReference(getType(), null, clr);*/
2190: /*otherCmd = getAbstractClassMetaData().getMetaDataManager().getMetaDataForInterface(getType(), clr);*/
2191: } else {
2192: otherCmd = getMetaDataManager().getMetaDataForClass(
2193: getType(), clr);
2194: }
2195: }
2196:
2197: //TODO [CORE-2585] when the element or value type is an interface (non persistent interface),
2198: //we should look at the implementation classes for the "otherCmd"
2199: //for now the relationType will be Relation.NONE in these cases because the otherCmd is null
2200: if (otherCmd == null) {
2201: if (hasArray()
2202: && getArray()
2203: .mayContainPersistenceCapableElements()) {
2204: relatedMemberMetaData = null;
2205: relationType = Relation.ONE_TO_MANY_UNI;
2206: } else {
2207: // Field cannot have a relation
2208: relatedMemberMetaData = null;
2209: relationType = Relation.NONE;
2210: }
2211: } else {
2212: // Field is bidirectional
2213: if (mappedBy != null) {
2214: // This class has the "mapped-by" specified
2215: AbstractMemberMetaData otherMmd = otherCmd
2216: .getMetaDataForMember(mappedBy);
2217: if (otherMmd == null) {
2218: throw new JPOXUserException(LOCALISER.msg("044115",
2219: getAbstractClassMetaData()
2220: .getFullClassName(), name,
2221: mappedBy, otherCmd.getFullClassName()))
2222: .setFatal();
2223: }
2224:
2225: relatedMemberMetaData = new AbstractMemberMetaData[] { otherMmd };
2226: if (hasContainer()
2227: && relatedMemberMetaData[0].hasContainer()) {
2228: relationType = Relation.MANY_TO_MANY_BI;
2229: } else if (hasContainer()
2230: && !relatedMemberMetaData[0].hasContainer()) {
2231: relationType = Relation.ONE_TO_MANY_BI;
2232: } else if (!hasContainer()
2233: && relatedMemberMetaData[0].hasContainer()) {
2234: relationType = Relation.MANY_TO_ONE_BI;
2235: } else {
2236: relationType = Relation.ONE_TO_ONE_BI;
2237: }
2238: } else {
2239: // The "other" class maybe has "mapped-by" across to this field so navigate through the fields to find this one
2240: int[] otherFieldNumbers = otherCmd
2241: .getAllMemberPositions();
2242: HashSet relatedFields = new HashSet();
2243: for (int i = 0; i < otherFieldNumbers.length; i++) {
2244: AbstractMemberMetaData otherFmd = otherCmd
2245: .getMetaDataForManagedMemberAtAbsolutePosition(otherFieldNumbers[i]);
2246: if (otherFmd.getMappedBy() != null
2247: && otherFmd.getMappedBy().equals(name)) {
2248: // e.g Look at org.jpox.samples.inheritance.marbles
2249: if (otherFmd.hasContainer()) {
2250: // N-1, M-N
2251: if ((otherFmd.hasCollection() && otherFmd
2252: .getCollection().getElementType()
2253: .equals(getClassName(true)))
2254: || (otherFmd.hasArray() && otherFmd
2255: .getArray()
2256: .getElementType().equals(
2257: getClassName(true)))
2258: || (otherFmd.hasMap() && otherFmd
2259: .getMap().getKeyType()
2260: .equals(getClassName(true)))
2261: || (otherFmd.hasMap() && otherFmd
2262: .getMap().getValueType()
2263: .equals(getClassName(true)))) {
2264: relatedFields.add(otherFmd);
2265: if (hasContainer()) {
2266: // Should we mark Arrays, Lists, Maps as M-N ?
2267: relationType = Relation.MANY_TO_MANY_BI;
2268: } else {
2269: relationType = Relation.MANY_TO_ONE_BI;
2270: }
2271: }
2272: } else {
2273: // 1-1, 1-N
2274: Class cls = clr
2275: .classForName(getClassName(true));
2276: if (otherFmd.getType()
2277: .isAssignableFrom(cls)
2278: || cls.isAssignableFrom(otherFmd
2279: .getType())) {
2280: // Consistent 1-1, 1-N types (allow subclasses of the defined types)
2281: relatedFields.add(otherFmd);
2282: if (hasContainer()) {
2283: relationType = Relation.ONE_TO_MANY_BI;
2284: } else {
2285: relationType = Relation.ONE_TO_ONE_BI;
2286: }
2287: }
2288: }
2289: }
2290: }
2291: if (relatedFields.size() > 0) {
2292: relatedMemberMetaData = (AbstractMemberMetaData[]) relatedFields
2293: .toArray(new AbstractMemberMetaData[relatedFields
2294: .size()]);
2295: relatedFields.clear();
2296: relatedFields = null;
2297: } else {
2298: // No "mapped-by" found at either end so is unidirectional
2299: if (hasContainer()) {
2300: relationType = Relation.ONE_TO_MANY_UNI;
2301: } else {
2302: relationType = Relation.ONE_TO_ONE_UNI;
2303: }
2304: }
2305: }
2306: }
2307: }
2308:
2309: /**
2310: * Accessor for the relation type for this field.
2311: * @param clr ClassLoader resolver
2312: * @return The relation type.
2313: */
2314: public int getRelationType(ClassLoaderResolver clr) {
2315: if (relationType == -1) {
2316: // Is possible that this could be done in the populate() step but depends on availability of the related class
2317: setRelation(clr);
2318: }
2319: return relationType;
2320: }
2321:
2322: /**
2323: * Convenience method for whether this field is the owner of the relation.
2324: * If the field has no relation will return true.
2325: * If the field is in a unidirectional relation will return true.
2326: * If the field is in a bidirectional relation and has no mapped-by will return true.
2327: * Otherwise returns false.
2328: * @param clr ClassLoader resolver
2329: * @return Whether it is the owner side of a relation
2330: */
2331: public boolean isRelationOwner(ClassLoaderResolver clr) {
2332: if (relationType == -1) {
2333: setRelation(clr);
2334: }
2335: if (relationType == Relation.NONE) {
2336: return true;
2337: } else if (relationType == Relation.ONE_TO_MANY_UNI
2338: || relationType == Relation.ONE_TO_ONE_UNI) {
2339: return true;
2340: } else if (relationType == Relation.MANY_TO_MANY_BI
2341: || relationType == Relation.MANY_TO_ONE_BI
2342: || relationType == Relation.ONE_TO_MANY_BI
2343: || relationType == Relation.ONE_TO_ONE_BI) {
2344: return (mappedBy == null);
2345: }
2346: return false;
2347: }
2348:
2349: /**
2350: * Accessor for the FieldMetaData of any related field/property (where this field is part of a
2351: * bidirectional relation). Allows for 1-1, 1-N, and M-N. If this field is not part of a bidirectional
2352: * relation (no "mapped-by" at either end) then it returns null.
2353: * @param clr the ClassLoaderResolver
2354: * @return The MetaData for the field/property at the "other end".
2355: */
2356: public AbstractMemberMetaData[] getRelatedMemberMetaData(
2357: ClassLoaderResolver clr) {
2358: if (relationType == -1) {
2359: // Is possible that this could be done in the populate() step but depends on availability of the related class
2360: setRelation(clr);
2361: }
2362: // TODO This doesnt allow for 1-1, 1-N reference fields where there may be multiple "other fields".
2363: return relatedMemberMetaData;
2364: }
2365:
2366: /**
2367: * Convenience accessor for the MetaData for the field/property at the other side of the bidirectional
2368: * relation given the objects at this side and the other side.
2369: * @param clr ClassLoader Resolver
2370: * @param thisPC This object
2371: * @param otherPC The related object
2372: * @return The MetaData for the field in the related object
2373: */
2374: public AbstractMemberMetaData getRelatedMemberMetaDataForObject(
2375: ClassLoaderResolver clr, Object this PC, Object otherPC) {
2376: if (relationType == -1) {
2377: setRelation(clr);
2378: }
2379:
2380: if (relatedMemberMetaData == null) {
2381: return null;
2382: }
2383:
2384: // TODO Cater for 1-N, M-N types
2385: for (int i = 0; i < relatedMemberMetaData.length; i++) {
2386: if (relationType == Relation.ONE_TO_ONE_BI) {
2387: if (relatedMemberMetaData[i].getType()
2388: .isAssignableFrom(this PC.getClass())
2389: && getType().isAssignableFrom(
2390: otherPC.getClass())) {
2391: return relatedMemberMetaData[i];
2392: }
2393: } else if (relationType == Relation.MANY_TO_ONE_BI) {
2394: // Just allow for Collections
2395: if (relatedMemberMetaData[i].hasCollection()) {
2396: Class elementType = clr
2397: .classForName(relatedMemberMetaData[i]
2398: .getCollection().getElementType());
2399: if (elementType.isAssignableFrom(this PC.getClass())
2400: && getType().isAssignableFrom(
2401: otherPC.getClass())) {
2402: return relatedMemberMetaData[i];
2403: }
2404: } else if (relatedMemberMetaData[i].hasMap()) {
2405: Class valueType = clr
2406: .classForName(relatedMemberMetaData[i]
2407: .getMap().getValueType());
2408: if (valueType.isAssignableFrom(this PC.getClass())
2409: && getType().isAssignableFrom(
2410: otherPC.getClass())) {
2411: return relatedMemberMetaData[i];
2412: }
2413: Class keyType = clr
2414: .classForName(relatedMemberMetaData[i]
2415: .getMap().getKeyType());
2416: if (keyType.isAssignableFrom(this PC.getClass())
2417: && getType().isAssignableFrom(
2418: otherPC.getClass())) {
2419: return relatedMemberMetaData[i];
2420: }
2421: }
2422: }
2423: }
2424: return null;
2425: }
2426:
2427: /**
2428: * Accessor for all ClassMetaData referenced by this Field.
2429: * <p>
2430: * Part of the "persistence-by-reachability" concept.
2431: * @param orderedCMDs List of ordered ClassMetaData objects (added to).
2432: * @param referencedCMDs Set of referenced ClassMetaData objects (added to)
2433: * @param dba_vendor_id Vendor id of the DBA
2434: * @param clr the ClassLoaderResolver
2435: **/
2436: void getReferencedClassMetaData(final List orderedCMDs,
2437: final Set referencedCMDs, final String dba_vendor_id,
2438: final ClassLoaderResolver clr) {
2439: AbstractClassMetaData type_cmd = getAbstractClassMetaData()
2440: .getMetaDataManager().getMetaDataForClass(getType(),
2441: clr);
2442: if (type_cmd != null) {
2443: type_cmd.getReferencedClassMetaData(orderedCMDs,
2444: referencedCMDs, dba_vendor_id, clr);
2445: }
2446:
2447: if (container != null) {
2448: if (container instanceof CollectionMetaData) {
2449: ((CollectionMetaData) container)
2450: .getReferencedClassMetaData(orderedCMDs,
2451: referencedCMDs, dba_vendor_id, clr);
2452: } else if (container instanceof MapMetaData) {
2453: ((MapMetaData) container)
2454: .getReferencedClassMetaData(orderedCMDs,
2455: referencedCMDs, dba_vendor_id, clr);
2456: } else if (container instanceof ArrayMetaData) {
2457: ((ArrayMetaData) container)
2458: .getReferencedClassMetaData(orderedCMDs,
2459: referencedCMDs, dba_vendor_id, clr);
2460: }
2461: }
2462: }
2463:
2464: /**
2465: * Calculate wether this field should be a second class mutable field.
2466: * This calculation is a bit expensive.
2467: * Please note that this data will be cached in {@link AbstractClassMetaData#scoMutableMemberFlags}.
2468: *
2469: * @return wether this field should be a second class mutable field.
2470: */
2471: public boolean calcIsSecondClassMutable() {
2472: if (hasExtension("is-second-class")) {
2473: String isSecondClass = getValueForExtension("is-second-class");
2474:
2475: if (isSecondClass.equalsIgnoreCase("true")) {
2476: return true;
2477: } else if (isSecondClass.equalsIgnoreCase("false")) {
2478: return false;
2479: } else if (isSecondClass.equalsIgnoreCase("default")) { // fall through to default behaviour
2480:
2481: } else {
2482: // Be nice to JDO mapping authors and remind them that they may have a misspelling.
2483: throw new UnsupportedOperationException(
2484: this
2485: + ": extensions \"is-second-class\" may have only values \"true\", \"false\", \"default\" or it may not be set at all. But it is set to \""
2486: + isSecondClass + "\".");
2487: }
2488: } else { // fall through to default behaviour
2489:
2490: }
2491:
2492: return getMetaDataManager().getOMFContext().getTypeManager()
2493: .isSecondClassMutableType(getTypeName());
2494: }
2495:
2496: /**
2497: * Returns a string representation of the object using a prefix
2498: * This can be used as part of a facility to output a MetaData file.
2499: * @param prefix prefix string
2500: * @param indent indent string
2501: * @return a string representation of the object.
2502: */
2503: public String toString(String prefix, String indent) {
2504: return super .toString(prefix, indent);
2505: }
2506:
2507: /**
2508: * Comparator method. This allows the ClassMetaData to search for a
2509: * FieldMetaData with a particular name.
2510: * @param o The object to compare against
2511: * @return The comparison result
2512: */
2513: public int compareTo(Object o) {
2514: if (o instanceof AbstractMemberMetaData) {
2515: AbstractMemberMetaData c = (AbstractMemberMetaData) o;
2516: return this .name.compareTo(c.name);
2517: } else if (o instanceof String) {
2518: return this .name.compareTo((String) o);
2519: } else if (o == null) {
2520: throw new ClassCastException("object is null");
2521: }
2522: throw new ClassCastException(this .getClass().getName() + " != "
2523: + o.getClass().getName());
2524: }
2525:
2526: /**
2527: * Convenience accessor for the MetaData Manager in use.
2528: * @return MetaDataManager.
2529: */
2530: public MetaDataManager getMetaDataManager() {
2531: MetaData overallCmd = getOverallParentClassMetaData(this );
2532: return (overallCmd != null ? ((AbstractClassMetaData) overallCmd)
2533: .getMetaDataManager()
2534: : null);
2535: }
2536: }
|