0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one
0003: * or more contributor license agreements. See the NOTICE file
0004: * distributed with this work for additional information
0005: * regarding copyright ownership. The ASF licenses this file
0006: * to you under the Apache License, Version 2.0 (the
0007: * "License"); you may not use this file except in compliance
0008: * with the License. You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing,
0013: * software distributed under the License is distributed on an
0014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015: * KIND, either express or implied. See the License for the
0016: * specific language governing permissions and limitations
0017: * under the License.
0018: */
0019: package org.apache.openjpa.meta;
0020:
0021: import java.io.Externalizable;
0022: import java.io.IOException;
0023: import java.io.ObjectInput;
0024: import java.io.ObjectOutput;
0025: import java.io.Serializable;
0026: import java.lang.reflect.Constructor;
0027: import java.lang.reflect.Field;
0028: import java.lang.reflect.InvocationTargetException;
0029: import java.lang.reflect.Member;
0030: import java.lang.reflect.Method;
0031: import java.lang.reflect.Modifier;
0032: import java.security.AccessController;
0033: import java.security.PrivilegedActionException;
0034: import java.util.ArrayList;
0035: import java.util.Calendar;
0036: import java.util.Collection;
0037: import java.util.Collections;
0038: import java.util.Comparator;
0039: import java.util.HashMap;
0040: import java.util.HashSet;
0041: import java.util.Iterator;
0042: import java.util.List;
0043: import java.util.Map;
0044: import java.util.Properties;
0045: import java.util.Set;
0046: import java.util.TimeZone;
0047:
0048: import org.apache.commons.collections.comparators.ComparatorChain;
0049: import org.apache.commons.lang.StringUtils;
0050: import org.apache.openjpa.conf.OpenJPAConfiguration;
0051: import org.apache.openjpa.kernel.OpenJPAStateManager;
0052: import org.apache.openjpa.kernel.StoreContext;
0053: import org.apache.openjpa.lib.conf.Configurations;
0054: import org.apache.openjpa.lib.log.Log;
0055: import org.apache.openjpa.lib.util.J2DoPrivHelper;
0056: import org.apache.openjpa.lib.util.JavaVersions;
0057: import org.apache.openjpa.lib.util.Localizer;
0058: import org.apache.openjpa.lib.util.Options;
0059: import org.apache.openjpa.lib.xml.Commentable;
0060: import org.apache.openjpa.util.Exceptions;
0061: import org.apache.openjpa.util.InternalException;
0062: import org.apache.openjpa.util.MetaDataException;
0063: import org.apache.openjpa.util.OpenJPAException;
0064: import org.apache.openjpa.util.UnsupportedException;
0065: import org.apache.openjpa.util.ImplHelper;
0066: import org.apache.openjpa.util.UserException;
0067:
0068: import serp.util.Strings;
0069:
0070: /**
0071: * Metadata for a managed class field.
0072: *
0073: * @author Abe White
0074: */
0075: public class FieldMetaData extends Extensions implements ValueMetaData,
0076: MetaDataContext, MetaDataModes, Commentable {
0077:
0078: /**
0079: * Constant specifying that no null-value was given.
0080: */
0081: public static final int NULL_UNSET = -1;
0082:
0083: /**
0084: * Constant specifying to use a datastore null to persist null values
0085: * in object fields.
0086: */
0087: public static final int NULL_NONE = 0;
0088:
0089: /**
0090: * Constant specifying to use a datastore default value to persist null
0091: * values in object fields.
0092: */
0093: public static final int NULL_DEFAULT = 1;
0094:
0095: /**
0096: * Constant specifying to throw an exception when attempting to persist
0097: * null values in object fields.
0098: */
0099: public static final int NULL_EXCEPTION = 2;
0100:
0101: /**
0102: * Constant specifying the management level of a field.
0103: */
0104: public static final int MANAGE_PERSISTENT = 3;
0105:
0106: /**
0107: * Constant specifying the management level of a field.
0108: */
0109: public static final int MANAGE_TRANSACTIONAL = 1;
0110:
0111: /**
0112: * Constant specifying the management level of a field.
0113: */
0114: public static final int MANAGE_NONE = 0;
0115:
0116: private static final Localizer _loc = Localizer
0117: .forPackage(FieldMetaData.class);
0118:
0119: private static final int DFG_FALSE = 1;
0120: private static final int DFG_TRUE = 2;
0121: private static final int DFG_EXPLICIT = 4;
0122:
0123: private static final Method DEFAULT_METHOD;
0124: static {
0125: try {
0126: DEFAULT_METHOD = Object.class.getMethod("wait",
0127: (Class[]) null);
0128: } catch (Exception e) {
0129: // shouldn't ever happen
0130: throw new InternalException(e);
0131: }
0132: }
0133:
0134: // name and type
0135: private final ValueMetaData _val;
0136: private final ValueMetaData _key;
0137: private final ValueMetaData _elem;
0138: private final ClassMetaData _owner;
0139: private final String _name;
0140: private Class _dec = null;
0141: private ClassMetaData _decMeta = null;
0142: private String _fullName = null;
0143: private String _embedFullName = null;
0144: private int _resMode = MODE_NONE;
0145:
0146: // load/store info
0147: private String[] _comments = null;
0148: private int _listIndex = -1;
0149:
0150: ////////////////////////////////////////////////////////////////////
0151: // Note: if you add additional state, make sure to add it to copy()
0152: ////////////////////////////////////////////////////////////////////
0153:
0154: // misc info
0155: private Class _proxyClass = null;
0156: private Object _initializer = null;
0157: private boolean _transient = false;
0158: private boolean _primKey = false;
0159: private Boolean _version = null;
0160: private int _nullValue = NULL_UNSET;
0161: private int _manage = MANAGE_PERSISTENT;
0162: private int _index = -1;
0163: private int _decIndex = -1;
0164: private int _pkIndex = -1;
0165: private boolean _explicit = false;
0166: private int _dfg = 0;
0167: private Set _fgSet = null;
0168: private String[] _fgs = null;
0169: private String _lfg = null;
0170: private Boolean _lrs = null;
0171: private Boolean _stream = null;
0172: private String _extName = null;
0173: private String _factName = null;
0174: private String _extString = null;
0175: private Map _extValues = Collections.EMPTY_MAP;
0176: private Map _fieldValues = Collections.EMPTY_MAP;
0177: private Boolean _enumField = null;
0178: private Boolean _lobField = null;
0179: private Boolean _serializableField = null;
0180: private boolean _generated = false;
0181:
0182: // Members aren't serializable. Use a proxy that can provide a Member
0183: // to avoid writing the full Externalizable implementation.
0184: private MemberProvider _backingMember = null;
0185:
0186: // Members aren't serializable. Initializing _extMethod and _factMethod to
0187: // DEFAULT_METHOD is sufficient to trigger lazy population of these fields.
0188: private transient Method _extMethod = DEFAULT_METHOD;
0189: private transient Member _factMethod = DEFAULT_METHOD;
0190:
0191: // intermediate and impl data
0192: private boolean _intermediate = true;
0193: private Boolean _implData = Boolean.TRUE;
0194:
0195: // value generation
0196: private int _valStrategy = -1;
0197: private int _upStrategy = -1;
0198: private String _seqName = ClassMetaData.DEFAULT_STRING;
0199: private SequenceMetaData _seqMeta = null;
0200:
0201: // inverses
0202: private String _mappedBy = null;
0203: private FieldMetaData _mappedByMeta = null;
0204: private FieldMetaData[] _inverses = null;
0205: private String _inverse = ClassMetaData.DEFAULT_STRING;
0206:
0207: // ordering on load
0208: private Order[] _orders = null;
0209: private String _orderDec = null;
0210: // indicate if this field is used by other field as "order by" value
0211: private boolean _usedInOrderBy = false;
0212:
0213: /**
0214: * Constructor.
0215: *
0216: * @param name the field name
0217: * @param type the field type
0218: * @param owner the owning class metadata
0219: */
0220: protected FieldMetaData(String name, Class type, ClassMetaData owner) {
0221: _name = name;
0222: _owner = owner;
0223: _dec = null;
0224: _decMeta = null;
0225: _val = owner.getRepository().newValueMetaData(this );
0226: _key = owner.getRepository().newValueMetaData(this );
0227: _elem = owner.getRepository().newValueMetaData(this );
0228:
0229: setDeclaredType(type);
0230: }
0231:
0232: /**
0233: * Supply the backing member object; this allows us to utilize
0234: * parameterized type information if available.
0235: */
0236: public void backingMember(Member member) {
0237: if (member == null)
0238: return;
0239: if (Modifier.isTransient(member.getModifiers()))
0240: _transient = true;
0241:
0242: _backingMember = new MemberProvider(member);
0243:
0244: Class type;
0245: Class[] types;
0246: if (member instanceof Field) {
0247: Field f = (Field) member;
0248: type = f.getType();
0249: types = JavaVersions.getParameterizedTypes(f);
0250: } else {
0251: Method meth = (Method) member;
0252: type = meth.getReturnType();
0253: types = JavaVersions.getParameterizedTypes(meth);
0254: }
0255:
0256: setDeclaredType(type);
0257: if (Collection.class.isAssignableFrom(type)
0258: && _elem.getDeclaredType() == Object.class
0259: && types.length == 1) {
0260: _elem.setDeclaredType(types[0]);
0261: } else if (Map.class.isAssignableFrom(type)
0262: && types.length == 2) {
0263: if (_key.getDeclaredType() == Object.class)
0264: _key.setDeclaredType(types[0]);
0265: if (_elem.getDeclaredType() == Object.class)
0266: _elem.setDeclaredType(types[1]);
0267: }
0268: }
0269:
0270: /**
0271: * Return the backing member supplied in {@link #backingMember}.
0272: */
0273: public Member getBackingMember() {
0274: return (_backingMember == null) ? null : _backingMember
0275: .getMember();
0276: }
0277:
0278: /**
0279: * The metadata repository.
0280: */
0281: public MetaDataRepository getRepository() {
0282: return _owner.getRepository();
0283: }
0284:
0285: /**
0286: * The class that defines the metadata for this field.
0287: */
0288: public ClassMetaData getDefiningMetaData() {
0289: return _owner;
0290: }
0291:
0292: /**
0293: * The declaring class.
0294: */
0295: public Class getDeclaringType() {
0296: return (_dec == null) ? _owner.getDescribedType() : _dec;
0297: }
0298:
0299: /**
0300: * The declaring class.
0301: */
0302: public void setDeclaringType(Class cls) {
0303: _dec = cls;
0304: _decMeta = null;
0305: _fullName = null;
0306: _embedFullName = null;
0307: }
0308:
0309: /**
0310: * The declaring class.
0311: */
0312: public ClassMetaData getDeclaringMetaData() {
0313: if (_dec == null)
0314: return _owner;
0315: if (_decMeta == null)
0316: _decMeta = getRepository().getMetaData(_dec,
0317: _owner.getEnvClassLoader(), true);
0318: return _decMeta;
0319: }
0320:
0321: /**
0322: * The field name.
0323: */
0324: public String getName() {
0325: return _name;
0326: }
0327:
0328: /**
0329: * The field name, qualified by the owning class.
0330: * @deprecated Use getFullName(boolean) instead.
0331: */
0332: public String getFullName() {
0333: return getFullName(false);
0334: }
0335:
0336: /**
0337: * The field name, qualified by the owning class and optionally the
0338: * embedding owner's name (if any).
0339: */
0340: public String getFullName(boolean embedOwner) {
0341: if (_fullName == null)
0342: _fullName = getDeclaringType().getName() + "." + _name;
0343: if (embedOwner && _embedFullName == null) {
0344: if (_owner.getEmbeddingMetaData() == null)
0345: _embedFullName = _fullName;
0346: else
0347: _embedFullName = _owner.getEmbeddingMetaData()
0348: .getFieldMetaData().getFullName(true)
0349: + "." + _fullName;
0350: }
0351: return (embedOwner) ? _embedFullName : _fullName;
0352: }
0353:
0354: /**
0355: * MetaData about the field value.
0356: */
0357: public ValueMetaData getValue() {
0358: return _val;
0359: }
0360:
0361: /**
0362: * Metadata about the key value.
0363: */
0364: public ValueMetaData getKey() {
0365: return _key;
0366: }
0367:
0368: /**
0369: * Metadata about the element value.
0370: */
0371: public ValueMetaData getElement() {
0372: return _elem;
0373: }
0374:
0375: /**
0376: * Return whether this field is mapped to the datastore. By default,
0377: * returns true for all persistent fields whose defining class is mapped.
0378: */
0379: public boolean isMapped() {
0380: return _manage == MANAGE_PERSISTENT && _owner.isMapped();
0381: }
0382:
0383: /**
0384: * The type this field was initialized with, and therefore the
0385: * type to use for proxies when loading data into this field.
0386: */
0387: public Class getProxyType() {
0388: return (_proxyClass == null) ? getDeclaredType() : _proxyClass;
0389: }
0390:
0391: /**
0392: * The type this field was initialized with, and therefore the
0393: * type to use for proxies when loading data into this field.
0394: */
0395: public void setProxyType(Class type) {
0396: _proxyClass = type;
0397: }
0398:
0399: /**
0400: * The initializer used by the field, or null if none. This
0401: * is additional information for initializing the field, such as
0402: * a custom {@link Comparator} used by a {@link Set} or
0403: * a {@link TimeZone} used by a {@link Calendar}.
0404: */
0405: public Object getInitializer() {
0406: return _initializer;
0407: }
0408:
0409: /**
0410: * The initializer used by the field, or null if none. This
0411: * is additional information for initializing the field, such as
0412: * a custom {@link Comparator} used by a {@link Set} or
0413: * a {@link TimeZone} used by a {@link Calendar}.
0414: */
0415: public void setInitializer(Object initializer) {
0416: _initializer = initializer;
0417: }
0418:
0419: /**
0420: * Return whether this is a transient field.
0421: */
0422: public boolean isTransient() {
0423: return _transient;
0424: }
0425:
0426: /**
0427: * Return whether this is a transient field.
0428: */
0429: public void setTransient(boolean trans) {
0430: _transient = trans;
0431: }
0432:
0433: /**
0434: * The absolute index of this persistent/transactional field.
0435: */
0436: public int getIndex() {
0437: return _index;
0438: }
0439:
0440: /**
0441: * The absolute index of this persistent/transactional field.
0442: */
0443: public void setIndex(int index) {
0444: _index = index;
0445: }
0446:
0447: /**
0448: * The relative index of this persistent/transactional field.
0449: */
0450: public int getDeclaredIndex() {
0451: return _decIndex;
0452: }
0453:
0454: /**
0455: * The relative index of this persistent/transactional field.
0456: */
0457: public void setDeclaredIndex(int index) {
0458: _decIndex = index;
0459: }
0460:
0461: /**
0462: * The index in which this field was listed in the metadata. Defaults to
0463: * <code>-1</code> if this field was not listed in the metadata.
0464: */
0465: public int getListingIndex() {
0466: return _listIndex;
0467: }
0468:
0469: /**
0470: * The index in which this field was listed in the metadata. Defaults to
0471: * <code>-1</code> if this field was not listed in the metadata.
0472: */
0473: public void setListingIndex(int index) {
0474: _listIndex = index;
0475: }
0476:
0477: /**
0478: * The absolute primary key index for this field, or -1 if not a primary
0479: * key. The first primary key field has index 0, the second index 1, etc.
0480: */
0481: public int getPrimaryKeyIndex() {
0482: return _pkIndex;
0483: }
0484:
0485: /**
0486: * The absolute primary key index for this field, or -1 if not a primary
0487: * key. The first primary key field has index 0, the second index 1, etc.
0488: */
0489: public void setPrimaryKeyIndex(int index) {
0490: _pkIndex = index;
0491: }
0492:
0493: /**
0494: * Return the management level for the field. Will be one of:
0495: * <ul>
0496: * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
0497: * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
0498: * persistent</li>
0499: * <li>{@link #MANAGE_NONE}: the field is not managed</li>
0500: * </ul> Defaults to {@link #MANAGE_PERSISTENT}.
0501: */
0502: public int getManagement() {
0503: return _manage;
0504: }
0505:
0506: /**
0507: * Return the management level for the field. Will be one of:
0508: * <ul>
0509: * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
0510: * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
0511: * persistent</li>
0512: * <li>{@link #MANAGE_NONE}: the field is not managed</li>
0513: * </ul>
0514: * Defaults to {@link #MANAGE_PERSISTENT}.
0515: */
0516: public void setManagement(int manage) {
0517: if ((_manage == MANAGE_NONE) != (manage == MANAGE_NONE))
0518: _owner.clearFieldCache();
0519: _manage = manage;
0520: }
0521:
0522: /**
0523: * Whether this is a primary key field.
0524: */
0525: public boolean isPrimaryKey() {
0526: return _primKey;
0527: }
0528:
0529: /**
0530: * Whether this is a primary key field.
0531: */
0532: public void setPrimaryKey(boolean primKey) {
0533: _primKey = primKey;
0534: }
0535:
0536: /**
0537: * For a primary key field, return the type of the corresponding object id
0538: * class field.
0539: */
0540: public int getObjectIdFieldTypeCode() {
0541: ClassMetaData relmeta = getDeclaredTypeMetaData();
0542: if (relmeta == null)
0543: return getDeclaredTypeCode();
0544: if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
0545: boolean unwrap = getRepository().getMetaDataFactory()
0546: .getDefaults().isDataStoreObjectIdFieldUnwrapped();
0547: return (unwrap) ? JavaTypes.LONG : JavaTypes.OBJECT;
0548: }
0549: if (relmeta.isOpenJPAIdentity())
0550: return relmeta.getPrimaryKeyFields()[0]
0551: .getObjectIdFieldTypeCode();
0552: return JavaTypes.OBJECT;
0553: }
0554:
0555: /**
0556: * For a primary key field, return the type of the corresponding object id
0557: * class field.
0558: */
0559: public Class getObjectIdFieldType() {
0560: ClassMetaData relmeta = getDeclaredTypeMetaData();
0561: if (relmeta == null)
0562: return getDeclaredType();
0563: switch (relmeta.getIdentityType()) {
0564: case ClassMetaData.ID_DATASTORE:
0565: boolean unwrap = getRepository().getMetaDataFactory()
0566: .getDefaults().isDataStoreObjectIdFieldUnwrapped();
0567: return (unwrap) ? long.class : Object.class;
0568: case ClassMetaData.ID_APPLICATION:
0569: if (relmeta.isOpenJPAIdentity())
0570: return relmeta.getPrimaryKeyFields()[0]
0571: .getObjectIdFieldType();
0572: return (relmeta.getObjectIdType() == null) ? Object.class
0573: : relmeta.getObjectIdType();
0574: default:
0575: return Object.class;
0576: }
0577: }
0578:
0579: /**
0580: * Whether this field holds optimistic version information.
0581: */
0582: public boolean isVersion() {
0583: return _version == Boolean.TRUE;
0584: }
0585:
0586: /**
0587: * Whether this field holds optimistic version information.
0588: */
0589: public void setVersion(boolean version) {
0590: _version = (version) ? Boolean.TRUE : Boolean.FALSE;
0591: }
0592:
0593: /**
0594: * Whether this field is in the default fetch group.
0595: */
0596: public boolean isInDefaultFetchGroup() {
0597: if (_dfg == 0) {
0598: if (_manage != MANAGE_PERSISTENT || isPrimaryKey()
0599: || isVersion())
0600: _dfg = DFG_FALSE;
0601: else {
0602: // field left as default; dfg setting depends on type
0603: switch (getTypeCode()) {
0604: case JavaTypes.OBJECT:
0605: if (isSerializable() || isEnum())
0606: _dfg = DFG_TRUE;
0607: else
0608: _dfg = DFG_FALSE;
0609: break;
0610: case JavaTypes.ARRAY:
0611: if (isLobArray())
0612: _dfg = DFG_TRUE;
0613: else
0614: _dfg = DFG_FALSE;
0615: break;
0616: case JavaTypes.COLLECTION:
0617: case JavaTypes.MAP:
0618: case JavaTypes.PC:
0619: case JavaTypes.PC_UNTYPED:
0620: _dfg = DFG_FALSE;
0621: break;
0622: default:
0623: _dfg = DFG_TRUE;
0624: }
0625: }
0626: }
0627: return (_dfg & DFG_TRUE) > 0;
0628: }
0629:
0630: private boolean isEnum() {
0631: if (_enumField == null) {
0632: Class dt = getDeclaredType();
0633: _enumField = JavaVersions.isEnumeration(dt) ? Boolean.TRUE
0634: : Boolean.FALSE;
0635: }
0636: return _enumField.booleanValue();
0637: }
0638:
0639: private boolean isSerializable() {
0640: if (_serializableField == null) {
0641: Class dt = getDeclaredType();
0642: if (Serializable.class.isAssignableFrom(dt))
0643: _serializableField = Boolean.TRUE;
0644: else
0645: _serializableField = Boolean.FALSE;
0646: }
0647: return _serializableField.booleanValue();
0648: }
0649:
0650: private boolean isLobArray() {
0651: // check for byte[], Byte[], char[], Character[]
0652: if (_lobField == null) {
0653: Class dt = getDeclaredType();
0654: if (dt == byte[].class || dt == Byte[].class
0655: || dt == char[].class || dt == Character[].class)
0656: _lobField = Boolean.TRUE;
0657: else
0658: _lobField = Boolean.FALSE;
0659: }
0660: return _lobField.booleanValue();
0661: }
0662:
0663: /**
0664: * Whether this field is in the default fetch group.
0665: */
0666: public void setInDefaultFetchGroup(boolean dfg) {
0667: if (dfg)
0668: _dfg = DFG_TRUE;
0669: else
0670: _dfg = DFG_FALSE;
0671: _dfg |= DFG_EXPLICIT;
0672: }
0673:
0674: /**
0675: * Whether the default fetch group setting is explicit.
0676: */
0677: public boolean isDefaultFetchGroupExplicit() {
0678: return (_dfg & DFG_EXPLICIT) > 0;
0679: }
0680:
0681: /**
0682: * Whether the default fetch group setting is explicit. Allow setting
0683: * for testing.
0684: */
0685: public void setDefaultFetchGroupExplicit(boolean explicit) {
0686: if (explicit)
0687: _dfg |= DFG_EXPLICIT;
0688: else
0689: _dfg &= ~DFG_EXPLICIT;
0690: }
0691:
0692: /**
0693: * Gets the name of the custom fetch groups those are associated to this
0694: * receiver. This does not include the "default" and "all" fetch groups.
0695: *
0696: * @return the set of fetch group names, not including the default and
0697: * all fetch groups.
0698: */
0699: public String[] getCustomFetchGroups() {
0700: if (_fgs == null) {
0701: if (_fgSet == null || _manage != MANAGE_PERSISTENT
0702: || isPrimaryKey() || isVersion())
0703: _fgs = new String[0];
0704: else
0705: _fgs = (String[]) _fgSet.toArray(new String[_fgSet
0706: .size()]);
0707: }
0708: return _fgs;
0709: }
0710:
0711: /**
0712: * The fetch group that is to be loaded when this receiver is loaded, or
0713: * null if none set.
0714: */
0715: public String getLoadFetchGroup() {
0716: return _lfg;
0717: }
0718:
0719: /**
0720: * The fetch group that is to be loaded when this receiver is loaded, or
0721: * null if none set.
0722: */
0723: public void setLoadFetchGroup(String lfg) {
0724: if ("".equals(lfg))
0725: lfg = null;
0726: _lfg = lfg;
0727: }
0728:
0729: /**
0730: * Whether this field is in the given fetch group.
0731: */
0732: public boolean isInFetchGroup(String fg) {
0733: if (_manage != MANAGE_PERSISTENT || isPrimaryKey()
0734: || isVersion())
0735: return false;
0736: if (FetchGroup.NAME_ALL.equals(fg))
0737: return true;
0738: if (FetchGroup.NAME_DEFAULT.equals(fg))
0739: return isInDefaultFetchGroup();
0740: return _fgSet != null && _fgSet.contains(fg);
0741: }
0742:
0743: /**
0744: * Set whether this field is in the given fetch group.
0745: *
0746: * @param fg is the name of a fetch group that must be present in the
0747: * class that declared this field or any of its persistent superclasses.
0748: */
0749: public void setInFetchGroup(String fg, boolean in) {
0750: if (StringUtils.isEmpty(fg))
0751: throw new MetaDataException(_loc.get("empty-fg-name", this ));
0752: if (fg.equals(FetchGroup.NAME_ALL))
0753: return;
0754: if (fg.equals(FetchGroup.NAME_DEFAULT)) {
0755: setInDefaultFetchGroup(in);
0756: return;
0757: }
0758: if (_owner.getFetchGroup(fg) == null)
0759: throw new MetaDataException(_loc
0760: .get("unknown-fg", fg, this ));
0761: if (in && _fgSet == null)
0762: _fgSet = new HashSet();
0763: if ((in && _fgSet.add(fg))
0764: || (!in && _fgSet != null && _fgSet.remove(fg)))
0765: _fgs = null;
0766: }
0767:
0768: /**
0769: * How the data store should treat null values for this field:
0770: * <ul>
0771: * <li>{@link #NULL_UNSET}: no value supplied</li>
0772: * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
0773: * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
0774: * at commit</li>
0775: * <li>{@link #NULL_DEFAULT}: use the database default if this field is
0776: * null at commit</li>
0777: * </ul> Defaults to {@link #NULL_UNSET}.
0778: */
0779: public int getNullValue() {
0780: return _nullValue;
0781: }
0782:
0783: /**
0784: * How the data store should treat null values for this field:
0785: * <ul>
0786: * <li>{@link #NULL_UNSET}: no value supplied</li>
0787: * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
0788: * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
0789: * at commit</li>
0790: * <li>{@link #NULL_DEFAULT}: use the database default if this field is
0791: * null at commit</li>
0792: * </ul> Defaults to {@link #NULL_UNSET}.
0793: */
0794: public void setNullValue(int nullValue) {
0795: _nullValue = nullValue;
0796: }
0797:
0798: /**
0799: * Whether this field is explicitly declared in the metadata.
0800: */
0801: public boolean isExplicit() {
0802: return _explicit;
0803: }
0804:
0805: /**
0806: * Whether this field is explicitly declared in the metadata.
0807: */
0808: public void setExplicit(boolean explicit) {
0809: _explicit = explicit;
0810: }
0811:
0812: /**
0813: * The field that this field shares a mapping with.
0814: */
0815: public String getMappedBy() {
0816: return _mappedBy;
0817: }
0818:
0819: /**
0820: * The field that this field shares a mapping with.
0821: */
0822: public void setMappedBy(String mapped) {
0823: _mappedBy = mapped;
0824: _mappedByMeta = null;
0825: }
0826:
0827: /**
0828: * The field that this field shares a mapping with.
0829: */
0830: public FieldMetaData getMappedByMetaData() {
0831: if (_mappedBy != null && _mappedByMeta == null) {
0832: ClassMetaData meta = null;
0833: switch (getTypeCode()) {
0834: case JavaTypes.PC:
0835: meta = getTypeMetaData();
0836: break;
0837: case JavaTypes.ARRAY:
0838: case JavaTypes.COLLECTION:
0839: case JavaTypes.MAP:
0840: meta = _elem.getTypeMetaData();
0841: break;
0842: }
0843:
0844: FieldMetaData field = (meta == null) ? null : meta
0845: .getField(_mappedBy);
0846: if (field == null)
0847: throw new MetaDataException(_loc.get("no-mapped-by",
0848: this , _mappedBy));
0849: if (field.getMappedBy() != null)
0850: throw new MetaDataException(_loc.get("circ-mapped-by",
0851: this , _mappedBy));
0852: _mappedByMeta = field;
0853: }
0854: return _mappedByMeta;
0855: }
0856:
0857: /**
0858: * Logical inverse field.
0859: */
0860: public String getInverse() {
0861: if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
0862: _inverse = null;
0863: return _inverse;
0864: }
0865:
0866: /**
0867: * Logical inverse field.
0868: */
0869: public void setInverse(String inverse) {
0870: _inverses = null;
0871: _inverse = inverse;
0872: }
0873:
0874: /**
0875: * Return all inverses of this field.
0876: */
0877: public FieldMetaData[] getInverseMetaDatas() {
0878: if (_inverses == null) {
0879: // can't declare both an inverse owner and a logical inverse
0880: String inv = getInverse();
0881: if (_mappedBy != null && inv != null
0882: && !_mappedBy.equals(inv))
0883: throw new MetaDataException(_loc.get(
0884: "mapped-not-inverse", this ));
0885:
0886: // get the metadata for the type on the other side of this relation
0887: ClassMetaData meta = null;
0888: switch (getTypeCode()) {
0889: case JavaTypes.PC:
0890: meta = getTypeMetaData();
0891: break;
0892: case JavaTypes.ARRAY:
0893: case JavaTypes.COLLECTION:
0894: meta = _elem.getTypeMetaData();
0895: break;
0896: }
0897:
0898: Collection inverses = null;
0899: if (meta != null) {
0900: // add mapped by and named inverse, if any
0901: FieldMetaData field = getMappedByMetaData();
0902: if (field != null) {
0903: // mapped by field isn't necessarily a pc type, but all
0904: // inverses must be
0905: if (field.getTypeCode() == JavaTypes.PC
0906: || field.getElement().getTypeCode() == JavaTypes.PC) {
0907: inverses = new ArrayList(3);
0908: inverses.add(field);
0909: }
0910: } else if (inv != null) {
0911: field = meta.getField(inv);
0912: if (field == null)
0913: throw new MetaDataException(_loc.get(
0914: "no-inverse", this , inv));
0915: inverses = new ArrayList(3);
0916: inverses.add(field);
0917: }
0918:
0919: // scan rel type for fields that name this field as an inverse
0920: FieldMetaData[] fields = meta.getFields();
0921: Class type = getDeclaringMetaData().getDescribedType();
0922: for (int i = 0; i < fields.length; i++) {
0923: // skip fields that aren't compatible with our owning class
0924: switch (fields[i].getTypeCode()) {
0925: case JavaTypes.PC:
0926: if (!type.isAssignableFrom(fields[i].getType()))
0927: continue;
0928: break;
0929: case JavaTypes.COLLECTION:
0930: case JavaTypes.ARRAY:
0931: if (!type.isAssignableFrom(fields[i]
0932: .getElement().getType()))
0933: continue;
0934: break;
0935: default:
0936: continue;
0937: }
0938:
0939: // if the field declares us as its inverse and we haven't
0940: // already added it (we might have if we also declared it
0941: // as our inverse), add it now
0942: if (_name.equals(fields[i].getMappedBy())
0943: || _name.equals(fields[i].getInverse())) {
0944: if (inverses == null)
0945: inverses = new ArrayList(3);
0946: if (!inverses.contains(fields[i]))
0947: inverses.add(fields[i]);
0948: }
0949: }
0950: }
0951:
0952: MetaDataRepository repos = getRepository();
0953: if (inverses == null)
0954: _inverses = repos.EMPTY_FIELDS;
0955: else
0956: _inverses = (FieldMetaData[]) inverses.toArray(repos
0957: .newFieldMetaDataArray(inverses.size()));
0958: }
0959: return _inverses;
0960: }
0961:
0962: /**
0963: * The strategy to use for insert value generation.
0964: * One of the constants from {@link ValueStrategies}.
0965: */
0966: public int getValueStrategy() {
0967: if (_valStrategy == -1)
0968: _valStrategy = ValueStrategies.NONE;
0969: return _valStrategy;
0970: }
0971:
0972: /**
0973: * The strategy to use for insert value generation.
0974: * One of the constants from {@link ValueStrategies}.
0975: */
0976: public void setValueStrategy(int strategy) {
0977: _valStrategy = strategy;
0978: if (strategy != ValueStrategies.SEQUENCE)
0979: setValueSequenceName(null);
0980: }
0981:
0982: /**
0983: * The value sequence name, or null for none.
0984: */
0985: public String getValueSequenceName() {
0986: if (ClassMetaData.DEFAULT_STRING.equals(_seqName))
0987: _seqName = null;
0988: return _seqName;
0989: }
0990:
0991: /**
0992: * The value sequence name, or null for none.
0993: */
0994: public void setValueSequenceName(String seqName) {
0995: _seqName = seqName;
0996: _seqMeta = null;
0997: if (seqName != null)
0998: setValueStrategy(ValueStrategies.SEQUENCE);
0999: }
1000:
1001: /**
1002: * Metadata for the value sequence.
1003: */
1004: public SequenceMetaData getValueSequenceMetaData() {
1005: if (_seqMeta == null && getValueSequenceName() != null)
1006: _seqMeta = getRepository().getSequenceMetaData(_owner,
1007: getValueSequenceName(), true);
1008: return _seqMeta;
1009: }
1010:
1011: /**
1012: * The strategy to use when updating the field.
1013: */
1014: public int getUpdateStrategy() {
1015: if (isVersion())
1016: return UpdateStrategies.RESTRICT;
1017: if (_upStrategy == -1)
1018: _upStrategy = UpdateStrategies.NONE;
1019: return _upStrategy;
1020: }
1021:
1022: /**
1023: * Set the update strategy.
1024: */
1025: public void setUpdateStrategy(int strategy) {
1026: _upStrategy = strategy;
1027: }
1028:
1029: /**
1030: * Whether this field is backed by a large result set.
1031: */
1032: public boolean isLRS() {
1033: return _lrs == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
1034: }
1035:
1036: /**
1037: * Whether this field is backed by a large result set.
1038: */
1039: public void setLRS(boolean lrs) {
1040: _lrs = (lrs) ? Boolean.TRUE : Boolean.FALSE;
1041: }
1042:
1043: /**
1044: * Whether this field is backed by a stream.
1045: *
1046: * @since 1.1.0
1047: */
1048: public boolean isStream() {
1049: return _stream == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
1050: }
1051:
1052: /**
1053: * Whether this field is backed by a stream.
1054: *
1055: * @since 1.1.0
1056: */
1057: public void setStream(boolean stream) {
1058: _stream = (stream) ? Boolean.TRUE : Boolean.FALSE;
1059: }
1060:
1061: /**
1062: * Whether this field uses intermediate data when loading/storing
1063: * information through a {@link OpenJPAStateManager}. Defaults to true.
1064: *
1065: * @see OpenJPAStateManager#setIntermediate(int,Object)
1066: */
1067: public boolean usesIntermediate() {
1068: return _intermediate;
1069: }
1070:
1071: /**
1072: * Whether this field uses intermediate data when loading/storing
1073: * information through a {@link OpenJPAStateManager}. Defaults to true.
1074: *
1075: * @see OpenJPAStateManager#setIntermediate(int,Object)
1076: */
1077: public void setUsesIntermediate(boolean intermediate) {
1078: _intermediate = intermediate;
1079: _owner.clearExtraFieldDataTable();
1080: }
1081:
1082: /**
1083: * Whether this field uses impl data in conjunction with standard
1084: * field data when acting on a {@link OpenJPAStateManager}.
1085: * Defaults to {@link Boolean#TRUE} (non-cachable impl data).
1086: *
1087: * @return {@link Boolean#FALSE} if this field does not use impl data,
1088: * {@link Boolean#TRUE} if this field uses non-cachable impl
1089: * data, or <code>null</code> if this field uses impl data that
1090: * should be cached across instances
1091: * @see OpenJPAStateManager#setImplData(int,Object)
1092: */
1093: public Boolean usesImplData() {
1094: return _implData;
1095: }
1096:
1097: /**
1098: * Whether this field uses impl data in conjunction with standard
1099: * field data when acting on a {@link OpenJPAStateManager}.
1100: *
1101: * @see OpenJPAStateManager#setImplData(int,Object)
1102: * @see #usesImplData
1103: */
1104: public void setUsesImplData(Boolean implData) {
1105: _implData = implData;
1106: _owner.clearExtraFieldDataTable();
1107: }
1108:
1109: /**
1110: * The orderings for this field to be applied on load, or empty array.
1111: */
1112: public Order[] getOrders() {
1113: if (_orders == null) {
1114: if (_orderDec == null)
1115: _orders = getRepository().EMPTY_ORDERS;
1116: else {
1117: String[] decs = Strings.split(_orderDec, ",", 0);
1118: Order[] orders = getRepository().newOrderArray(
1119: decs.length);
1120: int spc;
1121: boolean asc;
1122: for (int i = 0; i < decs.length; i++) {
1123: decs[i] = decs[i].trim();
1124: spc = decs[i].indexOf(' ');
1125: if (spc == -1)
1126: asc = true;
1127: else {
1128: asc = decs[i].substring(spc + 1).trim()
1129: .toLowerCase().startsWith("asc");
1130: decs[i] = decs[i].substring(0, spc);
1131: }
1132: orders[i] = getRepository().newOrder(this , decs[i],
1133: asc);
1134: //set "isUsedInOrderBy" to the field
1135: ClassMetaData elemCls = getElement()
1136: .getDeclaredTypeMetaData();
1137: FieldMetaData fmd = elemCls
1138: .getDeclaredField(decs[i]);
1139: if (fmd != null)
1140: fmd.setUsedInOrderBy(true);
1141: }
1142: _orders = orders;
1143: }
1144: }
1145: return _orders;
1146: }
1147:
1148: /**
1149: * The orderings for this field to be applied on load.
1150: */
1151: public void setOrders(Order[] orders) {
1152: _orderDec = null;
1153: _orders = orders;
1154: }
1155:
1156: /**
1157: * String declaring the orderings for this field to be applied on load,
1158: * or null. The string is of the form:<br />
1159: * <code>orderable[ asc|desc][, ...]</code><br />
1160: * The orderable <code>#element</code> is used to denote the value of
1161: * the field's elements.
1162: */
1163: public String getOrderDeclaration() {
1164: if (_orderDec == null && _orders != null) {
1165: StringBuffer buf = new StringBuffer();
1166: for (int i = 0; i < _orders.length; i++) {
1167: if (i > 0)
1168: buf.append(", ");
1169: buf.append(_orders[i].getName()).append(" ");
1170: buf.append((_orders[i].isAscending()) ? "asc" : "desc");
1171: }
1172: _orderDec = buf.toString();
1173: }
1174: return _orderDec;
1175: }
1176:
1177: /**
1178: * String declaring the orderings for this field to be applied on load,
1179: * or null. The string is of the form:<br />
1180: * <code>orderable[ asc|desc][, ...]</code><br />
1181: * The orderable <code>#element</code> is used to denote the value of
1182: * the field's elements.
1183: */
1184: public void setOrderDeclaration(String dec) {
1185: _orderDec = StringUtils.trimToNull(dec);
1186: _orders = null;
1187: }
1188:
1189: /**
1190: * Order this field value when it is loaded.
1191: */
1192: public Object order(Object val) {
1193: if (val == null)
1194: return null;
1195:
1196: Order[] orders = getOrders();
1197: if (orders.length == 0)
1198: return val;
1199:
1200: // create a comparator for the elements of the value
1201: Comparator comp;
1202: if (orders.length == 1)
1203: comp = orders[0].getComparator();
1204: else {
1205: List comps = null;
1206: Comparator curComp;
1207: for (int i = 0; i < orders.length; i++) {
1208: curComp = orders[i].getComparator();
1209: if (curComp != null) {
1210: if (comps == null)
1211: comps = new ArrayList(orders.length);
1212: if (i != comps.size())
1213: throw new MetaDataException(_loc.get(
1214: "mixed-inmem-ordering", this ));
1215: comps.add(curComp);
1216: }
1217: }
1218: if (comps == null)
1219: comp = null;
1220: else
1221: comp = new ComparatorChain(comps);
1222: }
1223:
1224: if (comp == null)
1225: return val;
1226:
1227: // sort
1228: switch (getTypeCode()) {
1229: case JavaTypes.ARRAY:
1230: List l = JavaTypes.toList(val, _elem.getType(), true);
1231: Collections.sort(l, comp);
1232: return JavaTypes.toArray(l, _elem.getType());
1233: case JavaTypes.COLLECTION:
1234: if (val instanceof List)
1235: Collections.sort((List) val, comp);
1236: return val;
1237: default:
1238: throw new MetaDataException(_loc.get("cant-order", this ));
1239: }
1240: }
1241:
1242: /**
1243: * Whether the field is externalized.
1244: */
1245: public boolean isExternalized() {
1246: return getExternalizerMethod() != null
1247: || getExternalValueMap() != null;
1248: }
1249:
1250: /**
1251: * Convert the given field value to its external value through the
1252: * provided externalizer, or return the value as-is if no externalizer.
1253: */
1254: public Object getExternalValue(Object val, StoreContext ctx) {
1255: Map extValues = getExternalValueMap();
1256: if (extValues != null) {
1257: Object foundVal = extValues.get(val);
1258: if (foundVal == null) {
1259: throw new UserException(_loc.get(
1260: "bad-externalized-value", new Object[] { val,
1261: extValues.keySet(), this })).setFatal(
1262: true).setFailedObject(val);
1263: } else {
1264: return foundVal;
1265: }
1266: }
1267:
1268: Method externalizer = getExternalizerMethod();
1269: if (externalizer == null)
1270: return val;
1271:
1272: // special case for queries: allow the given value to pass through
1273: // as-is if it is already in externalized form
1274: if (val != null
1275: && getType().isInstance(val)
1276: && (!getDeclaredType().isInstance(val) || getDeclaredType() == Object.class))
1277: return val;
1278:
1279: try {
1280: // either invoke the static toExternal(val[, ctx]) method, or the
1281: // non-static val.toExternal([ctx]) method
1282: if (Modifier.isStatic(externalizer.getModifiers())) {
1283: if (externalizer.getParameterTypes().length == 1)
1284: return externalizer.invoke(null,
1285: new Object[] { val });
1286: return externalizer.invoke(null, new Object[] { val,
1287: ctx });
1288: }
1289: if (val == null)
1290: return null;
1291: if (externalizer.getParameterTypes().length == 0)
1292: return externalizer.invoke(val, (Object[]) null);
1293: return externalizer.invoke(val, new Object[] { ctx });
1294: } catch (OpenJPAException ke) {
1295: throw ke;
1296: } catch (Exception e) {
1297: throw new MetaDataException(_loc.get("externalizer-err",
1298: this , Exceptions.toString(val), e.toString()))
1299: .setCause(e);
1300: }
1301: }
1302:
1303: /**
1304: * Return the result of passing the given external value through the
1305: * factory to get the field value. If no factory is present,
1306: * the given value is returned as-is.
1307: */
1308: public Object getFieldValue(Object val, StoreContext ctx) {
1309: Map fieldValues = getFieldValueMap();
1310: if (fieldValues != null)
1311: return fieldValues.get(val);
1312:
1313: Member factory = getFactoryMethod();
1314: if (factory == null)
1315: return val;
1316:
1317: try {
1318: if (val == null && getNullValue() == NULL_DEFAULT)
1319: return AccessController.doPrivileged(J2DoPrivHelper
1320: .newInstanceAction(getDeclaredType()));
1321:
1322: // invoke either the constructor for the field type,
1323: // or the static type.toField(val[, ctx]) method
1324: if (factory instanceof Constructor) {
1325: if (val == null)
1326: return null;
1327: return ((Constructor) factory)
1328: .newInstance(new Object[] { val });
1329: }
1330:
1331: Method meth = (Method) factory;
1332: if (meth.getParameterTypes().length == 1)
1333: return meth.invoke(null, new Object[] { val });
1334: return meth.invoke(null, new Object[] { val, ctx });
1335: } catch (Exception e) {
1336: // unwrap cause
1337: if (e instanceof InvocationTargetException) {
1338: Throwable t = ((InvocationTargetException) e)
1339: .getTargetException();
1340: if (t instanceof Error)
1341: throw (Error) t;
1342: e = (Exception) t;
1343:
1344: // allow null values to cause NPEs and illegal arg exceptions
1345: // without error
1346: if (val == null
1347: && (e instanceof NullPointerException || e instanceof IllegalArgumentException))
1348: return null;
1349: }
1350:
1351: if (e instanceof OpenJPAException)
1352: throw (OpenJPAException) e;
1353: if (e instanceof PrivilegedActionException)
1354: e = ((PrivilegedActionException) e).getException();
1355: throw new MetaDataException(_loc.get("factory-err", this ,
1356: Exceptions.toString(val), e.toString()))
1357: .setCause(e);
1358: }
1359: }
1360:
1361: /**
1362: * The name of this field's externalizer, or null if none.
1363: */
1364: public String getExternalizer() {
1365: return _extName;
1366: }
1367:
1368: /**
1369: * The name of this field's externalizer, or null if none.
1370: */
1371: public void setExternalizer(String externalizer) {
1372: _extName = externalizer;
1373: _extMethod = DEFAULT_METHOD;
1374: }
1375:
1376: /**
1377: * The name of this field's factory, or null if none.
1378: */
1379: public String getFactory() {
1380: return _factName;
1381: }
1382:
1383: /**
1384: * The name of this field's factory, or null if none.
1385: */
1386: public void setFactory(String factory) {
1387: _factName = factory;
1388: _factMethod = DEFAULT_METHOD;
1389: }
1390:
1391: /**
1392: * Properties string mapping field values to external values.
1393: */
1394: public String getExternalValues() {
1395: return _extString;
1396: }
1397:
1398: /**
1399: * Properties string mapping field values to external values.
1400: */
1401: public void setExternalValues(String values) {
1402: _extString = values;
1403: _extValues = null;
1404: }
1405:
1406: /**
1407: * Return the mapping of field values to external values.
1408: */
1409: public Map getExternalValueMap() {
1410: parseExternalValues();
1411: return _extValues;
1412: }
1413:
1414: /**
1415: * Return the mapping of external values to field values.
1416: */
1417: public Map getFieldValueMap() {
1418: parseExternalValues();
1419: return _fieldValues;
1420: }
1421:
1422: /**
1423: * Parse external values into maps.
1424: */
1425: private void parseExternalValues() {
1426: if (_extValues != Collections.EMPTY_MAP
1427: && _fieldValues != Collections.EMPTY_MAP)
1428: return;
1429:
1430: if (_extString == null) {
1431: _extValues = null;
1432: _fieldValues = null;
1433: return;
1434: }
1435:
1436: // parse string into options; this takes care of proper trimming etc
1437: Options values = Configurations.parseProperties(_extString);
1438: if (values.isEmpty())
1439: throw new MetaDataException(_loc.get("no-external-values",
1440: this , _extString));
1441:
1442: Map extValues = new HashMap((int) (values.size() * 1.33 + 1));
1443: Map fieldValues = new HashMap((int) (values.size() * 1.33 + 1));
1444: Map.Entry entry;
1445: Object extValue, fieldValue;
1446: for (Iterator itr = values.entrySet().iterator(); itr.hasNext();) {
1447: entry = (Map.Entry) itr.next();
1448: fieldValue = transform((String) entry.getKey(),
1449: getDeclaredTypeCode());
1450: extValue = transform((String) entry.getValue(),
1451: getTypeCode());
1452:
1453: extValues.put(fieldValue, extValue);
1454: fieldValues.put(extValue, fieldValue);
1455: }
1456:
1457: _extValues = extValues;
1458: _fieldValues = fieldValues;
1459: }
1460:
1461: /**
1462: * Return the string value converted to the given type code. The string
1463: * must be non-null and trimmed.
1464: */
1465: private Object transform(String val, int typeCode) {
1466: if ("null".equals(val))
1467: return null;
1468:
1469: switch (typeCode) {
1470: case JavaTypes.BOOLEAN:
1471: case JavaTypes.BOOLEAN_OBJ:
1472: return Boolean.valueOf(val);
1473: case JavaTypes.BYTE:
1474: case JavaTypes.BYTE_OBJ:
1475: return Byte.valueOf(val);
1476: case JavaTypes.INT:
1477: case JavaTypes.INT_OBJ:
1478: return Integer.valueOf(val);
1479: case JavaTypes.LONG:
1480: case JavaTypes.LONG_OBJ:
1481: return Long.valueOf(val);
1482: case JavaTypes.SHORT:
1483: case JavaTypes.SHORT_OBJ:
1484: return Short.valueOf(val);
1485: case JavaTypes.DOUBLE:
1486: case JavaTypes.DOUBLE_OBJ:
1487: return Double.valueOf(val);
1488: case JavaTypes.FLOAT:
1489: case JavaTypes.FLOAT_OBJ:
1490: return Float.valueOf(val);
1491: case JavaTypes.CHAR:
1492: case JavaTypes.CHAR_OBJ:
1493: return new Character(val.charAt(0));
1494: case JavaTypes.STRING:
1495: return val;
1496: }
1497: throw new MetaDataException(_loc.get("bad-external-type", this ));
1498: }
1499:
1500: /**
1501: * The externalizer method.
1502: */
1503: public Method getExternalizerMethod() {
1504: if (_manage != MANAGE_PERSISTENT)
1505: return null;
1506: if (_extMethod == DEFAULT_METHOD) {
1507: if (_extName != null) {
1508: _extMethod = findMethod(_extName);
1509: if (_extMethod == null)
1510: throw new MetaDataException(_loc.get(
1511: "bad-externalizer", this , _extName));
1512: } else
1513: _extMethod = null;
1514: }
1515: return _extMethod;
1516: }
1517:
1518: /**
1519: * The factory method or constructor.
1520: */
1521: public Member getFactoryMethod() {
1522: if (_manage != MANAGE_PERSISTENT)
1523: return null;
1524: if (_factMethod == DEFAULT_METHOD) {
1525: if (getExternalizerMethod() == null)
1526: _factMethod = null;
1527: else {
1528: try {
1529: if (_factName == null)
1530: _factMethod = getDeclaredType().getConstructor(
1531: new Class[] { getType() });
1532: else
1533: _factMethod = findMethod(_factName);
1534: } catch (OpenJPAException ke) {
1535: throw ke;
1536: } catch (Exception e) {
1537: }
1538:
1539: if (!(_factMethod instanceof Constructor)
1540: && !(_factMethod instanceof Method))
1541: throw new MetaDataException(_loc.get("bad-factory",
1542: this ));
1543: }
1544: }
1545: return _factMethod;
1546: }
1547:
1548: /**
1549: * Find the method for the specified name. Possible forms are:
1550: * <ul>
1551: * <li>toExternalString</li>
1552: * <li>MyFactoryClass.toExternalString</li>
1553: * <li>com.company.MyFactoryClass.toExternalString</li>
1554: * </ul>
1555: *
1556: * @param method the name of the method to locate
1557: * @return the method for invocation
1558: */
1559: private Method findMethod(String method) {
1560: if (StringUtils.isEmpty(method))
1561: return null;
1562:
1563: // get class name and get package name divide on the last '.', so the
1564: // names don't apply in this case, but the methods do what we want
1565: String methodName = Strings.getClassName(method);
1566: String clsName = Strings.getPackageName(method);
1567:
1568: Class cls = null;
1569: Class owner = _owner.getDescribedType();
1570:
1571: if (clsName.length() == 0)
1572: cls = getDeclaredType();
1573: else if (clsName.equals(owner.getName())
1574: || clsName.equals(Strings.getClassName(owner)))
1575: cls = owner;
1576: else
1577: cls = JavaTypes.classForName(clsName, this );
1578:
1579: // find the named method
1580: Method[] methods = cls.getMethods();
1581: Class[] params;
1582: for (int i = 0; i < methods.length; i++) {
1583: if (methods[i].getName().equals(methodName)) {
1584: params = methods[i].getParameterTypes();
1585:
1586: // static factory methods require one argument or one argument
1587: // plus a ctx; non-static methods require zero arguments or
1588: // just a ctx
1589: if (Modifier.isStatic(methods[i].getModifiers())
1590: && (params.length == 1 || (params.length == 2 && isStoreContextParameter(params[1]))))
1591: return methods[i];
1592: if (!Modifier.isStatic(methods[i].getModifiers())
1593: && (params.length == 0 || (params.length == 1 && isStoreContextParameter(params[0]))))
1594: return methods[i];
1595: }
1596: }
1597:
1598: return null;
1599: }
1600:
1601: /**
1602: * Return true if the given type is a store context type; we can't
1603: * use the standard <code>isAssignableFrom</code> because of classloader
1604: * oddness.
1605: */
1606: private static boolean isStoreContextParameter(Class type) {
1607: return StoreContext.class.getName().equals(type.getName());
1608: }
1609:
1610: public boolean equals(Object other) {
1611: if (other == this )
1612: return true;
1613: if (!(other instanceof FieldMetaData))
1614: return false;
1615: return getFullName(true).equals(
1616: ((FieldMetaData) other).getFullName(true));
1617: }
1618:
1619: public int hashCode() {
1620: return getFullName(true).hashCode();
1621: }
1622:
1623: public int compareTo(Object other) {
1624: if (other == null)
1625: return 1;
1626: return getFullName(true).compareTo(
1627: ((FieldMetaData) other).getFullName(true));
1628: }
1629:
1630: public String toString() {
1631: return getFullName(true);
1632: }
1633:
1634: ////////////////////////
1635: // Resolve and validate
1636: ////////////////////////
1637:
1638: /**
1639: * Resolve mode for this field.
1640: */
1641: public int getResolve() {
1642: return _resMode;
1643: }
1644:
1645: /**
1646: * Resolve mode for this field.
1647: */
1648: public void setResolve(int mode) {
1649: _resMode = mode;
1650: }
1651:
1652: /**
1653: * Resolve mode for this field.
1654: */
1655: public void setResolve(int mode, boolean on) {
1656: if (mode == MODE_NONE)
1657: _resMode = mode;
1658: else if (on)
1659: _resMode |= mode;
1660: else
1661: _resMode &= ~mode;
1662: }
1663:
1664: /**
1665: * Resolve and validate metadata. Return true if already resolved.
1666: */
1667: public boolean resolve(int mode) {
1668: if ((_resMode & mode) == mode)
1669: return true;
1670: int cur = _resMode;
1671: _resMode |= mode;
1672:
1673: Log log = getRepository().getLog();
1674: if (log.isTraceEnabled())
1675: log.trace(_loc.get("resolve-field", _owner + "@"
1676: + System.identityHashCode(_owner) + "." + _name));
1677:
1678: // we only perform actions for metadata mode
1679: if ((mode & MODE_META) == 0 || (cur & MODE_META) != 0)
1680: return false;
1681:
1682: Method externalizer = getExternalizerMethod();
1683: if (externalizer != null)
1684: setType(externalizer.getReturnType());
1685:
1686: // only pass on metadata resolve mode so that metadata is always
1687: // resolved before any other resolve modes our subclasses pass along
1688: _val.resolve(MODE_META);
1689: _key.resolve(MODE_META);
1690: _elem.resolve(MODE_META);
1691:
1692: MetaDataRepository repos = getRepository();
1693: int validate = repos.getValidate();
1694: if ((validate & MetaDataRepository.VALIDATE_META) != 0
1695: && (!ImplHelper.isManagedType(repos.getConfiguration(),
1696: _owner.getDescribedType()) || (validate & MetaDataRepository.VALIDATE_UNENHANCED) == 0)) {
1697: validateLRS();
1698: if ((validate & repos.VALIDATE_RUNTIME) == 0)
1699: validateSupportedType();
1700: validateValue();
1701: validateExtensionKeys();
1702: }
1703: return false;
1704: }
1705:
1706: /**
1707: * Validate that this field can be used for LRS.
1708: */
1709: private void validateLRS() {
1710: if (!isLRS())
1711: return;
1712:
1713: // can't use lrs for arrays
1714: if (getTypeCode() == JavaTypes.ARRAY)
1715: throw new MetaDataException(_loc.get("bad-lrs-array", this ));
1716:
1717: // can't use lrs for extranalized vals
1718: if (getExternalizerMethod() != null)
1719: throw new MetaDataException(_loc
1720: .get("bad-lrs-extern", this ));
1721:
1722: // can't use lrs for concrete types
1723: if (getType() != Collection.class && getType() != Map.class
1724: && getType() != Set.class)
1725: throw new MetaDataException(_loc.get("bad-lrs-concrete",
1726: this ));
1727: }
1728:
1729: /**
1730: * Validate that this field is supported by the runtime.
1731: */
1732: private void validateSupportedType() {
1733: // log warnings about things we don't handle
1734: OpenJPAConfiguration conf = getRepository().getConfiguration();
1735: Collection opts = conf.supportedOptions();
1736: Log log = conf.getLog(conf.LOG_METADATA);
1737: switch (getTypeCode()) {
1738: case JavaTypes.PC:
1739: if (isEmbedded()
1740: && !opts.contains(conf.OPTION_EMBEDDED_RELATION)) {
1741: setEmbedded(false);
1742: if (log.isWarnEnabled())
1743: log.warn(_loc.get("cant-embed", this ));
1744: } else if (isEmbedded()
1745: && getDeclaredTypeCode() != JavaTypes.PC) {
1746: setEmbedded(false);
1747: if (log.isWarnEnabled())
1748: log.warn(_loc.get("cant-embed-extern", this ));
1749: }
1750: break;
1751: case JavaTypes.COLLECTION:
1752: if (!opts.contains(conf.OPTION_TYPE_COLLECTION))
1753: throw new UnsupportedException(_loc.get(
1754: "type-not-supported", "Collection", this ));
1755: if (_elem.isEmbeddedPC()
1756: && !opts
1757: .contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION)) {
1758: _elem.setEmbedded(false);
1759: if (log.isWarnEnabled())
1760: log.warn(_loc.get("cant-embed-element", this ));
1761: }
1762: break;
1763: case JavaTypes.ARRAY:
1764: if (!opts.contains(conf.OPTION_TYPE_ARRAY))
1765: throw new UnsupportedException(_loc.get(
1766: "type-not-supported", "Array", this ));
1767: if (_elem.isEmbeddedPC()
1768: && !opts
1769: .contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION)) {
1770: _elem.setEmbedded(false);
1771: if (log.isWarnEnabled())
1772: log.warn(_loc.get("cant-embed-element", this ));
1773: }
1774: break;
1775: case JavaTypes.MAP:
1776: if (!opts.contains(conf.OPTION_TYPE_MAP))
1777: throw new UnsupportedException(_loc.get(
1778: "type-not-supported", "Map", this ));
1779: if (_elem.isEmbeddedPC()
1780: && !opts
1781: .contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
1782: _elem.setEmbedded(false);
1783: if (log.isWarnEnabled())
1784: log.warn(_loc.get("cant-embed-element", this ));
1785: }
1786: if (_key.isEmbeddedPC()
1787: && !opts
1788: .contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
1789: _key.setEmbedded(false);
1790: if (log.isWarnEnabled())
1791: log.warn(_loc.get("cant-embed-key", this ));
1792: }
1793: break;
1794: }
1795: }
1796:
1797: /**
1798: * Validate our value strategy.
1799: */
1800: private void validateValue() {
1801: if (getExternalizerMethod() != null
1802: && getExternalValueMap() != null)
1803: throw new MetaDataException(_loc.get("extern-externvalues",
1804: this ));
1805: if (getValueStrategy() == ValueStrategies.SEQUENCE
1806: && getValueSequenceName() == null)
1807: throw new MetaDataException(_loc.get("no-seq-name", this ));
1808: ValueStrategies.assertSupported(getValueStrategy(), this ,
1809: "value strategy");
1810: }
1811:
1812: /**
1813: * Copy state from the given field to this one. Do not copy mapping
1814: * information.
1815: */
1816: public void copy(FieldMetaData field) {
1817: super .copy(field);
1818:
1819: _intermediate = field.usesIntermediate();
1820: _implData = field.usesImplData();
1821:
1822: // copy field-level info; use get methods to force resolution of
1823: // lazy data
1824: _proxyClass = field.getProxyType();
1825: _initializer = field.getInitializer();
1826: _transient = field.isTransient();
1827: _nullValue = field.getNullValue();
1828: _manage = field.getManagement();
1829: _explicit = field.isExplicit();
1830: _extName = field.getExternalizer();
1831: _extMethod = DEFAULT_METHOD;
1832: _factName = field.getFactory();
1833: _factMethod = DEFAULT_METHOD;
1834: _extString = field.getExternalValues();
1835: _extValues = Collections.EMPTY_MAP;
1836: _fieldValues = Collections.EMPTY_MAP;
1837: _primKey = field.isPrimaryKey();
1838: _backingMember = field._backingMember;
1839: _enumField = field._enumField;
1840: _lobField = field._lobField;
1841: _serializableField = field._serializableField;
1842: _generated = field._generated;
1843:
1844: // embedded fields can't be versions
1845: if (_owner.getEmbeddingMetaData() == null && _version == null)
1846: _version = (field.isVersion()) ? Boolean.TRUE
1847: : Boolean.FALSE;
1848:
1849: // only copy this data if not already set explicitly in this instance
1850: if (_dfg == 0) {
1851: _dfg = (field.isInDefaultFetchGroup()) ? DFG_TRUE
1852: : DFG_FALSE;
1853: if (field.isDefaultFetchGroupExplicit())
1854: _dfg |= DFG_EXPLICIT;
1855: }
1856: if (_fgSet == null && field._fgSet != null)
1857: _fgSet = new HashSet(field._fgSet);
1858: if (_lfg == null)
1859: _lfg = field.getLoadFetchGroup();
1860: if (_lrs == null)
1861: _lrs = (field.isLRS()) ? Boolean.TRUE : Boolean.FALSE;
1862: if (_valStrategy == -1)
1863: _valStrategy = field.getValueStrategy();
1864: if (_upStrategy == -1)
1865: _upStrategy = field.getUpdateStrategy();
1866: if (ClassMetaData.DEFAULT_STRING.equals(_seqName)) {
1867: _seqName = field.getValueSequenceName();
1868: _seqMeta = null;
1869: }
1870: if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
1871: _inverse = field.getInverse();
1872:
1873: // copy value metadata
1874: _val.copy(field);
1875: _key.copy(field.getKey());
1876: _elem.copy(field.getElement());
1877: }
1878:
1879: protected void addExtensionKeys(Collection exts) {
1880: getRepository().getMetaDataFactory()
1881: .addFieldExtensionKeys(exts);
1882: }
1883:
1884: ///////////////
1885: // Commentable
1886: ///////////////
1887:
1888: public String[] getComments() {
1889: return (_comments == null) ? EMPTY_COMMENTS : _comments;
1890: }
1891:
1892: public void setComments(String[] comments) {
1893: _comments = comments;
1894: }
1895:
1896: ////////////////////////////////
1897: // ValueMetaData implementation
1898: ////////////////////////////////
1899:
1900: public FieldMetaData getFieldMetaData() {
1901: return this ;
1902: }
1903:
1904: public Class getType() {
1905: return _val.getType();
1906: }
1907:
1908: public void setType(Class type) {
1909: _val.setType(type);
1910: if (type.isArray())
1911: _elem.setType(type.getComponentType());
1912: else if (type == Properties.class) {
1913: _key.setType(String.class);
1914: _elem.setType(String.class);
1915: }
1916: }
1917:
1918: public int getTypeCode() {
1919: return _val.getTypeCode();
1920: }
1921:
1922: public void setTypeCode(int code) {
1923: _val.setTypeCode(code);
1924: }
1925:
1926: public boolean isTypePC() {
1927: return _val.isTypePC();
1928: }
1929:
1930: public ClassMetaData getTypeMetaData() {
1931: return _val.getTypeMetaData();
1932: }
1933:
1934: public Class getDeclaredType() {
1935: return _val.getDeclaredType();
1936: }
1937:
1938: public void setDeclaredType(Class type) {
1939: _val.setDeclaredType(type);
1940: if (type.isArray())
1941: _elem.setDeclaredType(type.getComponentType());
1942: else if (type == Properties.class) {
1943: _key.setDeclaredType(String.class);
1944: _elem.setDeclaredType(String.class);
1945: }
1946: }
1947:
1948: public int getDeclaredTypeCode() {
1949: return _val.getDeclaredTypeCode();
1950: }
1951:
1952: public void setDeclaredTypeCode(int type) {
1953: _val.setDeclaredTypeCode(type);
1954: }
1955:
1956: public boolean isDeclaredTypePC() {
1957: return _val.isDeclaredTypePC();
1958: }
1959:
1960: public ClassMetaData getDeclaredTypeMetaData() {
1961: return _val.getDeclaredTypeMetaData();
1962: }
1963:
1964: public boolean isEmbedded() {
1965: return _val.isEmbedded();
1966: }
1967:
1968: public void setEmbedded(boolean embedded) {
1969: _val.setEmbedded(embedded);
1970: }
1971:
1972: public boolean isEmbeddedPC() {
1973: return _val.isEmbeddedPC();
1974: }
1975:
1976: public ClassMetaData getEmbeddedMetaData() {
1977: return _val.getEmbeddedMetaData();
1978: }
1979:
1980: public ClassMetaData addEmbeddedMetaData() {
1981: return _val.addEmbeddedMetaData();
1982: }
1983:
1984: public int getCascadeDelete() {
1985: return _val.getCascadeDelete();
1986: }
1987:
1988: public void setCascadeDelete(int delete) {
1989: _val.setCascadeDelete(delete);
1990: }
1991:
1992: public int getCascadePersist() {
1993: return _val.getCascadePersist();
1994: }
1995:
1996: public void setCascadePersist(int persist) {
1997: _val.setCascadePersist(persist);
1998: }
1999:
2000: public int getCascadeAttach() {
2001: return _val.getCascadeAttach();
2002: }
2003:
2004: public void setCascadeAttach(int attach) {
2005: _val.setCascadeAttach(attach);
2006: }
2007:
2008: public int getCascadeRefresh() {
2009: return _val.getCascadeRefresh();
2010: }
2011:
2012: public void setCascadeRefresh(int refresh) {
2013: _val.setCascadeRefresh(refresh);
2014: }
2015:
2016: public boolean isSerialized() {
2017: return _val.isSerialized();
2018: }
2019:
2020: public void setSerialized(boolean serialized) {
2021: _val.setSerialized(serialized);
2022: }
2023:
2024: public String getValueMappedBy() {
2025: return _val.getValueMappedBy();
2026: }
2027:
2028: public void setValueMappedBy(String mapped) {
2029: _val.setValueMappedBy(mapped);
2030: }
2031:
2032: public FieldMetaData getValueMappedByMetaData() {
2033: return _val.getValueMappedByMetaData();
2034: }
2035:
2036: public Class getTypeOverride() {
2037: return _val.getTypeOverride();
2038: }
2039:
2040: public void setTypeOverride(Class type) {
2041: _val.setTypeOverride(type);
2042: }
2043:
2044: public void copy(ValueMetaData vmd) {
2045: _val.copy(vmd);
2046: }
2047:
2048: /**
2049: * Check if this field is used by other field as "order by" value.
2050: *
2051: * @since 1.1.0
2052: */
2053: public boolean isUsedInOrderBy() {
2054: return _usedInOrderBy;
2055: }
2056:
2057: /**
2058: * Whether this field is used by other field as "order by" value .
2059: *
2060: * @since 1.1.0
2061: */
2062: public void setUsedInOrderBy(boolean isUsed) {
2063: _usedInOrderBy = isUsed;
2064: }
2065:
2066: /**
2067: * Serializable wrapper around a {@link Method} or {@link Field}. For
2068: * space considerations, this does not support {@link Constructor}s.
2069: */
2070: public static class MemberProvider implements Externalizable {
2071:
2072: private transient Member _member;
2073:
2074: public MemberProvider() {
2075: // for externalization
2076: }
2077:
2078: MemberProvider(Member member) {
2079: if (member instanceof Constructor)
2080: throw new IllegalArgumentException();
2081:
2082: _member = member;
2083: }
2084:
2085: public Member getMember() {
2086: return _member;
2087: }
2088:
2089: public void readExternal(ObjectInput in) throws IOException,
2090: ClassNotFoundException {
2091: boolean isField = in.readBoolean();
2092: Class cls = (Class) in.readObject();
2093: String memberName = (String) in.readObject();
2094: try {
2095: if (isField)
2096: _member = (Field) AccessController
2097: .doPrivileged(J2DoPrivHelper
2098: .getDeclaredFieldAction(cls,
2099: memberName));
2100: else {
2101: Class[] parameterTypes = (Class[]) in.readObject();
2102: _member = (Method) AccessController
2103: .doPrivileged(J2DoPrivHelper
2104: .getDeclaredMethodAction(cls,
2105: memberName, parameterTypes));
2106: }
2107: } catch (SecurityException e) {
2108: IOException ioe = new IOException(e.getMessage());
2109: ioe.initCause(e);
2110: throw ioe;
2111: } catch (PrivilegedActionException pae) {
2112: IOException ioe = new IOException(pae.getException()
2113: .getMessage());
2114: ioe.initCause(pae);
2115: throw ioe;
2116: }
2117: }
2118:
2119: public void writeExternal(ObjectOutput out) throws IOException {
2120: boolean isField = _member instanceof Field;
2121: out.writeBoolean(isField);
2122: out.writeObject(_member.getDeclaringClass());
2123: out.writeObject(_member.getName());
2124: if (!isField)
2125: out.writeObject(((Method) _member).getParameterTypes());
2126: }
2127: }
2128:
2129: public boolean isValueGenerated() {
2130: return _generated;
2131: }
2132:
2133: public void setValueGenerated(boolean generated) {
2134: this._generated = generated;
2135: }
2136: }
|