0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.metadata;
0012:
0013: import com.versant.core.jdo.VersantPersistenceManagerImp;
0014: import com.versant.core.jdo.VersantStateManager;
0015: import com.versant.core.jdo.sco.*;
0016: import com.versant.core.metadata.parser.JdoArray;
0017: import com.versant.core.metadata.parser.JdoCollection;
0018: import com.versant.core.metadata.parser.JdoField;
0019: import com.versant.core.metadata.parser.JdoMap;
0020: import com.versant.core.jdo.query.OrderNode;
0021: import com.versant.core.jdo.externalizer.Externalizer;
0022: import com.versant.core.util.classhelper.ClassHelper;
0023:
0024: import javax.jdo.spi.PersistenceCapable;
0025: import java.io.*;
0026: import java.lang.reflect.Field;
0027: import java.lang.reflect.Modifier;
0028: import java.util.*;
0029:
0030: import com.versant.core.common.*;
0031:
0032: /**
0033: * Meta data for a persistent or transactional field that is common to all
0034: * DataStore's and the enhancer.
0035: */
0036: public class FieldMetaData implements Externalizable, Comparable,
0037: MDStatics, VersantFieldMetaData {
0038:
0039: /**
0040: * The class this field belongs to.
0041: */
0042: public ClassMetaData classMetaData;
0043: /**
0044: * The parsed info for this field (null if none i.e. meta data created
0045: * using reflection).
0046: */
0047: public JdoField jdoField;
0048: /**
0049: * The name of the field.
0050: */
0051: public String name;
0052: public String origName;
0053: /**
0054: * The relative field number of this field.
0055: */
0056: public int fieldNo;
0057: /**
0058: * The state field no for this field.
0059: */
0060: public int stateFieldNo;
0061: /**
0062: * The absolute fieldNo for this field.
0063: */
0064: public int managedFieldNo = -1;
0065: /**
0066: * Is this an artificial field created to hold some store specific
0067: * information (e.g. row version column values for a JDBC store)?
0068: */
0069: public boolean fake;
0070: /**
0071: * If this is a fake field then these are the real fields it is linked
0072: * to. This is typically used to make sure the fake field is in all
0073: * the same fetch groups as its associated fields. This may be null in
0074: * which case the fake field is added to all fetch groups.
0075: */
0076: public FieldMetaData[] fakeLinks;
0077: /**
0078: * The persistence-modifier of the field.
0079: *
0080: * @see MDStatics#PERSISTENCE_MODIFIER_PERSISTENT
0081: * @see MDStatics#PERSISTENCE_MODIFIER_TRANSACTIONAL
0082: */
0083: public int persistenceModifier;
0084: /**
0085: * What sort of field is this? This is a general classification to make
0086: * it easier to work with the field.
0087: *
0088: * @see MDStatics#CATEGORY_SIMPLE
0089: * @see MDStatics#CATEGORY_COLLECTION
0090: * @see MDStatics#CATEGORY_ARRAY
0091: * @see MDStatics#CATEGORY_MAP
0092: * @see MDStatics#CATEGORY_REF
0093: * @see MDStatics#CATEGORY_TRANSACTIONAL
0094: * @see MDStatics#CATEGORY_EXTERNALIZED
0095: */
0096: public int category;
0097: /**
0098: * Is this field transformed into a different type when going to/from
0099: * storage? Null if not.
0100: */
0101: public Externalizer externalizer;
0102: /**
0103: * If this field transported using a CollectionDiff subclass
0104: * (e.g. List, Set and Map fields) ?
0105: */
0106: public boolean collectionDiffType;
0107:
0108: /**
0109: * Is this field persisted as part of the object itself in the datastore?
0110: * This is set by the store for the class (e.g. the JdbcDataStore will
0111: * set this for fields stored in the main table and the VdsDataStore will
0112: * set this for fields stored with the object itself).
0113: */
0114: public boolean primaryField;
0115: /**
0116: * Is this field persisted separately to the object itself? This is set by
0117: * the store for the class (e.g. the JdbcDataStore will set this for fields
0118: * stored using a link table and the VdsDataStore will set this for fields
0119: * not stored as part of the Object e.g. Collections).
0120: */
0121: public boolean secondaryField;
0122: /**
0123: * The java type of this field.
0124: */
0125: public Class type;
0126: /**
0127: * The java type code of this field if it is a simple type (int, Integer,
0128: * String etc.).
0129: *
0130: * @see MDStatics
0131: */
0132: public int typeCode;
0133:
0134: /**
0135: * This is just the method name on state to call to retrieve this field.
0136: */
0137: public String stateGetMethodName;
0138: /**
0139: * If this field is an array then this is the type of the entries (null
0140: * otherwise).
0141: */
0142: public Class componentType;
0143: /**
0144: * The type code for componentType (0 if none).
0145: * @see MDStatics
0146: * @see #componentType
0147: */
0148: public int componentTypeCode;
0149: /**
0150: * If the type or componentType is a PC class then this is its meta data.
0151: * This is null for Collection's.
0152: *
0153: * @see #componentType
0154: * @see #elementType
0155: * @see #elementTypeMetaData
0156: */
0157: public ClassMetaData typeMetaData;
0158: /**
0159: * The java modifiers for this field (transient etc).
0160: *
0161: * @see java.lang.reflect.Modifier
0162: */
0163: public int modifiers;
0164: /**
0165: * Is this field part of the primary key?
0166: */
0167: public boolean primaryKey;
0168: /**
0169: * Cache for the value of this field in a new objectid-class instance
0170: *
0171: * @see #getPKDefaultValue()
0172: */
0173: private Object pkDefaultValue;
0174: /**
0175: * If this field is part of the primary key and this class uses application
0176: * identity then this is the corresponding field from the objectid-class.
0177: */
0178: private transient Field objectidClassField;
0179: /**
0180: * How are nulls handled (null-value attribute)?
0181: *
0182: * @see MDStatics#NULL_VALUE_DEFAULT
0183: * @see MDStatics#NULL_VALUE_EXCEPTION
0184: * @see MDStatics#NULL_VALUE_NONE
0185: */
0186: public int nullValue;
0187: /**
0188: * Is this field in the default-fetch-group?
0189: */
0190: public boolean defaultFetchGroup;
0191: /**
0192: * Is this field in the default-fetch-group by default (true for ints etc
0193: * false for references and so on)?
0194: */
0195: public boolean defaultFetchGroupDefault;
0196: /**
0197: * Should this field embedded (hint to the store)?
0198: */
0199: public boolean embedded;
0200:
0201: /**
0202: * The parsed collection element (null if none).
0203: */
0204: public JdoCollection jdoCollection;
0205: /**
0206: * The parsed array element (null if none).
0207: */
0208: public JdoArray jdoArray;
0209: /**
0210: * The parsed map element (null if none).
0211: */
0212: public JdoMap jdoMap;
0213: /**
0214: * The type stored in the collection or the value type for a map or the
0215: * component type for an array.
0216: */
0217: public Class elementType;
0218: /**
0219: * The type code for elementType (0 if none).
0220: *
0221: * @see MDStatics
0222: * @see #elementType
0223: */
0224: public int elementTypeCode;
0225: /**
0226: * The meta data for elementType if it is a PC class (null otherwise).
0227: */
0228: public ClassMetaData elementTypeMetaData;
0229: /**
0230: * Should the collection or array elements (or values for a map)
0231: * be embedded?
0232: */
0233: public boolean embeddedElement;
0234: /**
0235: * The key type (null if not a map).
0236: */
0237: public Class keyType;
0238: /**
0239: * The type code for keyType (0 if none).
0240: *
0241: * @see MDStatics
0242: * @see #keyType
0243: */
0244: public int keyTypeCode;
0245: /**
0246: * The meta data for keyType if it is a PC class (null otherwise).
0247: */
0248: public ClassMetaData keyTypeMetaData;
0249: /**
0250: * Should the keys be embedded?
0251: */
0252: public boolean embeddedKey;
0253: /**
0254: * Is this an ordered collection?
0255: */
0256: public boolean ordered;
0257: /**
0258: * The fetch group for this field. This is the fetch group used when
0259: * the field is requested and no fetch group is specified.
0260: */
0261: public FetchGroup fetchGroup;
0262:
0263: /**
0264: * Are the objects referenced by this field dependent? This is only valid
0265: * for references, collections, arrays and maps etc. Dependent objects
0266: * are deleted when their owner (i.e. the object with this field) is
0267: * deleted. For a map this refers to the values.
0268: *
0269: * @see #dependentKeys
0270: */
0271: public boolean dependentValues;
0272: /**
0273: * This is only valid for map fields. It provides the same functionality
0274: * for the keys of the map as the the dependentValues flag does for the
0275: * values.
0276: *
0277: * @see #dependentValues
0278: */
0279: public boolean dependentKeys;
0280:
0281: /**
0282: * Is this fields value set automatically on commit? This feature is used
0283: * to implement row version and timestamp optimistic locking but can be
0284: * used for other purposes.
0285: */
0286: public int autoSet;
0287:
0288: /**
0289: * Extra store specific meta data for this field.
0290: */
0291: public transient Object storeField;
0292: /**
0293: * If this is a collection, array or map and this field is true then all
0294: * data must be provided in the diff instance instead of just the changes
0295: * on commit or flush. This is used for datastores like VDS that always
0296: * write everything.
0297: */
0298: public boolean includeAllDataInDiff;
0299: /**
0300: * Is this field a master (one) in a master/detail (one-to-many)
0301: * relationship
0302: */
0303: public boolean isMaster;
0304: /**
0305: * Is this field a detail (many) in a master/detail (one-to-many)
0306: * relationship? This field is set both for managed and unmanaged
0307: * relationships.
0308: */
0309: public boolean isDetail;
0310: /**
0311: * Is this field in a many-to-many relationship?
0312: */
0313: public boolean isManyToMany;
0314: /**
0315: * Is this field read only (e.g. the inverse side of a many-to-many)?
0316: */
0317: public boolean isReadOnly;
0318: /**
0319: * If isMaster, isDetail or isManyToMany is set then this is the fieldNo of
0320: * the field on the other side of the relationship.
0321: */
0322: public int inverseFieldNo = -1;
0323: /**
0324: * Is isMaster or isManyToMany is set then this indicates if
0325: * the relationship is managed by the SCOs or not. Note that this field
0326: * is filled in by the JdbcField involved.
0327: */
0328: public boolean managed;
0329: /**
0330: * If isMaster, isDetail or isManyToMany is set then this is the field
0331: * on the other side of the relationship.
0332: */
0333: public FieldMetaData inverseFieldMetaData;
0334: /**
0335: * If the field is a sco field.
0336: */
0337: public boolean scoField;
0338: /**
0339: * Should the field be returned as null if the referenced object is not
0340: * found? This is useful for references with all of their columns shared
0341: * with the primary key.
0342: */
0343: public boolean nullIfNotFound;
0344: /**
0345: * This is filled for unordered collection fields with an ordering
0346: * extension. Only required on server so is transient.
0347: */
0348: public transient OrderNode[] ordering;
0349: /**
0350: * The cascade type for this field.
0351: * This is currently only used by entitymanager.
0352: * @see MDStatics.CASCADE_ALL
0353: * @see MDStatics.CASCADE_MERGE
0354: * @see MDStatics.CASCADE_PERSIST
0355: * @see MDStatics.CASCADE_REFRESH
0356: * @see MDStatics.CASCADE_REMOVE
0357: */
0358: public int cascadeType = 0;
0359:
0360: private transient Comparator comparator;
0361: private transient boolean comparatorInitDone;
0362: private RuntimeException error;
0363: private long errorTime = Long.MAX_VALUE;
0364: private Object scoFactory;
0365: public VersantSCOFactory simpleSCOFactory;
0366: public VersantSCOCollectionFactory collectionFactory;
0367: public VersantSCOMapFactory mapFactory;
0368: public static final String NO_FIELD_TEXT = "{auto}";
0369: //If this is a fmd that was created from a embedded pc instance then this
0370: //is the link back to the original field
0371: public FieldMetaData origFmd;
0372: public FieldMetaData[] embeddedFmds;
0373: /**
0374: * If this field acts a nullIndicator
0375: */
0376: public FieldMetaData nullIndicatorFmd;
0377: public FieldMetaData[] managedEmbeddedFields;
0378: /**
0379: * Is this field embedded from another class into this class.
0380: */
0381: public boolean embeddedFakeField;
0382: /**
0383: * If this is a fake field that is created from the horizontal super class.
0384: */
0385: public boolean horizontalFakeField;
0386:
0387: public FieldMetaData() {
0388: }
0389:
0390: public String toString() {
0391: return "Field " + name;
0392: }
0393:
0394: /**
0395: * Return the fully qualified name of this field.
0396: */
0397: public String getQName() {
0398: return classMetaData.qname + "." + name;
0399: }
0400:
0401: /**
0402: * Return the type and fully qualified name of this field.
0403: */
0404: public String getTypeQName() {
0405: return type.getName() + " " + getQName();
0406: }
0407:
0408: /**
0409: * Get the meta data for the class we reference. If this field is an
0410: * array, collection or map of a PC class then this will be the meta data
0411: * for the value class. If this is a simple reference then this will
0412: * be the meta data of the referenced class. Otherwise null is returned.
0413: * Always returns null for externalized fields.
0414: *
0415: * @see #typeMetaData
0416: * @see #elementTypeMetaData
0417: */
0418: public ClassMetaData getRefOrValueClassMetaData() {
0419: if (category == MDStatics.CATEGORY_EXTERNALIZED)
0420: return null;
0421: if (typeMetaData != null)
0422: return typeMetaData;
0423: return elementTypeMetaData;
0424: }
0425:
0426: /**
0427: * Add all fetch groups we belong to to a.
0428: */
0429: public void findFetchGroups(ArrayList a) {
0430: FetchGroup[] groups = classMetaData.fetchGroups;
0431: int n = groups.length;
0432: for (int i = 0; i < n; i++) {
0433: FetchGroup g = groups[i];
0434: if (g.contains(this ))
0435: a.add(g);
0436: }
0437: }
0438:
0439: public void dump() {
0440: dump(Debug.OUT, "");
0441: }
0442:
0443: public void dump(PrintStream out, String indent) {
0444: out.println(indent + this );
0445: String is = indent + " ";
0446: out
0447: .println(is
0448: + "persistenceModifier = "
0449: + MDStaticUtils
0450: .toPersistenceModifierString(persistenceModifier));
0451: out.println(is + "category = "
0452: + MDStaticUtils.toCategoryString(category));
0453: out.println(is + "isPass1Field = " + primaryField);
0454: out.println(is + "isPass2Field = " + secondaryField);
0455: out.println(is + "fieldNo = " + fieldNo);
0456: out.println(is + "stateFieldNo = " + stateFieldNo);
0457: out.println(is + "fake = " + fake);
0458: StringBuffer s = new StringBuffer();
0459: s.append(is + "fakeLinks = ");
0460: if (fakeLinks == null) {
0461: s.append("null");
0462: } else {
0463: for (int i = 0; i < fakeLinks.length; i++) {
0464: if (i > 0)
0465: s.append(", ");
0466: s.append(fakeLinks[i].name);
0467: }
0468: s.append(']');
0469: }
0470: out.println(s.toString());
0471: out.println(is + "type = " + type);
0472: out.println(is + "typeCode = " + typeCode);
0473: out.println(is + "componentType = " + componentType);
0474: out.println(is + "componentTypeCode = " + componentTypeCode);
0475: out.println(is + "typeMetaData = " + typeMetaData);
0476: out.println(is + "modifiers = " + modifiers);
0477: out.println(is + "primaryKey = " + primaryKey);
0478: out.println(is + "nullValue = "
0479: + MDStaticUtils.toNullValueString(nullValue));
0480: out.println(is + "defaultFetchGroup = " + defaultFetchGroup);
0481: out.println(is + "defaultFetchGroupDefault = "
0482: + defaultFetchGroupDefault);
0483: out.println(is + "embedded = " + embedded);
0484: out.println(is + "jdoCollection = " + jdoCollection);
0485: out.println(is + "jdoArray = " + jdoArray);
0486: out.println(is + "jdoMap = " + jdoMap);
0487: out.println(is + "elementType = " + elementType);
0488: out.println(is + "elementTypeCode = " + elementTypeCode);
0489: out
0490: .println(is + "elementTypeMetaData = "
0491: + elementTypeMetaData);
0492: out.println(is + "embeddedElement = " + embeddedElement);
0493: out.println(is + "keyType = " + keyType);
0494: out.println(is + "keyTypeCode = " + keyTypeCode);
0495: out.println(is + "keyTypeMetaData = " + keyTypeMetaData);
0496: out.println(is + "embeddedKey = " + embeddedKey);
0497: out.println(is + "ordered = " + ordered);
0498: out.println(is + "fetchGroup = " + fetchGroup);
0499: out.println(is + "dependentValues = " + dependentValues);
0500: out.println(is + "dependentKeys = " + dependentKeys);
0501: out.println(is + "autoSet = "
0502: + MDStaticUtils.toAutoSetString(autoSet));
0503:
0504: out.println(is + "isMaster = " + isMaster);
0505: out.println(is + "isDetail = " + isDetail);
0506: out.println(is + "isManyToMany = " + isManyToMany);
0507: out.println(is + "isReadOnly = " + isReadOnly);
0508: out.println(is + "inverseFieldNo = " + inverseFieldNo);
0509:
0510: out.println(is + "storeField = " + storeField);
0511: }
0512:
0513: /**
0514: * Sort by name. Do not change this ordering as it is used to order fields
0515: * for fieldNos and so on.
0516: */
0517: public int compareTo(Object o) {
0518: return name.compareTo(((FieldMetaData) o).name);
0519: }
0520:
0521: public void setType(Class type) {
0522: this .type = type;
0523: if (type == null) {
0524: typeCode = 0;
0525: } else {
0526: typeCode = MDStaticUtils.toTypeCode(type);
0527: }
0528: }
0529:
0530: public static void setStateMethodName(FieldMetaData fmd) {
0531: switch (fmd.category) {
0532: case MDStatics.CATEGORY_SIMPLE:
0533: switch (fmd.typeCode) {
0534: case MDStatics.INT:
0535:
0536: fmd.stateGetMethodName = MDStatics.STATE_METHOD_INT;
0537: break;
0538: case MDStatics.LONG:
0539:
0540: fmd.stateGetMethodName = MDStatics.STATE_METHOD_LONG;
0541: break;
0542: case MDStatics.SHORT:
0543:
0544: fmd.stateGetMethodName = MDStatics.STATE_METHOD_SHORT;
0545: break;
0546: case MDStatics.STRING:
0547: fmd.stateGetMethodName = MDStatics.STATE_METHOD_STRING;
0548: break;
0549: case MDStatics.BOOLEAN:
0550: fmd.stateGetMethodName = MDStatics.STATE_METHOD_BOOLEAN;
0551: break;
0552: case MDStatics.BYTE:
0553:
0554: fmd.stateGetMethodName = MDStatics.STATE_METHOD_BYTE;
0555: break;
0556: case MDStatics.CHAR:
0557: fmd.stateGetMethodName = MDStatics.STATE_METHOD_CHAR;
0558: break;
0559: case MDStatics.DOUBLE:
0560: fmd.stateGetMethodName = MDStatics.STATE_METHOD_DOUBLE;
0561: break;
0562: case MDStatics.FLOAT:
0563: fmd.stateGetMethodName = MDStatics.STATE_METHOD_FLOAT;
0564: break;
0565: default:
0566: fmd.stateGetMethodName = MDStatics.STATE_METHOD_OBJECT;
0567: break;
0568: }
0569: break;
0570: default:
0571: fmd.stateGetMethodName = MDStatics.STATE_METHOD_OBJECT;
0572: break;
0573: }
0574: }
0575:
0576: public void setComponentType(Class componentType) {
0577: this .componentType = componentType;
0578: if (componentType == null) {
0579: componentTypeCode = 0;
0580: } else {
0581: componentTypeCode = MDStaticUtils.toTypeCode(componentType);
0582: }
0583: }
0584:
0585: public void setElementType(Class elementType) {
0586: this .elementType = elementType;
0587: if (elementType == null) {
0588: elementTypeCode = 0;
0589: } else {
0590: elementTypeCode = MDStaticUtils.toTypeCode(elementType);
0591: }
0592: }
0593:
0594: public void setKeyType(Class keyType) {
0595: this .keyType = keyType;
0596: if (keyType == null) {
0597: keyTypeCode = 0;
0598: } else {
0599: keyTypeCode = MDStaticUtils.toTypeCode(keyType);
0600: }
0601: }
0602:
0603: public void setScoField(boolean scoField) {
0604: this .scoField = scoField;
0605: }
0606:
0607: /**
0608: * Is this field a reference to another PC class? Note the PolyRef's are
0609: * not considered direct references.
0610: */
0611: public boolean isDirectRef() {
0612: return category == MDStatics.CATEGORY_REF;
0613: }
0614:
0615: public void setAutoSet(int autoSet) {
0616: this .autoSet = autoSet;
0617: }
0618:
0619: /**
0620: * Is the element type a persistent class?
0621: */
0622: public boolean isElementTypePC() {
0623: return elementTypeMetaData != null
0624: || elementType == Object.class
0625: || (elementType != null && elementType.isInterface());
0626: }
0627:
0628: /**
0629: * If the key field of the map is a PersistenceCapable instance.
0630: *
0631: * @see javax.jdo.spi.PersistenceCapable
0632: */
0633: public boolean isMapKeyRef() {
0634: return (category == MDStatics.CATEGORY_MAP ? isKeyTypePC()
0635: : false);
0636: }
0637:
0638: /**
0639: * If the value field of the map is a PersistenceCapable instance.
0640: *
0641: * @see javax.jdo.spi.PersistenceCapable
0642: */
0643: public boolean isMapValueRef() {
0644: return (category == MDStatics.CATEGORY_MAP ? isElementTypePC()
0645: : false);
0646: }
0647:
0648: /**
0649: * Must this field be removed from the state after commit of a transaction
0650: * with retainValues true? This is used to ensure that autoSet fields
0651: * are reread after an update or insert.
0652: */
0653: public boolean isClearOnRetainValues() {
0654: return autoSet == MDStatics.AUTOSET_NO;
0655: }
0656:
0657: /**
0658: * Must this field be included in all fetch groups? This is true for
0659: * fields that are always required (e.g. row version fields for optimistic
0660: * locking).
0661: * <p/>
0662: * App Id fields are also always included in all fetchGroups. This is done to
0663: * enable existence checking for a join.(if the owners id fields and the id fieds of the
0664: * joined row are the same. If the joined key fields are null then the ref is null)
0665: */
0666: public boolean includeInAllFGs() {
0667: // TODO make only version fields and timestamp fields used for locking
0668: return autoSet != AUTOSET_NO;
0669: }
0670:
0671: /**
0672: * Does this field have default-fetch-group set to true i.e. this was
0673: * done explicitly in the JDO meta data ?
0674: */
0675: public boolean isDefaultFetchGroupTrue() {
0676: return jdoField != null
0677: && jdoField.defaultFetchGroup == MDStatics.TRUE;
0678: }
0679:
0680: public boolean isJDODefaultFetchGroup() {
0681: if (isEmbeddedRef())
0682: return false;
0683: if (defaultFetchGroupDefault || isDefaultFetchGroupTrue()) {
0684: if (!defaultFetchGroup) {
0685: return false;
0686: } else {
0687: return true;
0688: }
0689: }
0690: return false;
0691: }
0692:
0693: // /**
0694: // * Get the default value of this field in the objectid-class. This is
0695: // * only valid for primary key fields.
0696: // */
0697: // public Object getPKDefaultValue() {
0698: // if (Debug.DEBUG) {
0699: // if (!primaryKey || classMetaData.identityType != MDStatics.IDENTITY_TYPE_APPLICATION) {
0700: // throw BindingSupportImpl.getInstance().internal(
0701: // "Not an application identity class: " + getQName());
0702: // }
0703: // }
0704: // if (!primaryKey) {
0705: // throw BindingSupportImpl.getInstance().internal("Not a primary-key field: " +
0706: // getQName());
0707: // }
0708: // if (pkDefaultValue == null) {
0709: // try {
0710: // Object o = classMetaData.objectIdClass.newInstance();
0711: // pkDefaultValue = classMetaData.objectIdClass.getField(getPkFieldName()).get(
0712: // o);
0713: // } catch (Exception e) {
0714: // throw BindingSupportImpl.getInstance().exception("Unable to get primary key field from objectid-class: " +
0715: // getQName() + ": " + e, e);
0716: // }
0717: // }
0718: // return pkDefaultValue;
0719: // }
0720:
0721: /**
0722: * Get the default value of this field in the objectid-class. This is
0723: * only valid for primary key fields.
0724: */
0725: public Object getPKDefaultValue() {
0726: if (Debug.DEBUG) {
0727: if (!primaryKey
0728: || classMetaData.identityType != MDStatics.IDENTITY_TYPE_APPLICATION) {
0729: throw BindingSupportImpl.getInstance().internal(
0730: "Not an application identity class: "
0731: + getQName());
0732: }
0733: }
0734: if (!primaryKey) {
0735: throw BindingSupportImpl.getInstance().internal(
0736: "Not a primary-key field: " + getQName());
0737: }
0738: return pkDefaultValue;
0739: }
0740:
0741: public void initPkDefaultValue(Object pkClassInst) {
0742: if (Debug.DEBUG) {
0743: if (!primaryKey
0744: || classMetaData.identityType != MDStatics.IDENTITY_TYPE_APPLICATION) {
0745: throw BindingSupportImpl.getInstance().internal(
0746: "Not an application identity class: "
0747: + getQName());
0748: }
0749: }
0750: if (!primaryKey) {
0751: throw BindingSupportImpl.getInstance().internal(
0752: "Not a primary-key field: " + getQName());
0753: }
0754: if (pkDefaultValue == null) {
0755: try {
0756: pkDefaultValue = classMetaData.objectIdClass.getField(
0757: getPkFieldName()).get(pkClassInst);
0758: } catch (Exception e) {
0759: throw BindingSupportImpl.getInstance().exception(
0760: "Unable to get primary key field from objectid-class: "
0761: + getQName() + ": " + e, e);
0762: }
0763: }
0764: }
0765:
0766: public void setPkDefaultValue(Object pkDefaultValue) {
0767: this .pkDefaultValue = pkDefaultValue;
0768: }
0769:
0770: public void writeExternal(ObjectOutput out) throws IOException {
0771: // if (Debug.DEBUG) {
0772: // System.out.println("%%% FieldMetaData.writeExternal " + getQName());
0773: // }
0774: out.writeObject(classMetaData);
0775: out.writeObject(jdoField);
0776: out.writeObject(name);
0777: out.writeObject(origName);
0778: out.writeInt(fieldNo);
0779: out.writeInt(stateFieldNo);
0780: out.writeInt(managedFieldNo);
0781: out.writeBoolean(fake);
0782: out.writeObject(fakeLinks);
0783: out.writeInt(persistenceModifier);
0784: out.writeInt(category);
0785: out.writeBoolean(primaryField);
0786: out.writeBoolean(secondaryField);
0787: out.writeInt(typeCode);
0788: if (MDStaticUtils.toSimpleClass(MDStaticUtils
0789: .toSimpleName(typeCode)) == null) {
0790: out.writeObject(type);
0791: }
0792: out.writeObject(componentType);
0793: out.writeInt(componentTypeCode);
0794: out.writeObject(typeMetaData);
0795: out.writeInt(modifiers);
0796: out.writeInt(nullValue);
0797: out.writeBoolean(defaultFetchGroup);
0798: out.writeBoolean(defaultFetchGroupDefault);
0799: out.writeBoolean(embedded);
0800: out.writeObject(jdoCollection);
0801: out.writeObject(jdoArray);
0802: out.writeObject(elementType);
0803: out.writeInt(elementTypeCode);
0804: out.writeObject(elementTypeMetaData);
0805: out.writeBoolean(embeddedElement);
0806: out.writeObject(keyType);
0807: out.writeInt(keyTypeCode);
0808: out.writeObject(keyTypeMetaData);
0809: out.writeBoolean(embeddedKey);
0810: out.writeBoolean(ordered);
0811: out.writeObject(fetchGroup);
0812: out.writeBoolean(dependentValues);
0813: out.writeBoolean(dependentKeys);
0814: out.writeInt(autoSet);
0815: out.writeBoolean(scoField);
0816: out.writeBoolean(primaryKey);
0817: out.writeBoolean(collectionDiffType);
0818: if (stateGetMethodName == null) {
0819: out.writeBoolean(false);
0820: } else {
0821: out.writeBoolean(true);
0822: out.writeUTF(stateGetMethodName);
0823: }
0824: out.writeBoolean(isMaster);
0825: out.writeBoolean(isDetail);
0826: out.writeBoolean(isManyToMany);
0827: out.writeBoolean(managed);
0828: out.writeBoolean(isReadOnly);
0829: out.writeInt(inverseFieldNo);
0830: out.writeObject(simpleSCOFactory);
0831: out.writeObject(collectionFactory);
0832: out.writeObject(mapFactory);
0833: out.writeBoolean(includeAllDataInDiff);
0834: out.writeObject(inverseFieldMetaData);
0835: out.writeObject(externalizer);
0836:
0837: out.writeObject(managedEmbeddedFields);
0838: out.writeObject(nullIndicatorFmd);
0839: out.writeBoolean(embeddedFakeField);
0840: out.writeObject(origFmd);
0841: out.writeObject(embeddedFmds);
0842: }
0843:
0844: public void readExternal(ObjectInput in) throws IOException,
0845: ClassNotFoundException {
0846: classMetaData = (ClassMetaData) in.readObject();
0847: jdoField = (JdoField) in.readObject();
0848: name = (String) in.readObject();
0849: origName = (String) in.readObject();
0850: fieldNo = in.readInt();
0851: stateFieldNo = in.readInt();
0852: managedFieldNo = in.readInt();
0853: fake = in.readBoolean();
0854: fakeLinks = (FieldMetaData[]) in.readObject();
0855: persistenceModifier = in.readInt();
0856: category = in.readInt();
0857: primaryField = in.readBoolean();
0858: secondaryField = in.readBoolean();
0859: typeCode = in.readInt();
0860: type = MDStaticUtils.toSimpleClass(MDStaticUtils
0861: .toSimpleName(typeCode));
0862: if (type == null) {
0863: type = (Class) in.readObject();
0864: }
0865: componentType = (Class) in.readObject();
0866: componentTypeCode = in.readInt();
0867: typeMetaData = (ClassMetaData) in.readObject();
0868: modifiers = in.readInt();
0869: nullValue = in.readInt();
0870: defaultFetchGroup = in.readBoolean();
0871: defaultFetchGroupDefault = in.readBoolean();
0872: embedded = in.readBoolean();
0873: jdoCollection = (JdoCollection) in.readObject();
0874: jdoArray = (JdoArray) in.readObject();
0875: elementType = (Class) in.readObject();
0876: elementTypeCode = in.readInt();
0877: elementTypeMetaData = (ClassMetaData) in.readObject();
0878: embeddedElement = in.readBoolean();
0879: keyType = (Class) in.readObject();
0880: keyTypeCode = in.readInt();
0881: keyTypeMetaData = (ClassMetaData) in.readObject();
0882: embeddedKey = in.readBoolean();
0883: ordered = in.readBoolean();
0884: fetchGroup = (FetchGroup) in.readObject();
0885: dependentValues = in.readBoolean();
0886: dependentKeys = in.readBoolean();
0887: autoSet = in.readInt();
0888: scoField = in.readBoolean();
0889: primaryKey = in.readBoolean();
0890: collectionDiffType = in.readBoolean();
0891: if (in.readBoolean()) {
0892: stateGetMethodName = in.readUTF();
0893: } else {
0894: stateGetMethodName = null;
0895: }
0896: isMaster = in.readBoolean();
0897: isDetail = in.readBoolean();
0898: isManyToMany = in.readBoolean();
0899: managed = in.readBoolean();
0900: isReadOnly = in.readBoolean();
0901: inverseFieldNo = in.readInt();
0902: simpleSCOFactory = (VersantSCOFactory) in.readObject();
0903: collectionFactory = (VersantSCOCollectionFactory) in
0904: .readObject();
0905: mapFactory = (VersantSCOMapFactory) in.readObject();
0906: includeAllDataInDiff = in.readBoolean();
0907: inverseFieldMetaData = (FieldMetaData) in.readObject();
0908: externalizer = (Externalizer) in.readObject();
0909:
0910: managedEmbeddedFields = (FieldMetaData[]) in.readObject();
0911: nullIndicatorFmd = (FieldMetaData) in.readObject();
0912: embeddedFakeField = in.readBoolean();
0913: origFmd = (FieldMetaData) in.readObject();
0914: embeddedFmds = (FieldMetaData[]) in.readObject();
0915: }
0916:
0917: /**
0918: * Get the name for use in comments in SQL files and so on. This will
0919: * include the name of the class if this field is in a subclass.
0920: */
0921: public String getCommentName() {
0922: if (classMetaData.pcSuperMetaData == null)
0923: return name;
0924: if (classMetaData.jdoClass == null)
0925: return getQName();
0926: return classMetaData.jdoClass.name + "." + name;
0927: }
0928:
0929: /**
0930: * Get the comparator for this field if it makes sense i.e. this
0931: * is a sorted Collection or Map with Comparator. This is cached.
0932: */
0933: public Comparator getComparator() {
0934: if (!comparatorInitDone) {
0935: comparatorInitDone = true;
0936: boolean set = SortedSet.class.isAssignableFrom(type);
0937: boolean map = !set
0938: && SortedMap.class.isAssignableFrom(type);
0939: if (set || map) {
0940: Object o = classMetaData.getMetaDataInstance();
0941: if (o != null) {
0942: Field f = getReflectField();
0943: try {
0944: Object v = ClassHelper.get()
0945: .getFieldValue(f, o);
0946: if (v != null) {
0947: if (set) {
0948: comparator = ((SortedSet) v)
0949: .comparator();
0950: } else {
0951: comparator = ((SortedMap) v)
0952: .comparator();
0953: }
0954: }
0955: } catch (Exception x) {
0956: throw BindingSupportImpl
0957: .getInstance()
0958: .invalidOperation(
0959: "Unable get Field comparator with reflection: "
0960: + getQName() + ": " + x,
0961: x);
0962: }
0963: }
0964: }
0965: }
0966: return comparator;
0967: }
0968:
0969: /**
0970: * Get a Field instance for this field using reflection. This will have
0971: * setAccessible(true) called on it.
0972: */
0973: public Field getReflectField() {
0974: try {
0975: Field f = classMetaData.cls.getDeclaredField(origName);
0976: ClassHelper.get().setAccessible(f, true);
0977: return f;
0978: } catch (Exception x) {
0979: throw BindingSupportImpl.getInstance().invalidOperation(
0980: "Unable get Field with reflection: " + getQName()
0981: + ": " + x, x);
0982: }
0983: }
0984:
0985: public void addError(RuntimeException e, boolean quiet) {
0986: if (Debug.DEBUG)
0987: e.printStackTrace(System.out);
0988: if (error == null) {
0989: errorTime = System.currentTimeMillis();
0990: error = e;
0991: try {
0992: Thread.sleep(1);
0993: } catch (InterruptedException e1) {
0994: // ignore
0995: }
0996: }
0997: if (!quiet)
0998: throw e;
0999: }
1000:
1001: public RuntimeException getFirstError() {
1002: return error;
1003: }
1004:
1005: public long getFirstErrorTime() {
1006: return errorTime;
1007: }
1008:
1009: public boolean hasErrors() {
1010: return error != null;
1011: }
1012:
1013: /**
1014: * The name of the field.
1015: */
1016: public String getName() {
1017: return name;
1018: }
1019:
1020: /**
1021: * Is this an ordered collection?
1022: */
1023: public boolean isOrdered() {
1024: return ordered;
1025: }
1026:
1027: /**
1028: * The type code for elementType (0 if none).
1029: *
1030: * @see MDStatics
1031: * @see #elementType
1032: */
1033: public int getElementTypeCode() {
1034: return elementTypeCode;
1035: }
1036:
1037: /**
1038: * The absolute fieldNo for this field.
1039: */
1040: public int getManagedFieldNo() {
1041: return managedFieldNo;
1042: }
1043:
1044: /**
1045: * The meta data for keyType if it is a PC class (null otherwise).
1046: */
1047: public boolean isKeyTypePC() {
1048: return keyTypeMetaData != null || keyType == Object.class
1049: || (keyType != null && keyType.isInterface());
1050: }
1051:
1052: /**
1053: * The type code for keyType (0 if none).
1054: *
1055: * @see MDStatics
1056: * @see #keyType
1057: */
1058: public int getKeyTypeCode() {
1059: return keyTypeCode;
1060: }
1061:
1062: /**
1063: * The type stored in the collection or the value type for a map or the
1064: * component type for an array.
1065: */
1066: public Class getElementType() {
1067: return elementType;
1068: }
1069:
1070: /**
1071: * The key type (null if not a map).
1072: */
1073: public Class getKeyType() {
1074: return keyType;
1075: }
1076:
1077: /**
1078: * Is isMaster or isManyToMany is set then this indicates if
1079: * the relationship is managed by the SCOs or not. Note that this field
1080: * is filled in by the JdbcField involved.
1081: */
1082: public boolean isManaged() {
1083: return managed;
1084: }
1085:
1086: /**
1087: * Is this field a master (one) in a master/detail (one-to-many)
1088: * relationship
1089: */
1090: public boolean isMaster() {
1091: return isMaster;
1092: }
1093:
1094: /**
1095: * Is this field in a many-to-many relationship?
1096: */
1097: public boolean isManyToMany() {
1098: return isManyToMany;
1099: }
1100:
1101: /**
1102: * If isMaster, isDetail or isManyToMany is set then this is the fieldNo of
1103: * the field on the other side of the relationship.
1104: */
1105: public int getInverseFieldNo() {
1106: return inverseFieldNo;
1107: }
1108:
1109: /**
1110: * If isMaster, isDetail or isManyToMany is set then this is the field
1111: * on the other side of the relationship.
1112: */
1113: public VersantFieldMetaData getInverseFieldMetaData() {
1114: return inverseFieldMetaData;
1115: }
1116:
1117: public VersantSimpleSCO createSCO(PersistenceContext pm,
1118: VersantStateManager sm, FieldMetaData fmd,
1119: PersistenceCapable owner, Object data) {
1120: //create a new SCO data
1121: switch (fmd.category) {
1122: case MDStatics.CATEGORY_SIMPLE:
1123: return createSimpleSCO(pm, sm, fmd, owner, data);
1124: case MDStatics.CATEGORY_COLLECTION:
1125: return createCollectionSCO(pm, sm, fmd, owner, data);
1126: case MDStatics.CATEGORY_MAP:
1127: return createMapSCO(pm, sm, fmd, owner, data);
1128: }
1129: return null;
1130: }
1131:
1132: public VersantSimpleSCO createSimpleSCO(PersistenceContext pm,
1133: VersantStateManager sm, FieldMetaData fmd,
1134: PersistenceCapable owner, Object data) {
1135: if (data == null)
1136: return null;
1137: if (data instanceof VersantAdvancedSCO) {
1138: VersantAdvancedSCO sco = (VersantAdvancedSCO) data;
1139: if (sco.getOwner() != null && sco.getOwner() == owner) {
1140: //may re-use
1141: sco.reset();
1142: return sco;
1143: }
1144: }
1145: //create a new SCO data
1146: return simpleSCOFactory.createSCO(owner, pm, sm, fmd, data);
1147: }
1148:
1149: public VersantSimpleSCO createCollectionSCO(PersistenceContext pm,
1150: VersantStateManager sm, FieldMetaData fmd,
1151: PersistenceCapable owner, Object data) {
1152: if (data == null)
1153: return null;
1154: if (data instanceof VersantAdvancedSCO) {
1155: VersantAdvancedSCO sco = (VersantAdvancedSCO) data;
1156: if (sco.getOwner() != null && sco.getOwner() == owner) {
1157: //may re-use
1158: sco.reset();
1159: return sco;
1160: }
1161: }
1162: //create a new SCO data
1163: if (data instanceof CollectionData) {
1164: CollectionData collectionData = (CollectionData) data;
1165: return collectionFactory.createSCOCollection(owner, pm, sm,
1166: fmd, collectionData);
1167: } else if (data instanceof Object[]) {
1168: Object[] objects = (Object[]) data;
1169: CollectionData collectionData = new CollectionData();
1170: collectionData.valueCount = objects.length;
1171: collectionData.values = objects;
1172: return collectionFactory.createSCOCollection(owner, pm, sm,
1173: fmd, collectionData);
1174: } else if (data instanceof Collection) {
1175: Collection collection = (Collection) data;
1176: return collectionFactory.createSCOCollection(owner, pm, sm,
1177: fmd, collection);
1178:
1179: } else {
1180: throw BindingSupportImpl.getInstance().internal(
1181: "data is not CollectionData, Object[] or Collection: "
1182: + fmd.getQName() + " data " + data);
1183: }
1184: }
1185:
1186: public VersantSimpleSCO createMapSCO(PersistenceContext pm,
1187: VersantStateManager sm, FieldMetaData fmd,
1188: PersistenceCapable owner, Object data) {
1189: if (data == null)
1190: return null;
1191: if (data instanceof VersantAdvancedSCO) {
1192: VersantAdvancedSCO sco = (VersantAdvancedSCO) data;
1193: if (sco.getOwner() != null && sco.getOwner() == owner) {
1194: //may re-use
1195: sco.reset();
1196: return sco;
1197: }
1198: }
1199: //create a new SCO data
1200: if (data == null)
1201: return null;
1202: if (data instanceof MapData) {
1203: MapData mapData = (MapData) data;
1204: return mapFactory.createSCOHashMap(owner, pm, sm, fmd,
1205: mapData);
1206: } else if (data instanceof MapEntries) {
1207: MapEntries entries = (MapEntries) data;
1208: MapData mapData = new MapData();
1209: mapData.entryCount = entries.keys.length;
1210: mapData.keys = entries.keys;
1211: mapData.values = entries.values;
1212: return mapFactory.createSCOHashMap(owner, pm, sm, fmd,
1213: mapData);
1214: } else if (data instanceof Map) {
1215: return mapFactory.createSCOHashMap(owner, pm, sm, fmd,
1216: (Map) data);
1217:
1218: } else {
1219: throw BindingSupportImpl.getInstance().internal(
1220: "data is not MapData, MapEntries or MAp: "
1221: + fmd.getQName() + " data " + data);
1222: }
1223: }
1224:
1225: /**
1226: * If this is a collection, array or map and this field is true then all
1227: * data must be provided in the diff instance instead of just the changes
1228: * on commit or flush. This is used for datastores like VDS that always
1229: * write everything.
1230: */
1231: public boolean isIncludeAllDataInDiff() {
1232: return includeAllDataInDiff;
1233: }
1234:
1235: /**
1236: * Is this an artificial field created to hold some store specific
1237: * information (e.g. row version column values for a JDBC store)?
1238: */
1239: public boolean isFake() {
1240: return fake;
1241: }
1242:
1243: public void setScoFactory(Object factory) {
1244: this .scoFactory = factory;
1245: }
1246:
1247: public boolean checkCustomFactory() {
1248: if (scoFactory != null) {
1249: switch (category) {
1250: case MDStatics.CATEGORY_SIMPLE:
1251: simpleSCOFactory = (VersantSCOFactory) scoFactory;
1252: break;
1253: case MDStatics.CATEGORY_COLLECTION:
1254: collectionFactory = (VersantSCOCollectionFactory) scoFactory;
1255: break;
1256: case MDStatics.CATEGORY_MAP:
1257: mapFactory = (VersantSCOMapFactory) scoFactory;
1258: break;
1259: default:
1260: throw BindingSupportImpl.getInstance().runtime(
1261: "SCO factory '"
1262: + scoFactory.getClass().getName()
1263: + "' set on non SCO field. class: "
1264: + classMetaData.cls.getName()
1265: + " field: " + name);
1266: }
1267: return true;
1268: } else {
1269: return false;
1270: }
1271: }
1272:
1273: /**
1274: * Return the {@link Externalizer transformer} for this field.
1275: */
1276: public Externalizer getSerializer(VersantPersistenceManagerImp pm) {
1277: if (Debug.DEBUG) {
1278: if (category != MDStatics.CATEGORY_EXTERNALIZED) {
1279: throw BindingSupportImpl.getInstance().internal(
1280: "This field '" + name
1281: + "' is not a 'Serialized' field");
1282: }
1283: }
1284: return externalizer;
1285: }
1286:
1287: public boolean isEmbeddedRef() {
1288: return embedded && category == MDStatics.CATEGORY_REF;
1289: }
1290:
1291: /**
1292: * If this field is part of the primary key and this class uses application
1293: * identity then this is the corresponding field from the objectid-class.
1294: */
1295: public Field getObjectidClassField() {
1296: if (objectidClassField == null) {
1297: Class idClass = classMetaData.objectIdClass;
1298: Field field = getRField(idClass);
1299: if (field == null) {
1300: throw BindingSupportImpl.getInstance().runtime(
1301: "Application id class '" + idClass.getName()
1302: + "' must have field: 'public "
1303: + type.getName() + " " + name + "'");
1304: }
1305: if (!Modifier.isPublic(field.getModifiers())) {
1306: throw BindingSupportImpl.getInstance().runtime(
1307: "Application id class '" + idClass.getName()
1308: + "' field '" + name
1309: + "' is not public");
1310: }
1311:
1312: if (type != field.getType()) {
1313: throw BindingSupportImpl.getInstance().runtime(
1314: "Application id class '" + idClass.getName()
1315: + "' field '" + name
1316: + "' has wrong type '" + type.getName()
1317: + "' (should be " + type + ")");
1318: }
1319: objectidClassField = field;
1320: }
1321: return objectidClassField;
1322: }
1323:
1324: private Field getRField(Class idClass) {
1325: for (; idClass != null; idClass = idClass.getSuperclass()) {
1326: Field[] fields = idClass.getFields();
1327: Field ans = null;
1328: for (int i = 0; i < fields.length; i++) {
1329: Field field = fields[i];
1330: if (field.getName().equals(getPkFieldName())) {
1331: if (Modifier.isPublic(field.getModifiers()))
1332: return field;
1333: ans = field;
1334: }
1335:
1336: }
1337: if (ans != null)
1338: return ans;
1339: }
1340: return null;
1341: }
1342:
1343: public FieldMetaData findEmbeddedFmd(String name) {
1344: if (embeddedFmds == null) {
1345: return null;
1346: }
1347: // do a binary search since fields is sorted by name
1348: int low = 0;
1349: int high = embeddedFmds.length - 1;
1350: name = this .name + "/" + name;
1351: while (low <= high) {
1352: int mid = (low + high) / 2;
1353: FieldMetaData midVal = embeddedFmds[mid];
1354: String name2 = midVal.name;
1355: int cmp = name2.compareTo(name);
1356: if (cmp < 0) {
1357: low = mid + 1;
1358: } else if (cmp > 0) {
1359: high = mid - 1;
1360: } else {
1361: return midVal;
1362: }
1363: }
1364: return null;
1365: }
1366:
1367: public String getPkFieldName() {
1368: if (fake)
1369: return origFmd.name;
1370: return name;
1371: }
1372:
1373: public void setNullIndicatorFmd(FieldMetaData fmd) {
1374: if (isEmbeddedRef()) {
1375: if (nullIndicatorFmd == null) {
1376: nullIndicatorFmd = fmd;
1377: } else {
1378: throw BindingSupportImpl
1379: .getInstance()
1380: .invalidOperation(
1381: "Attempting to set more that one 'Null-Indicator' "
1382: + "field for Embedded field '"
1383: + classMetaData.qname
1384: + "."
1385: + name
1386: + "'. "
1387: + "Please correct the metadata.");
1388: }
1389: } else {
1390: throw BindingSupportImpl.getInstance().internal(
1391: "Not allowed to set NullIndicator '"
1392: + fmd.classMetaData.qname + "." + fmd.name
1393: + "' on a non-embedded ref: '"
1394: + classMetaData.qname + "." + name + "'");
1395: }
1396: }
1397: }
|