Home » apache-openjpa-1.1.0-source » org.apache.openjpa » meta » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.meta;
   20   
   21   import java.io.Externalizable;
   22   import java.io.IOException;
   23   import java.io.ObjectInput;
   24   import java.io.ObjectOutput;
   25   import java.io.Serializable;
   26   import java.lang.reflect.Constructor;
   27   import java.lang.reflect.Field;
   28   import java.lang.reflect.InvocationTargetException;
   29   import java.lang.reflect.Member;
   30   import java.lang.reflect.Method;
   31   import java.lang.reflect.Modifier;
   32   import java.security.AccessController;
   33   import java.security.PrivilegedActionException;
   34   import java.util.ArrayList;
   35   import java.util.Calendar;
   36   import java.util.Collection;
   37   import java.util.Collections;
   38   import java.util.Comparator;
   39   import java.util.HashMap;
   40   import java.util.HashSet;
   41   import java.util.Iterator;
   42   import java.util.List;
   43   import java.util.Map;
   44   import java.util.Properties;
   45   import java.util.Set;
   46   import java.util.TimeZone;
   47   
   48   import org.apache.commons.collections.comparators.ComparatorChain;
   49   import org.apache.commons.lang.StringUtils;
   50   import org.apache.openjpa.conf.OpenJPAConfiguration;
   51   import org.apache.openjpa.kernel.OpenJPAStateManager;
   52   import org.apache.openjpa.kernel.StoreContext;
   53   import org.apache.openjpa.lib.conf.Configurations;
   54   import org.apache.openjpa.lib.log.Log;
   55   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   56   import org.apache.openjpa.lib.util.JavaVersions;
   57   import org.apache.openjpa.lib.util.Localizer;
   58   import org.apache.openjpa.lib.util.Options;
   59   import org.apache.openjpa.lib.xml.Commentable;
   60   import org.apache.openjpa.util.Exceptions;
   61   import org.apache.openjpa.util.InternalException;
   62   import org.apache.openjpa.util.MetaDataException;
   63   import org.apache.openjpa.util.OpenJPAException;
   64   import org.apache.openjpa.util.UnsupportedException;
   65   import org.apache.openjpa.util.ImplHelper;
   66   import org.apache.openjpa.util.UserException;
   67   
   68   import serp.util.Strings;
   69   
   70   /**
   71    * Metadata for a managed class field.
   72    *
   73    * @author Abe White
   74    */
   75   public class FieldMetaData
   76       extends Extensions
   77       implements ValueMetaData, MetaDataContext, MetaDataModes, Commentable {
   78       
   79       /**
   80        * Constant specifying that no null-value was given.
   81        */
   82       public static final int NULL_UNSET = -1;
   83   
   84       /**
   85        * Constant specifying to use a datastore null to persist null values
   86        * in object fields.
   87        */
   88       public static final int NULL_NONE = 0;
   89   
   90       /**
   91        * Constant specifying to use a datastore default value to persist null
   92        * values in object fields.
   93        */
   94       public static final int NULL_DEFAULT = 1;
   95   
   96       /**
   97        * Constant specifying to throw an exception when attempting to persist
   98        * null values in object fields.
   99        */
  100       public static final int NULL_EXCEPTION = 2;
  101   
  102       /**
  103        * Constant specifying the management level of a field.
  104        */
  105       public static final int MANAGE_PERSISTENT = 3;
  106   
  107       /**
  108        * Constant specifying the management level of a field.
  109        */
  110       public static final int MANAGE_TRANSACTIONAL = 1;
  111   
  112       /**
  113        * Constant specifying the management level of a field.
  114        */
  115       public static final int MANAGE_NONE = 0;
  116   
  117       private static final Localizer _loc = Localizer.forPackage
  118           (FieldMetaData.class);
  119   
  120       private static final int DFG_FALSE = 1;
  121       private static final int DFG_TRUE = 2;
  122       private static final int DFG_EXPLICIT = 4;
  123   
  124       private static final Method DEFAULT_METHOD;
  125       static {
  126           try {
  127               DEFAULT_METHOD = Object.class.getMethod("wait", (Class[]) null);
  128           } catch (Exception e) {
  129               // shouldn't ever happen
  130               throw new InternalException(e);
  131           }
  132       }
  133   
  134       // name and type
  135       private final ValueMetaData _val;
  136       private final ValueMetaData _key;
  137       private final ValueMetaData _elem;
  138       private final ClassMetaData _owner;
  139       private final String _name;
  140       private Class _dec = null;
  141       private ClassMetaData _decMeta = null;
  142       private String _fullName = null;
  143       private String _embedFullName = null;
  144       private int _resMode = MODE_NONE;
  145   
  146       // load/store info
  147       private String[] _comments = null;
  148       private int _listIndex = -1;
  149   
  150       ////////////////////////////////////////////////////////////////////
  151       // Note: if you add additional state, make sure to add it to copy()
  152       ////////////////////////////////////////////////////////////////////
  153   
  154       // misc info
  155       private Class _proxyClass = null;
  156       private Object _initializer = null;
  157       private boolean _transient = false;
  158       private boolean _primKey = false;
  159       private Boolean _version = null;
  160       private int _nullValue = NULL_UNSET;
  161       private int _manage = MANAGE_PERSISTENT;
  162       private int _index = -1;
  163       private int _decIndex = -1;
  164       private int _pkIndex = -1;
  165       private boolean _explicit = false;
  166       private int _dfg = 0;
  167       private Set _fgSet = null;
  168       private String[] _fgs = null;
  169       private String   _lfg = null;
  170       private Boolean _lrs = null;
  171       private Boolean _stream = null;
  172       private String _extName = null;
  173       private String _factName = null;
  174       private String _extString = null;
  175       private Map _extValues = Collections.EMPTY_MAP;
  176       private Map _fieldValues = Collections.EMPTY_MAP;
  177       private Boolean _enumField = null;
  178       private Boolean _lobField = null;
  179       private Boolean _serializableField = null;
  180       private boolean _generated = false;
  181   
  182       // Members aren't serializable. Use a proxy that can provide a Member
  183       // to avoid writing the full Externalizable implementation.
  184       private MemberProvider _backingMember = null;
  185   
  186       // Members aren't serializable. Initializing _extMethod and _factMethod to
  187       // DEFAULT_METHOD is sufficient to trigger lazy population of these fields.
  188       private transient Method _extMethod = DEFAULT_METHOD;
  189       private transient Member _factMethod = DEFAULT_METHOD;
  190   
  191       // intermediate and impl data
  192       private boolean _intermediate = true;
  193       private Boolean _implData = Boolean.TRUE;
  194   
  195       // value generation
  196       private int _valStrategy = -1;
  197       private int _upStrategy = -1;
  198       private String _seqName = ClassMetaData.DEFAULT_STRING;
  199       private SequenceMetaData _seqMeta = null;
  200   
  201       // inverses
  202       private String _mappedBy = null;
  203       private FieldMetaData _mappedByMeta = null;
  204       private FieldMetaData[] _inverses = null;
  205       private String _inverse = ClassMetaData.DEFAULT_STRING;
  206   
  207       // ordering on load
  208       private Order[] _orders = null;
  209       private String _orderDec = null;
  210       // indicate if this field is used by other field as "order by" value 
  211       private boolean _usedInOrderBy = false;
  212   
  213       /**
  214        * Constructor.
  215        *
  216        * @param name the field name
  217        * @param type the field type
  218        * @param owner the owning class metadata
  219        */
  220       protected FieldMetaData(String name, Class type, ClassMetaData owner) {
  221           _name = name;
  222           _owner = owner;
  223           _dec = null;
  224           _decMeta = null;
  225           _val = owner.getRepository().newValueMetaData(this);
  226           _key = owner.getRepository().newValueMetaData(this);
  227           _elem = owner.getRepository().newValueMetaData(this);
  228   
  229           setDeclaredType(type);
  230       }
  231   
  232       /**
  233        * Supply the backing member object; this allows us to utilize
  234        * parameterized type information if available.
  235        */
  236       public void backingMember(Member member) {
  237           if (member == null)
  238               return;
  239           if (Modifier.isTransient(member.getModifiers()))
  240               _transient = true;
  241   
  242           _backingMember = new MemberProvider(member);
  243   
  244           Class type;
  245           Class[] types;
  246           if (member instanceof Field) {
  247               Field f = (Field) member;
  248               type = f.getType();
  249               types = JavaVersions.getParameterizedTypes(f);
  250           } else {
  251               Method meth = (Method) member;
  252               type = meth.getReturnType();
  253               types = JavaVersions.getParameterizedTypes(meth);
  254           }
  255   
  256           setDeclaredType(type);
  257           if (Collection.class.isAssignableFrom(type)
  258               && _elem.getDeclaredType() == Object.class
  259               && types.length == 1) {
  260               _elem.setDeclaredType(types[0]);
  261           } else if (Map.class.isAssignableFrom(type)
  262               && types.length == 2) {
  263               if (_key.getDeclaredType() == Object.class)
  264                   _key.setDeclaredType(types[0]);
  265               if (_elem.getDeclaredType() == Object.class)
  266                   _elem.setDeclaredType(types[1]);
  267           }
  268       }
  269   
  270       /**
  271        * Return the backing member supplied in {@link #backingMember}.
  272        */
  273       public Member getBackingMember() {
  274           return (_backingMember == null) ? null : _backingMember.getMember();
  275       }
  276   
  277       /**
  278        * The metadata repository.
  279        */
  280       public MetaDataRepository getRepository() {
  281           return _owner.getRepository();
  282       }
  283   
  284       /**
  285        * The class that defines the metadata for this field.
  286        */
  287       public ClassMetaData getDefiningMetaData() {
  288           return _owner;
  289       }
  290   
  291       /**
  292        * The declaring class.
  293        */
  294       public Class getDeclaringType() {
  295           return (_dec == null) ? _owner.getDescribedType() : _dec;
  296       }
  297   
  298       /**
  299        * The declaring class.
  300        */
  301       public void setDeclaringType(Class cls) {
  302           _dec = cls;
  303           _decMeta = null;
  304           _fullName = null;
  305           _embedFullName = null;
  306       }
  307   
  308       /**
  309        * The declaring class.
  310        */
  311       public ClassMetaData getDeclaringMetaData() {
  312           if (_dec == null)
  313               return _owner;
  314           if (_decMeta == null)
  315               _decMeta = getRepository().getMetaData(_dec,
  316                   _owner.getEnvClassLoader(), true);
  317           return _decMeta;
  318       }
  319   
  320       /**
  321        * The field name.
  322        */
  323       public String getName() {
  324           return _name;
  325       }
  326   
  327       /**
  328        * The field name, qualified by the owning class.
  329        * @deprecated Use getFullName(boolean) instead.
  330        */
  331       public String getFullName() {
  332           return getFullName(false);
  333       }
  334   
  335       /**
  336        * The field name, qualified by the owning class and optionally the
  337        * embedding owner's name (if any).
  338        */
  339       public String getFullName(boolean embedOwner) {
  340           if (_fullName == null)
  341               _fullName = getDeclaringType().getName() + "." + _name;
  342           if (embedOwner && _embedFullName == null) {
  343               if (_owner.getEmbeddingMetaData() == null)
  344                   _embedFullName = _fullName;
  345               else
  346                   _embedFullName = _owner.getEmbeddingMetaData().
  347                       getFieldMetaData().getFullName(true) + "." + _fullName;
  348           }
  349           return (embedOwner) ? _embedFullName : _fullName;
  350       }
  351   
  352       /**
  353        * MetaData about the field value.
  354        */
  355       public ValueMetaData getValue() {
  356           return _val;
  357       }
  358   
  359       /**
  360        * Metadata about the key value.
  361        */
  362       public ValueMetaData getKey() {
  363           return _key;
  364       }
  365   
  366       /**
  367        * Metadata about the element value.
  368        */
  369       public ValueMetaData getElement() {
  370           return _elem;
  371       }
  372   
  373       /**
  374        * Return whether this field is mapped to the datastore. By default,
  375        * returns true for all persistent fields whose defining class is mapped.
  376        */
  377       public boolean isMapped() {
  378           return _manage == MANAGE_PERSISTENT && _owner.isMapped();
  379       }
  380   
  381       /**
  382        * The type this field was initialized with, and therefore the
  383        * type to use for proxies when loading data into this field.
  384        */
  385       public Class getProxyType() {
  386           return (_proxyClass == null) ? getDeclaredType() : _proxyClass;
  387       }
  388   
  389       /**
  390        * The type this field was initialized with, and therefore the
  391        * type to use for proxies when loading data into this field.
  392        */
  393       public void setProxyType(Class type) {
  394           _proxyClass = type;
  395       }
  396   
  397       /**
  398        * The initializer used by the field, or null if none. This
  399        * is additional information for initializing the field, such as
  400        * a custom {@link Comparator} used by a {@link Set} or
  401        * a {@link TimeZone} used by a {@link Calendar}.
  402        */
  403       public Object getInitializer() {
  404           return _initializer;
  405       }
  406   
  407       /**
  408        * The initializer used by the field, or null if none. This
  409        * is additional information for initializing the field, such as
  410        * a custom {@link Comparator} used by a {@link Set} or
  411        * a {@link TimeZone} used by a {@link Calendar}.
  412        */
  413       public void setInitializer(Object initializer) {
  414           _initializer = initializer;
  415       }
  416   
  417       /**
  418        * Return whether this is a transient field.
  419        */
  420       public boolean isTransient() {
  421           return _transient;
  422       }
  423   
  424       /**
  425        * Return whether this is a transient field.
  426        */
  427       public void setTransient(boolean trans) {
  428           _transient = trans;
  429       }
  430   
  431       /**
  432        * The absolute index of this persistent/transactional field.
  433        */
  434       public int getIndex() {
  435           return _index;
  436       }
  437   
  438       /**
  439        * The absolute index of this persistent/transactional field.
  440        */
  441       public void setIndex(int index) {
  442           _index = index;
  443       }
  444   
  445       /**
  446        * The relative index of this persistent/transactional field.
  447        */
  448       public int getDeclaredIndex() {
  449           return _decIndex;
  450       }
  451   
  452       /**
  453        * The relative index of this persistent/transactional field.
  454        */
  455       public void setDeclaredIndex(int index) {
  456           _decIndex = index;
  457       }
  458   
  459       /**
  460        * The index in which this field was listed in the metadata. Defaults to
  461        * <code>-1</code> if this field was not listed in the metadata.
  462        */
  463       public int getListingIndex() {
  464           return _listIndex;
  465       }
  466   
  467       /**
  468        * The index in which this field was listed in the metadata. Defaults to
  469        * <code>-1</code> if this field was not listed in the metadata.
  470        */
  471       public void setListingIndex(int index) {
  472           _listIndex = index;
  473       }
  474   
  475       /**
  476        * The absolute primary key index for this field, or -1 if not a primary
  477        * key. The first primary key field has index 0, the second index 1, etc.
  478        */
  479       public int getPrimaryKeyIndex() {
  480           return _pkIndex;
  481       }
  482   
  483       /**
  484        * The absolute primary key index for this field, or -1 if not a primary
  485        * key. The first primary key field has index 0, the second index 1, etc.
  486        */
  487       public void setPrimaryKeyIndex(int index) {
  488           _pkIndex = index;
  489       }
  490   
  491       /**
  492        * Return the management level for the field. Will be one of:
  493        * <ul>
  494        * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
  495        * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
  496        * persistent</li>
  497        * <li>{@link #MANAGE_NONE}: the field is not managed</li>
  498        * </ul> Defaults to {@link #MANAGE_PERSISTENT}.
  499        */
  500       public int getManagement() {
  501           return _manage;
  502       }
  503   
  504       /**
  505        * Return the management level for the field. Will be one of:
  506        * <ul>
  507        * <li>{@link #MANAGE_PERSISTENT}: the field is persistent</li>
  508        * <li>{@link #MANAGE_TRANSACTIONAL}: the field is transactional but not
  509        * persistent</li>
  510        * <li>{@link #MANAGE_NONE}: the field is not managed</li>
  511        * </ul> 
  512        * Defaults to {@link #MANAGE_PERSISTENT}.
  513        */
  514       public void setManagement(int manage) {
  515           if ((_manage == MANAGE_NONE) != (manage == MANAGE_NONE))
  516               _owner.clearFieldCache();
  517           _manage = manage;
  518       }
  519   
  520       /**
  521        * Whether this is a primary key field.
  522        */
  523       public boolean isPrimaryKey() {
  524           return _primKey;
  525       }
  526   
  527       /**
  528        * Whether this is a primary key field.
  529        */
  530       public void setPrimaryKey(boolean primKey) {
  531           _primKey = primKey;
  532       }
  533   
  534       /**
  535        * For a primary key field, return the type of the corresponding object id 
  536        * class field.
  537        */
  538       public int getObjectIdFieldTypeCode() {
  539           ClassMetaData relmeta = getDeclaredTypeMetaData();
  540           if (relmeta == null)
  541               return getDeclaredTypeCode();
  542           if (relmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
  543               boolean unwrap = getRepository().getMetaDataFactory().getDefaults().
  544                   isDataStoreObjectIdFieldUnwrapped();
  545               return (unwrap) ? JavaTypes.LONG : JavaTypes.OBJECT;
  546           }
  547           if (relmeta.isOpenJPAIdentity())
  548               return relmeta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode();
  549           return JavaTypes.OBJECT;
  550       }
  551   
  552       /**
  553        * For a primary key field, return the type of the corresponding object id 
  554        * class field.
  555        */
  556       public Class getObjectIdFieldType() {
  557           ClassMetaData relmeta = getDeclaredTypeMetaData();
  558           if (relmeta == null)
  559               return getDeclaredType();
  560           switch (relmeta.getIdentityType()) {
  561               case ClassMetaData.ID_DATASTORE:
  562                   boolean unwrap = getRepository().getMetaDataFactory().
  563                       getDefaults().isDataStoreObjectIdFieldUnwrapped();
  564                   return (unwrap) ? long.class : Object.class;
  565               case ClassMetaData.ID_APPLICATION:
  566                   if (relmeta.isOpenJPAIdentity())
  567                       return relmeta.getPrimaryKeyFields()[0].
  568                           getObjectIdFieldType();
  569                   return (relmeta.getObjectIdType() == null) ? Object.class
  570                       : relmeta.getObjectIdType();
  571               default:
  572                   return Object.class;
  573           } 
  574       }
  575   
  576       /**
  577        * Whether this field holds optimistic version information.
  578        */
  579       public boolean isVersion() {
  580           return _version == Boolean.TRUE;
  581       }
  582   
  583       /**
  584        * Whether this field holds optimistic version information.
  585        */
  586       public void setVersion(boolean version) {
  587           _version = (version) ? Boolean.TRUE : Boolean.FALSE;
  588       }
  589   
  590       /**
  591        * Whether this field is in the default fetch group.
  592        */
  593       public boolean isInDefaultFetchGroup() {
  594           if (_dfg == 0) {
  595               if (_manage != MANAGE_PERSISTENT || isPrimaryKey() || isVersion())
  596                   _dfg = DFG_FALSE;
  597               else {
  598                   // field left as default; dfg setting depends on type
  599                   switch (getTypeCode()) {
  600                       case JavaTypes.OBJECT:
  601                           if (isSerializable() || isEnum())
  602                               _dfg = DFG_TRUE;
  603                           else
  604                               _dfg = DFG_FALSE;
  605                           break;
  606                       case JavaTypes.ARRAY:
  607                           if (isLobArray())
  608                               _dfg = DFG_TRUE;
  609                           else
  610                               _dfg = DFG_FALSE;
  611                           break;
  612                       case JavaTypes.COLLECTION:
  613                       case JavaTypes.MAP:
  614                       case JavaTypes.PC:
  615                       case JavaTypes.PC_UNTYPED:
  616                           _dfg = DFG_FALSE;
  617                           break;
  618                       default:
  619                           _dfg = DFG_TRUE;
  620                   }
  621               }
  622           }
  623           return (_dfg & DFG_TRUE) > 0;
  624       }
  625   
  626       private boolean isEnum() {
  627           if (_enumField == null) {
  628               Class dt = getDeclaredType();
  629               _enumField = JavaVersions.isEnumeration(dt)
  630                   ? Boolean.TRUE : Boolean.FALSE;
  631           }
  632           return _enumField.booleanValue();
  633       }
  634   
  635       private boolean isSerializable() {
  636           if (_serializableField == null) {
  637               Class dt = getDeclaredType();
  638               if (Serializable.class.isAssignableFrom(dt))
  639                   _serializableField = Boolean.TRUE;
  640               else
  641                   _serializableField = Boolean.FALSE;
  642           }
  643           return _serializableField.booleanValue();
  644       }
  645   
  646       private boolean isLobArray() {
  647           // check for byte[], Byte[], char[], Character[]
  648           if (_lobField == null) {
  649               Class dt = getDeclaredType();
  650               if (dt == byte[].class || dt == Byte[].class ||
  651                   dt == char[].class || dt == Character[].class)
  652                   _lobField = Boolean.TRUE;
  653               else
  654                   _lobField = Boolean.FALSE;
  655           }
  656           return _lobField.booleanValue();
  657       }
  658   
  659       /**
  660        * Whether this field is in the default fetch group.
  661        */
  662       public void setInDefaultFetchGroup(boolean dfg) {
  663           if (dfg)
  664               _dfg = DFG_TRUE;
  665           else
  666               _dfg = DFG_FALSE;
  667           _dfg |= DFG_EXPLICIT;
  668       }
  669   
  670       /**
  671        * Whether the default fetch group setting is explicit.
  672        */
  673       public boolean isDefaultFetchGroupExplicit() {
  674           return (_dfg & DFG_EXPLICIT) > 0;
  675       }
  676   
  677       /**
  678        * Whether the default fetch group setting is explicit. Allow setting
  679        * for testing.
  680        */
  681       public void setDefaultFetchGroupExplicit(boolean explicit) {
  682           if (explicit)
  683               _dfg |= DFG_EXPLICIT;
  684           else
  685               _dfg &= ~DFG_EXPLICIT;
  686       }
  687   
  688       /**
  689        * Gets the name of the custom fetch groups those are associated to this 
  690        * receiver.  This does not include the "default" and "all" fetch groups.
  691        *
  692        * @return the set of fetch group names, not including the default and
  693        * all fetch groups.
  694        */
  695       public String[] getCustomFetchGroups() {
  696           if (_fgs == null) {
  697               if (_fgSet == null || _manage != MANAGE_PERSISTENT 
  698                   || isPrimaryKey() || isVersion())
  699                   _fgs = new String[0];
  700               else
  701                   _fgs = (String[]) _fgSet.toArray(new String[_fgSet.size()]);
  702           }
  703           return _fgs;
  704       }
  705   
  706       /**
  707        * The fetch group that is to be loaded when this receiver is loaded, or
  708        * null if none set.
  709        */
  710       public String getLoadFetchGroup () {
  711       	return _lfg;
  712       }
  713       
  714       /**
  715        * The fetch group that is to be loaded when this receiver is loaded, or
  716        * null if none set.
  717        */
  718       public void setLoadFetchGroup (String lfg) {
  719           if ("".equals(lfg))
  720               lfg = null;
  721       	_lfg = lfg;
  722       }
  723   
  724       /**
  725        * Whether this field is in the given fetch group.
  726        */
  727       public boolean isInFetchGroup(String fg) {
  728           if (_manage != MANAGE_PERSISTENT || isPrimaryKey() || isVersion())
  729               return false;
  730           if (FetchGroup.NAME_ALL.equals(fg))
  731               return true;
  732           if (FetchGroup.NAME_DEFAULT.equals(fg))
  733               return isInDefaultFetchGroup();
  734           return _fgSet != null && _fgSet.contains(fg);
  735       }
  736   
  737       /**
  738        * Set whether this field is in the given fetch group.
  739        *
  740        * @param fg is the name of a fetch group that must be present in the
  741        * class that declared this field or any of its persistent superclasses.
  742        */
  743       public void setInFetchGroup(String fg, boolean in) {
  744           if (StringUtils.isEmpty(fg))
  745               throw new MetaDataException(_loc.get("empty-fg-name", this));
  746           if (fg.equals(FetchGroup.NAME_ALL))
  747               return;
  748           if (fg.equals(FetchGroup.NAME_DEFAULT)) {
  749               setInDefaultFetchGroup(in);
  750               return;
  751           }
  752           if (_owner.getFetchGroup(fg) == null)
  753               throw new MetaDataException(_loc.get("unknown-fg", fg, this));
  754           if (in && _fgSet == null)
  755               _fgSet = new HashSet();
  756           if ((in && _fgSet.add(fg))
  757               || (!in && _fgSet != null && _fgSet.remove(fg)))
  758               _fgs = null;
  759       }
  760       
  761       /**
  762        * How the data store should treat null values for this field:
  763        * <ul>
  764        * <li>{@link #NULL_UNSET}: no value supplied</li>
  765        * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
  766        * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
  767        * at commit</li>
  768        * <li>{@link #NULL_DEFAULT}: use the database default if this field is
  769        * null at commit</li>
  770        * </ul> Defaults to {@link #NULL_UNSET}.
  771        */
  772       public int getNullValue() {
  773           return _nullValue;
  774       }
  775   
  776       /**
  777        * How the data store should treat null values for this field:
  778        * <ul>
  779        * <li>{@link #NULL_UNSET}: no value supplied</li>
  780        * <li>{@link #NULL_NONE}: leave null values as null in the data store</li>
  781        * <li>{@link #NULL_EXCEPTION}: throw an exception if this field is null
  782        * at commit</li>
  783        * <li>{@link #NULL_DEFAULT}: use the database default if this field is
  784        * null at commit</li>
  785        * </ul> Defaults to {@link #NULL_UNSET}.
  786        */
  787       public void setNullValue(int nullValue) {
  788           _nullValue = nullValue;
  789       }
  790   
  791       /**
  792        * Whether this field is explicitly declared in the metadata.
  793        */
  794       public boolean isExplicit() {
  795           return _explicit;
  796       }
  797   
  798       /**
  799        * Whether this field is explicitly declared in the metadata.
  800        */
  801       public void setExplicit(boolean explicit) {
  802           _explicit = explicit;
  803       }
  804   
  805       /**
  806        * The field that this field shares a mapping with.
  807        */
  808       public String getMappedBy() {
  809           return _mappedBy;
  810       }
  811   
  812       /**
  813        * The field that this field shares a mapping with.
  814        */
  815       public void setMappedBy(String mapped) {
  816           _mappedBy = mapped;
  817           _mappedByMeta = null;
  818       }
  819   
  820       /**
  821        * The field that this field shares a mapping with.
  822        */
  823       public FieldMetaData getMappedByMetaData() {
  824           if (_mappedBy != null && _mappedByMeta == null) {
  825               ClassMetaData meta = null;
  826               switch (getTypeCode()) {
  827                   case JavaTypes.PC:
  828                       meta = getTypeMetaData();
  829                       break;
  830                   case JavaTypes.ARRAY:
  831                   case JavaTypes.COLLECTION:
  832                   case JavaTypes.MAP:
  833                       meta = _elem.getTypeMetaData();
  834                       break;
  835               }
  836   
  837               FieldMetaData field = (meta == null) ? null
  838                   : meta.getField(_mappedBy);
  839               if (field == null)
  840                   throw new MetaDataException(_loc.get("no-mapped-by", this,
  841                       _mappedBy));
  842               if (field.getMappedBy() != null)
  843                   throw new MetaDataException(_loc.get("circ-mapped-by", this,
  844                       _mappedBy));
  845               _mappedByMeta = field;
  846           }
  847           return _mappedByMeta;
  848       }
  849   
  850       /**
  851        * Logical inverse field.
  852        */
  853       public String getInverse() {
  854           if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
  855               _inverse = null;
  856           return _inverse;
  857       }
  858   
  859       /**
  860        * Logical inverse field.
  861        */
  862       public void setInverse(String inverse) {
  863           _inverses = null;
  864           _inverse = inverse;
  865       }
  866   
  867       /**
  868        * Return all inverses of this field.
  869        */
  870       public FieldMetaData[] getInverseMetaDatas() {
  871           if (_inverses == null) {
  872               // can't declare both an inverse owner and a logical inverse
  873               String inv = getInverse();
  874               if (_mappedBy != null && inv != null && !_mappedBy.equals(inv))
  875                   throw new MetaDataException(_loc.get("mapped-not-inverse",
  876                       this));
  877   
  878               // get the metadata for the type on the other side of this relation
  879               ClassMetaData meta = null;
  880               switch (getTypeCode()) {
  881                   case JavaTypes.PC:
  882                       meta = getTypeMetaData();
  883                       break;
  884                   case JavaTypes.ARRAY:
  885                   case JavaTypes.COLLECTION:
  886                       meta = _elem.getTypeMetaData();
  887                       break;
  888               }
  889   
  890               Collection inverses = null;
  891               if (meta != null) {
  892                   // add mapped by and named inverse, if any
  893                   FieldMetaData field = getMappedByMetaData();
  894                   if (field != null) {
  895                       // mapped by field isn't necessarily a pc type, but all
  896                       // inverses must be
  897                       if (field.getTypeCode() == JavaTypes.PC
  898                           || field.getElement().getTypeCode() == JavaTypes.PC) {
  899                           inverses = new ArrayList(3);
  900                           inverses.add(field);
  901                       }
  902                   } else if (inv != null) {
  903                       field = meta.getField(inv);
  904                       if (field == null)
  905                           throw new MetaDataException(_loc.get("no-inverse",
  906                               this, inv));
  907                       inverses = new ArrayList(3);
  908                       inverses.add(field);
  909                   }
  910   
  911                   // scan rel type for fields that name this field as an inverse
  912                   FieldMetaData[] fields = meta.getFields();
  913                   Class type = getDeclaringMetaData().getDescribedType();
  914                   for (int i = 0; i < fields.length; i++) {
  915                       // skip fields that aren't compatible with our owning class
  916                       switch (fields[i].getTypeCode()) {
  917                           case JavaTypes.PC:
  918                               if (!type.isAssignableFrom(fields[i].getType()))
  919                                   continue;
  920                               break;
  921                           case JavaTypes.COLLECTION:
  922                           case JavaTypes.ARRAY:
  923                               if (!type.isAssignableFrom(fields[i].
  924                                   getElement().getType()))
  925                                   continue;
  926                               break;
  927                           default:
  928                               continue;
  929                       }
  930   
  931                       // if the field declares us as its inverse and we haven't
  932                       // already added it (we might have if we also declared it
  933                       // as our inverse), add it now
  934                       if (_name.equals(fields[i].getMappedBy())
  935                           || _name.equals(fields[i].getInverse())) {
  936                           if (inverses == null)
  937                               inverses = new ArrayList(3);
  938                           if (!inverses.contains(fields[i]))
  939                               inverses.add(fields[i]);
  940                       }
  941                   }
  942               }
  943   
  944               MetaDataRepository repos = getRepository();
  945               if (inverses == null)
  946                   _inverses = repos.EMPTY_FIELDS;
  947               else
  948                   _inverses = (FieldMetaData[]) inverses.toArray
  949                       (repos.newFieldMetaDataArray(inverses.size()));
  950           }
  951           return _inverses;
  952       }
  953   
  954       /**
  955        * The strategy to use for insert value generation.
  956        * One of the constants from {@link ValueStrategies}.
  957        */
  958       public int getValueStrategy() {
  959           if (_valStrategy == -1)
  960               _valStrategy = ValueStrategies.NONE;
  961           return _valStrategy;
  962       }
  963   
  964       /**
  965        * The strategy to use for insert value generation.
  966        * One of the constants from {@link ValueStrategies}.
  967        */
  968       public void setValueStrategy(int strategy) {
  969           _valStrategy = strategy;
  970           if (strategy != ValueStrategies.SEQUENCE)
  971               setValueSequenceName(null);
  972       }
  973   
  974       /**
  975        * The value sequence name, or null for none.
  976        */
  977       public String getValueSequenceName() {
  978           if (ClassMetaData.DEFAULT_STRING.equals(_seqName))
  979               _seqName = null;
  980           return _seqName;
  981       }
  982   
  983       /**
  984        * The value sequence name, or null for none.
  985        */
  986       public void setValueSequenceName(String seqName) {
  987           _seqName = seqName;
  988           _seqMeta = null;
  989           if (seqName != null)
  990               setValueStrategy(ValueStrategies.SEQUENCE);
  991       }
  992   
  993       /**
  994        * Metadata for the value sequence.
  995        */
  996       public SequenceMetaData getValueSequenceMetaData() {
  997           if (_seqMeta == null && getValueSequenceName() != null)
  998               _seqMeta = getRepository().getSequenceMetaData(_owner,
  999                   getValueSequenceName(), true);
 1000           return _seqMeta;
 1001       }
 1002   
 1003       /**
 1004        * The strategy to use when updating the field.
 1005        */
 1006       public int getUpdateStrategy() {
 1007           if (isVersion())
 1008               return UpdateStrategies.RESTRICT;
 1009           if (_upStrategy == -1)
 1010               _upStrategy = UpdateStrategies.NONE;
 1011           return _upStrategy;
 1012       }
 1013   
 1014       /**
 1015        * Set the update strategy.
 1016        */
 1017       public void setUpdateStrategy(int strategy) {
 1018           _upStrategy = strategy;
 1019       }
 1020   
 1021       /**
 1022        * Whether this field is backed by a large result set.
 1023        */
 1024       public boolean isLRS() {
 1025           return _lrs == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
 1026       }
 1027   
 1028       /**
 1029        * Whether this field is backed by a large result set.
 1030        */
 1031       public void setLRS(boolean lrs) {
 1032           _lrs = (lrs) ? Boolean.TRUE : Boolean.FALSE;
 1033       }
 1034   
 1035       /**
 1036        * Whether this field is backed by a stream.
 1037        *
 1038        * @since 1.1.0
 1039        */
 1040       public boolean isStream() {
 1041           return _stream == Boolean.TRUE && _manage == MANAGE_PERSISTENT;
 1042       }
 1043       
 1044       /**
 1045        * Whether this field is backed by a stream.
 1046        *
 1047        * @since 1.1.0
 1048        */
 1049       public void setStream(boolean stream) {
 1050           _stream = (stream) ? Boolean.TRUE : Boolean.FALSE;
 1051       }
 1052       
 1053       /**
 1054        * Whether this field uses intermediate data when loading/storing
 1055        * information through a {@link OpenJPAStateManager}. Defaults to true.
 1056        *
 1057        * @see OpenJPAStateManager#setIntermediate(int,Object)
 1058        */
 1059       public boolean usesIntermediate() {
 1060           return _intermediate;
 1061       }
 1062   
 1063       /**
 1064        * Whether this field uses intermediate data when loading/storing
 1065        * information through a {@link OpenJPAStateManager}. Defaults to true.
 1066        *
 1067        * @see OpenJPAStateManager#setIntermediate(int,Object)
 1068        */
 1069       public void setUsesIntermediate(boolean intermediate) {
 1070           _intermediate = intermediate;
 1071           _owner.clearExtraFieldDataTable();
 1072       }
 1073   
 1074       /**
 1075        * Whether this field uses impl data in conjunction with standard
 1076        * field data when acting on a {@link OpenJPAStateManager}.
 1077        * Defaults to {@link Boolean#TRUE} (non-cachable impl data).
 1078        *
 1079        * @return {@link Boolean#FALSE} if this field does not use impl data,
 1080        * {@link Boolean#TRUE} if this field uses non-cachable impl
 1081        * data, or <code>null</code> if this field uses impl data that
 1082        * should be cached across instances
 1083        * @see OpenJPAStateManager#setImplData(int,Object)
 1084        */
 1085       public Boolean usesImplData() {
 1086           return _implData;
 1087       }
 1088   
 1089       /**
 1090        * Whether this field uses impl data in conjunction with standard
 1091        * field data when acting on a {@link OpenJPAStateManager}.
 1092        *
 1093        * @see OpenJPAStateManager#setImplData(int,Object)
 1094        * @see #usesImplData
 1095        */
 1096       public void setUsesImplData(Boolean implData) {
 1097           _implData = implData;
 1098           _owner.clearExtraFieldDataTable();
 1099       }
 1100   
 1101       /**
 1102        * The orderings for this field to be applied on load, or empty array.
 1103        */
 1104       public Order[] getOrders() {
 1105           if (_orders == null) {
 1106               if (_orderDec == null)
 1107                   _orders = getRepository().EMPTY_ORDERS;
 1108               else {
 1109                   String[] decs = Strings.split(_orderDec, ",", 0);
 1110                   Order[] orders = getRepository().newOrderArray(decs.length);
 1111                   int spc;
 1112                   boolean asc;
 1113                   for (int i = 0; i < decs.length; i++) {
 1114                       decs[i] = decs[i].trim();
 1115                       spc = decs[i].indexOf(' ');
 1116                       if (spc == -1)
 1117                           asc = true;
 1118                       else {
 1119                           asc = decs[i].substring(spc + 1).trim().
 1120                               toLowerCase().startsWith("asc");
 1121                           decs[i] = decs[i].substring(0, spc);
 1122                       }
 1123                       orders[i] = getRepository().newOrder(this, decs[i], asc);
 1124                       //set "isUsedInOrderBy" to the field
 1125                       ClassMetaData elemCls = getElement()
 1126                           .getDeclaredTypeMetaData();
 1127                       if (elemCls != null) {
 1128                         FieldMetaData fmd = elemCls.getDeclaredField(decs[i]);
 1129                         if (fmd != null)
 1130                           fmd.setUsedInOrderBy(true);                      
 1131                       }
 1132                   }
 1133                   _orders = orders;
 1134               }
 1135           }
 1136           return _orders;
 1137       }
 1138   
 1139       /**
 1140        * The orderings for this field to be applied on load.
 1141        */
 1142       public void setOrders(Order[] orders) {
 1143           _orderDec = null;
 1144           _orders = orders;
 1145       }
 1146   
 1147       /**
 1148        * String declaring the orderings for this field to be applied on load,
 1149        * or null. The string is of the form:<br />
 1150        * <code>orderable[ asc|desc][, ...]</code><br />
 1151        * The orderable <code>#element</code> is used to denote the value of
 1152        * the field's elements.
 1153        */
 1154       public String getOrderDeclaration() {
 1155           if (_orderDec == null && _orders != null) {
 1156               StringBuffer buf = new StringBuffer();
 1157               for (int i = 0; i < _orders.length; i++) {
 1158                   if (i > 0)
 1159                       buf.append(", ");
 1160                   buf.append(_orders[i].getName()).append(" ");
 1161                   buf.append((_orders[i].isAscending()) ? "asc" : "desc");
 1162               }
 1163               _orderDec = buf.toString();
 1164           }
 1165           return _orderDec;
 1166       }
 1167   
 1168       /**
 1169        * String declaring the orderings for this field to be applied on load,
 1170        * or null. The string is of the form:<br />
 1171        * <code>orderable[ asc|desc][, ...]</code><br />
 1172        * The orderable <code>#element</code> is used to denote the value of
 1173        * the field's elements.
 1174        */
 1175       public void setOrderDeclaration(String dec) {
 1176           _orderDec = StringUtils.trimToNull(dec);
 1177           _orders = null;
 1178       }
 1179   
 1180       /**
 1181        * Order this field value when it is loaded.
 1182        */
 1183       public Object order(Object val) {
 1184           if (val == null)
 1185               return null;
 1186   
 1187           Order[] orders = getOrders();
 1188           if (orders.length == 0)
 1189               return val;
 1190   
 1191           // create a comparator for the elements of the value
 1192           Comparator comp;
 1193           if (orders.length == 1)
 1194               comp = orders[0].getComparator();
 1195           else {
 1196               List comps = null;
 1197               Comparator curComp;
 1198               for (int i = 0; i < orders.length; i++) {
 1199                   curComp = orders[i].getComparator();
 1200                   if (curComp != null) {
 1201                       if (comps == null)
 1202                           comps = new ArrayList(orders.length);
 1203                       if (i != comps.size())
 1204                           throw new MetaDataException(_loc.get
 1205                               ("mixed-inmem-ordering", this));
 1206                       comps.add(curComp);
 1207                   }
 1208               }
 1209               if (comps == null)
 1210                   comp = null;
 1211               else
 1212                   comp = new ComparatorChain(comps);
 1213           }
 1214   
 1215           if (comp == null)
 1216               return val;
 1217   
 1218           // sort
 1219           switch (getTypeCode()) {
 1220               case JavaTypes.ARRAY:
 1221                   List l = JavaTypes.toList(val, _elem.getType(), true);
 1222                   Collections.sort(l, comp);
 1223                   return JavaTypes.toArray(l, _elem.getType());
 1224               case JavaTypes.COLLECTION:
 1225                   if (val instanceof List)
 1226                       Collections.sort((List) val, comp);
 1227                   return val;
 1228               default:
 1229                   throw new MetaDataException(_loc.get("cant-order", this));
 1230           }
 1231       }
 1232   
 1233       /**
 1234        * Whether the field is externalized.
 1235        */
 1236       public boolean isExternalized() {
 1237           return getExternalizerMethod() != null
 1238               || getExternalValueMap() != null;
 1239       }
 1240   
 1241       /**
 1242        * Convert the given field value to its external value through the
 1243        * provided externalizer, or return the value as-is if no externalizer.
 1244        */
 1245       public Object getExternalValue(Object val, StoreContext ctx) {
 1246           Map extValues = getExternalValueMap();
 1247           if (extValues != null) {
 1248               Object foundVal = extValues.get(val);
 1249               if (foundVal == null) {
 1250                   throw new UserException(_loc.get("bad-externalized-value",
 1251                           new Object[] { val, extValues.keySet(), this }))
 1252                           .setFatal(true).setFailedObject(val);
 1253               } else {
 1254                   return foundVal;
 1255               }
 1256           }
 1257   
 1258           Method externalizer = getExternalizerMethod();
 1259           if (externalizer == null)
 1260               return val;
 1261   
 1262           // special case for queries: allow the given value to pass through
 1263           // as-is if it is already in externalized form
 1264           if (val != null && getType().isInstance(val)
 1265               && (!getDeclaredType().isInstance(val)
 1266               || getDeclaredType() == Object.class))
 1267               return val;
 1268   
 1269           try {
 1270               // either invoke the static toExternal(val[, ctx]) method, or the
 1271               // non-static val.toExternal([ctx]) method
 1272               if (Modifier.isStatic(externalizer.getModifiers())) {
 1273                   if (externalizer.getParameterTypes().length == 1)
 1274                       return externalizer.invoke(null, new Object[]{ val });
 1275                   return externalizer.invoke(null, new Object[]{ val, ctx });
 1276               }
 1277               if (val == null)
 1278                   return null;
 1279               if (externalizer.getParameterTypes().length == 0)
 1280                   return externalizer.invoke(val, (Object[]) null);
 1281               return externalizer.invoke(val, new Object[]{ ctx });
 1282           } catch (OpenJPAException ke) {
 1283               throw ke;
 1284           } catch (Exception e) {
 1285               throw new MetaDataException(_loc.get("externalizer-err", this,
 1286                   Exceptions.toString(val), e.toString())).setCause(e);
 1287           }
 1288       }
 1289   
 1290       /**
 1291        * Return the result of passing the given external value through the
 1292        * factory to get the field value. If no factory is present,
 1293        * the given value is returned as-is.
 1294        */
 1295       public Object getFieldValue(Object val, StoreContext ctx) {
 1296           Map fieldValues = getFieldValueMap();
 1297           if (fieldValues != null)
 1298               return fieldValues.get(val);
 1299   
 1300           Member factory = getFactoryMethod();
 1301           if (factory == null)
 1302               return val;
 1303   
 1304           try {
 1305               if (val == null && getNullValue() == NULL_DEFAULT)
 1306                   return AccessController.doPrivileged(
 1307                       J2DoPrivHelper.newInstanceAction(getDeclaredType())); 
 1308   
 1309               // invoke either the constructor for the field type,
 1310               // or the static type.toField(val[, ctx]) method
 1311               if (factory instanceof Constructor) {
 1312                   if (val == null)
 1313                       return null;
 1314                   return ((Constructor) factory).newInstance
 1315                       (new Object[]{ val });
 1316               }
 1317   
 1318               Method meth = (Method) factory;
 1319               if (meth.getParameterTypes().length == 1)
 1320                   return meth.invoke(null, new Object[]{ val });
 1321               return meth.invoke(null, new Object[]{ val, ctx });
 1322           } catch (Exception e) {
 1323               // unwrap cause
 1324               if (e instanceof InvocationTargetException) {
 1325                   Throwable t = ((InvocationTargetException) e).
 1326                       getTargetException();
 1327                   if (t instanceof Error)
 1328                       throw (Error) t;
 1329                   e = (Exception) t;
 1330   
 1331                   // allow null values to cause NPEs and illegal arg exceptions
 1332                   // without error
 1333                   if (val == null && (e instanceof NullPointerException
 1334                       || e instanceof IllegalArgumentException))
 1335                       return null;
 1336               }
 1337   
 1338               if (e instanceof OpenJPAException)
 1339                   throw (OpenJPAException) e;
 1340               if (e instanceof PrivilegedActionException)
 1341                   e = ((PrivilegedActionException) e).getException();
 1342               throw new MetaDataException(_loc.get("factory-err", this,
 1343                   Exceptions.toString(val), e.toString())).setCause(e);
 1344           }
 1345       }
 1346   
 1347       /**
 1348        * The name of this field's externalizer, or null if none.
 1349        */
 1350       public String getExternalizer() {
 1351           return _extName;
 1352       }
 1353   
 1354       /**
 1355        * The name of this field's externalizer, or null if none.
 1356        */
 1357       public void setExternalizer(String externalizer) {
 1358           _extName = externalizer;
 1359           _extMethod = DEFAULT_METHOD;
 1360       }
 1361   
 1362       /**
 1363        * The name of this field's factory, or null if none.
 1364        */
 1365       public String getFactory() {
 1366           return _factName;
 1367       }
 1368   
 1369       /**
 1370        * The name of this field's factory, or null if none.
 1371        */
 1372       public void setFactory(String factory) {
 1373           _factName = factory;
 1374           _factMethod = DEFAULT_METHOD;
 1375       }
 1376   
 1377       /**
 1378        * Properties string mapping field values to external values.
 1379        */
 1380       public String getExternalValues() {
 1381           return _extString;
 1382       }
 1383   
 1384       /**
 1385        * Properties string mapping field values to external values.
 1386        */
 1387       public void setExternalValues(String values) {
 1388           _extString = values;
 1389           _extValues = null;
 1390       }
 1391   
 1392       /**
 1393        * Return the mapping of field values to external values.
 1394        */
 1395       public Map getExternalValueMap() {
 1396           parseExternalValues();
 1397           return _extValues;
 1398       }
 1399   
 1400       /**
 1401        * Return the mapping of external values to field values.
 1402        */
 1403       public Map getFieldValueMap() {
 1404           parseExternalValues();
 1405           return _fieldValues;
 1406       }
 1407   
 1408       /**
 1409        * Parse external values into maps.
 1410        */
 1411       private void parseExternalValues() {
 1412           if (_extValues != Collections.EMPTY_MAP
 1413               && _fieldValues != Collections.EMPTY_MAP)
 1414               return;
 1415   
 1416           if (_extString == null) {
 1417               _extValues = null;
 1418               _fieldValues = null;
 1419               return;
 1420           }
 1421   
 1422           // parse string into options; this takes care of proper trimming etc
 1423           Options values = Configurations.parseProperties(_extString);
 1424           if (values.isEmpty())
 1425               throw new MetaDataException(_loc.get("no-external-values", this,
 1426                   _extString));
 1427   
 1428           Map extValues = new HashMap((int) (values.size() * 1.33 + 1));
 1429           Map fieldValues = new HashMap((int) (values.size() * 1.33 + 1));
 1430           Map.Entry entry;
 1431           Object extValue, fieldValue;
 1432           for (Iterator itr = values.entrySet().iterator(); itr.hasNext();) {
 1433               entry = (Map.Entry) itr.next();
 1434               fieldValue = transform((String) entry.getKey(),
 1435                   getDeclaredTypeCode());
 1436               extValue = transform((String) entry.getValue(), getTypeCode());
 1437   
 1438               extValues.put(fieldValue, extValue);
 1439               fieldValues.put(extValue, fieldValue);
 1440           }
 1441   
 1442           _extValues = extValues;
 1443           _fieldValues = fieldValues;
 1444       }
 1445   
 1446       /**
 1447        * Return the string value converted to the given type code. The string
 1448        * must be non-null and trimmed.
 1449        */
 1450       private Object transform(String val, int typeCode) {
 1451           if ("null".equals(val))
 1452               return null;
 1453   
 1454           switch (typeCode) {
 1455               case JavaTypes.BOOLEAN:
 1456               case JavaTypes.BOOLEAN_OBJ:
 1457                   return Boolean.valueOf(val);
 1458               case JavaTypes.BYTE:
 1459               case JavaTypes.BYTE_OBJ:
 1460                   return Byte.valueOf(val);
 1461               case JavaTypes.INT:
 1462               case JavaTypes.INT_OBJ:
 1463                   return Integer.valueOf(val);
 1464               case JavaTypes.LONG:
 1465               case JavaTypes.LONG_OBJ:
 1466                   return Long.valueOf(val);
 1467               case JavaTypes.SHORT:
 1468               case JavaTypes.SHORT_OBJ:
 1469                   return Short.valueOf(val);
 1470               case JavaTypes.DOUBLE:
 1471               case JavaTypes.DOUBLE_OBJ:
 1472                   return Double.valueOf(val);
 1473               case JavaTypes.FLOAT:
 1474               case JavaTypes.FLOAT_OBJ:
 1475                   return Float.valueOf(val);
 1476               case JavaTypes.CHAR:
 1477               case JavaTypes.CHAR_OBJ:
 1478                   return new Character(val.charAt(0));
 1479               case JavaTypes.STRING:
 1480                   return val;
 1481           }
 1482           throw new MetaDataException(_loc.get("bad-external-type", this));
 1483       }
 1484   
 1485       /**
 1486        * The externalizer method.
 1487        */
 1488       public Method getExternalizerMethod() {
 1489           if (_manage != MANAGE_PERSISTENT)
 1490               return null;
 1491           if (_extMethod == DEFAULT_METHOD) {
 1492               if (_extName != null) {
 1493                   _extMethod = findMethod(_extName);
 1494                   if (_extMethod == null)
 1495                       throw new MetaDataException(_loc.get("bad-externalizer",
 1496                           this, _extName));
 1497               } else
 1498                   _extMethod = null;
 1499           }
 1500           return _extMethod;
 1501       }
 1502   
 1503       /**
 1504        * The factory method or constructor.
 1505        */
 1506       public Member getFactoryMethod() {
 1507           if (_manage != MANAGE_PERSISTENT)
 1508               return null;
 1509           if (_factMethod == DEFAULT_METHOD) {
 1510               if (getExternalizerMethod() == null)
 1511                   _factMethod = null;
 1512               else {
 1513                   try {
 1514                       if (_factName == null)
 1515                           _factMethod = getDeclaredType().getConstructor
 1516                               (new Class[]{ getType() });
 1517                       else
 1518                           _factMethod = findMethod(_factName);
 1519                   } catch (OpenJPAException ke) {
 1520                       throw ke;
 1521                   } catch (Exception e) {
 1522                   }
 1523   
 1524                   if (!(_factMethod instanceof Constructor)
 1525                       && !(_factMethod instanceof Method))
 1526                       throw new MetaDataException(_loc.get("bad-factory", this));
 1527               }
 1528           }
 1529           return _factMethod;
 1530       }
 1531   
 1532       /**
 1533        * Find the method for the specified name. Possible forms are:
 1534        * <ul>
 1535        * <li>toExternalString</li>
 1536        * <li>MyFactoryClass.toExternalString</li>
 1537        * <li>com.company.MyFactoryClass.toExternalString</li>
 1538        * </ul>
 1539        *
 1540        * @param method the name of the method to locate
 1541        * @return the method for invocation
 1542        */
 1543       private Method findMethod(String method) {
 1544           if (StringUtils.isEmpty(method))
 1545               return null;
 1546   
 1547           // get class name and get package name divide on the last '.', so the
 1548           // names don't apply in this case, but the methods do what we want
 1549           String methodName = Strings.getClassName(method);
 1550           String clsName = Strings.getPackageName(method);
 1551   
 1552           Class cls = null;
 1553           Class owner = _owner.getDescribedType();
 1554   
 1555           if (clsName.length() == 0)
 1556               cls = getDeclaredType();
 1557           else if (clsName.equals(owner.getName())
 1558               || clsName.equals(Strings.getClassName(owner)))
 1559               cls = owner;
 1560           else
 1561               cls = JavaTypes.classForName(clsName, this);
 1562   
 1563           // find the named method
 1564           Method[] methods = cls.getMethods();
 1565           Class[] params;
 1566           for (int i = 0; i < methods.length; i++) {
 1567               if (methods[i].getName().equals(methodName)) {
 1568                   params = methods[i].getParameterTypes();
 1569   
 1570                   // static factory methods require one argument or one argument
 1571                   // plus a ctx; non-static methods require zero arguments or
 1572                   // just a ctx
 1573                   if (Modifier.isStatic(methods[i].getModifiers())
 1574                       && (params.length == 1 || (params.length == 2
 1575                       && isStoreContextParameter(params[1]))))
 1576                       return methods[i];
 1577                   if (!Modifier.isStatic(methods[i].getModifiers())
 1578                       && (params.length == 0 || (params.length == 1
 1579                       && isStoreContextParameter(params[0]))))
 1580                       return methods[i];
 1581               }
 1582           }
 1583   
 1584           return null;
 1585       }
 1586   
 1587       /**
 1588        * Return true if the given type is a store context type; we can't
 1589        * use the standard <code>isAssignableFrom</code> because of classloader
 1590        * oddness.
 1591        */
 1592       private static boolean isStoreContextParameter(Class type) {
 1593           return StoreContext.class.getName().equals(type.getName());
 1594       }
 1595   
 1596       public boolean equals(Object other) {
 1597           if (other == this)
 1598               return true;
 1599           if (!(other instanceof FieldMetaData))
 1600               return false;
 1601           return getFullName(true).equals(((FieldMetaData) other).
 1602               getFullName(true));
 1603       }
 1604   
 1605       public int hashCode() {
 1606           return getFullName(true).hashCode();
 1607       }
 1608   
 1609       public int compareTo(Object other) {
 1610           if (other == null)
 1611               return 1;
 1612           return getFullName(true).compareTo(((FieldMetaData) other).
 1613               getFullName(true));
 1614       }
 1615   
 1616       public String toString() {
 1617           return getFullName(true);
 1618       }
 1619   
 1620       ////////////////////////
 1621       // Resolve and validate
 1622       ////////////////////////
 1623   
 1624       /**
 1625        * Resolve mode for this field.
 1626        */
 1627       public int getResolve() {
 1628           return _resMode;
 1629       }
 1630   
 1631       /**
 1632        * Resolve mode for this field.
 1633        */
 1634       public void setResolve(int mode) {
 1635           _resMode = mode;
 1636       }
 1637   
 1638       /**
 1639        * Resolve mode for this field.
 1640        */
 1641       public void setResolve(int mode, boolean on) {
 1642           if (mode == MODE_NONE)
 1643               _resMode = mode;
 1644           else if (on)
 1645               _resMode |= mode;
 1646           else
 1647               _resMode &= ~mode;
 1648       }
 1649   
 1650       /**
 1651        * Resolve and validate metadata. Return true if already resolved.
 1652        */
 1653       public boolean resolve(int mode) {
 1654           if ((_resMode & mode) == mode)
 1655               return true;
 1656           int cur = _resMode;
 1657           _resMode |= mode;
 1658   
 1659           Log log = getRepository().getLog();
 1660           if (log.isTraceEnabled())
 1661               log.trace(_loc.get("resolve-field", _owner + "@"
 1662                   + System.identityHashCode(_owner) + "." + _name));
 1663   
 1664           // we only perform actions for metadata mode
 1665           if ((mode & MODE_META) == 0 || (cur & MODE_META) != 0)
 1666               return false;
 1667   
 1668           Method externalizer = getExternalizerMethod();
 1669           if (externalizer != null)
 1670               setType(externalizer.getReturnType());
 1671   
 1672           // only pass on metadata resolve mode so that metadata is always
 1673           // resolved before any other resolve modes our subclasses pass along
 1674           _val.resolve(MODE_META);
 1675           _key.resolve(MODE_META);
 1676           _elem.resolve(MODE_META);
 1677   
 1678           MetaDataRepository repos = getRepository();
 1679           int validate = repos.getValidate();
 1680           if ((validate & MetaDataRepository.VALIDATE_META) != 0
 1681               && (!ImplHelper.isManagedType(repos.getConfiguration(),
 1682                   _owner.getDescribedType())
 1683               || (validate & MetaDataRepository.VALIDATE_UNENHANCED) == 0)) {
 1684               validateLRS();
 1685               if ((validate & repos.VALIDATE_RUNTIME) == 0)
 1686                   validateSupportedType();
 1687               validateValue();
 1688               validateExtensionKeys();
 1689           }
 1690           return false;
 1691       }
 1692   
 1693       /**
 1694        * Validate that this field can be used for LRS.
 1695        */
 1696       private void validateLRS() {
 1697           if (!isLRS())
 1698               return;
 1699   
 1700           // can't use lrs for arrays
 1701           if (getTypeCode() == JavaTypes.ARRAY)
 1702               throw new MetaDataException(_loc.get("bad-lrs-array", this));
 1703   
 1704           // can't use lrs for extranalized vals
 1705           if (getExternalizerMethod() != null)
 1706               throw new MetaDataException(_loc.get("bad-lrs-extern", this));
 1707   
 1708           // can't use lrs for concrete types
 1709           if (getType() != Collection.class && getType() != Map.class
 1710               && getType() != Set.class)
 1711               throw new MetaDataException(_loc.get("bad-lrs-concrete", this));
 1712       }
 1713   
 1714       /**
 1715        * Validate that this field is supported by the runtime.
 1716        */
 1717       private void validateSupportedType() {
 1718           // log warnings about things we don't handle
 1719           OpenJPAConfiguration conf = getRepository().getConfiguration();
 1720           Collection opts = conf.supportedOptions();
 1721           Log log = conf.getLog(conf.LOG_METADATA);
 1722           switch (getTypeCode()) {
 1723               case JavaTypes.PC:
 1724                   if (isEmbedded() &&
 1725                       !opts.contains(conf.OPTION_EMBEDDED_RELATION)) {
 1726                       setEmbedded(false);
 1727                       if (log.isWarnEnabled())
 1728                           log.warn(_loc.get("cant-embed", this));
 1729                   } else
 1730                   if (isEmbedded() && getDeclaredTypeCode() != JavaTypes.PC) {
 1731                       setEmbedded(false);
 1732                       if (log.isWarnEnabled())
 1733                           log.warn(_loc.get("cant-embed-extern", this));
 1734                   }
 1735                   break;
 1736               case JavaTypes.COLLECTION:
 1737                   if (!opts.contains(conf.OPTION_TYPE_COLLECTION))
 1738                       throw new UnsupportedException(
 1739                           _loc.get("type-not-supported",
 1740                               "Collection", this));
 1741                   if (_elem.isEmbeddedPC()
 1742                       && !opts.contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION))
 1743                   {
 1744                       _elem.setEmbedded(false);
 1745                       if (log.isWarnEnabled())
 1746                           log.warn(_loc.get("cant-embed-element", this));
 1747                   }
 1748                   break;
 1749               case JavaTypes.ARRAY:
 1750                   if (!opts.contains(conf.OPTION_TYPE_ARRAY))
 1751                       throw new UnsupportedException(
 1752                           _loc.get("type-not-supported",
 1753                               "Array", this));
 1754                   if (_elem.isEmbeddedPC()
 1755                       && !opts.contains(conf.OPTION_EMBEDDED_COLLECTION_RELATION))
 1756                   {
 1757                       _elem.setEmbedded(false);
 1758                       if (log.isWarnEnabled())
 1759                           log.warn(_loc.get("cant-embed-element", this));
 1760                   }
 1761                   break;
 1762               case JavaTypes.MAP:
 1763                   if (!opts.contains(conf.OPTION_TYPE_MAP))
 1764                       throw new UnsupportedException(
 1765                           _loc.get("type-not-supported",
 1766                               "Map", this));
 1767                   if (_elem.isEmbeddedPC()
 1768                       && !opts.contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
 1769                       _elem.setEmbedded(false);
 1770                       if (log.isWarnEnabled())
 1771                           log.warn(_loc.get("cant-embed-element", this));
 1772                   }
 1773                   if (_key.isEmbeddedPC()
 1774                       && !opts.contains(conf.OPTION_EMBEDDED_MAP_RELATION)) {
 1775                       _key.setEmbedded(false);
 1776                       if (log.isWarnEnabled())
 1777                           log.warn(_loc.get("cant-embed-key", this));
 1778                   }
 1779                   break;
 1780           }
 1781       }
 1782   
 1783       /**
 1784        * Validate our value strategy.
 1785        */
 1786       private void validateValue() {
 1787           if (getExternalizerMethod() != null && getExternalValueMap() != null)
 1788               throw new MetaDataException(_loc.get("extern-externvalues", this));
 1789           if (getValueStrategy() == ValueStrategies.SEQUENCE
 1790               && getValueSequenceName() == null)
 1791               throw new MetaDataException(_loc.get("no-seq-name", this));
 1792           ValueStrategies.assertSupported(getValueStrategy(), this,
 1793               "value strategy");
 1794       }
 1795   
 1796       /**
 1797        * Copy state from the given field to this one. Do not copy mapping
 1798        * information.
 1799        */
 1800       public void copy(FieldMetaData field) {
 1801           super.copy(field);
 1802   
 1803           _intermediate = field.usesIntermediate();
 1804           _implData = field.usesImplData();
 1805   
 1806           // copy field-level info; use get methods to force resolution of
 1807           // lazy data
 1808           _proxyClass = field.getProxyType();
 1809           _initializer = field.getInitializer();
 1810           _transient = field.isTransient();
 1811           _nullValue = field.getNullValue();
 1812           _manage = field.getManagement();
 1813           _explicit = field.isExplicit();
 1814           _extName = field.getExternalizer();
 1815           _extMethod = DEFAULT_METHOD;
 1816           _factName = field.getFactory();
 1817           _factMethod = DEFAULT_METHOD;
 1818           _extString = field.getExternalValues();
 1819           _extValues = Collections.EMPTY_MAP;
 1820           _fieldValues = Collections.EMPTY_MAP;
 1821           _primKey = field.isPrimaryKey();
 1822           _backingMember = field._backingMember;
 1823           _enumField = field._enumField;
 1824           _lobField = field._lobField;
 1825           _serializableField = field._serializableField;
 1826           _generated = field._generated;
 1827   
 1828           // embedded fields can't be versions
 1829           if (_owner.getEmbeddingMetaData() == null && _version == null)
 1830               _version = (field.isVersion()) ? Boolean.TRUE : Boolean.FALSE;
 1831   
 1832           // only copy this data if not already set explicitly in this instance
 1833           if (_dfg == 0) {
 1834               _dfg = (field.isInDefaultFetchGroup()) ? DFG_TRUE : DFG_FALSE;
 1835               if (field.isDefaultFetchGroupExplicit())
 1836                   _dfg |= DFG_EXPLICIT;
 1837           }
 1838           if (_fgSet == null && field._fgSet != null)
 1839               _fgSet = new HashSet(field._fgSet);
 1840           if (_lfg == null)
 1841               _lfg = field.getLoadFetchGroup();
 1842           if (_lrs == null)
 1843               _lrs = (field.isLRS()) ? Boolean.TRUE : Boolean.FALSE;
 1844           if (_valStrategy == -1)
 1845               _valStrategy = field.getValueStrategy();
 1846           if (_upStrategy == -1)
 1847               _upStrategy = field.getUpdateStrategy();
 1848           if (ClassMetaData.DEFAULT_STRING.equals(_seqName)) {
 1849               _seqName = field.getValueSequenceName();
 1850               _seqMeta = null;
 1851           }
 1852           if (ClassMetaData.DEFAULT_STRING.equals(_inverse))
 1853               _inverse = field.getInverse();
 1854   
 1855           // copy value metadata
 1856           _val.copy(field);
 1857           _key.copy(field.getKey());
 1858           _elem.copy(field.getElement());
 1859       }
 1860   
 1861       protected void addExtensionKeys(Collection exts) {
 1862           getRepository().getMetaDataFactory().addFieldExtensionKeys(exts);
 1863       }
 1864   
 1865       ///////////////
 1866       // Commentable
 1867       ///////////////
 1868   
 1869       public String[] getComments() {
 1870           return (_comments == null) ? EMPTY_COMMENTS : _comments;
 1871       }
 1872   
 1873       public void setComments(String[] comments) {
 1874           _comments = comments;
 1875       }
 1876   
 1877       ////////////////////////////////
 1878       // ValueMetaData implementation
 1879       ////////////////////////////////
 1880   
 1881       public FieldMetaData getFieldMetaData() {
 1882           return this;
 1883       }
 1884   
 1885       public Class getType() {
 1886           return _val.getType();
 1887       }
 1888   
 1889       public void setType(Class type) {
 1890           _val.setType(type);
 1891           if (type.isArray())
 1892               _elem.setType(type.getComponentType());
 1893           else if (type == Properties.class) {
 1894               _key.setType(String.class);
 1895               _elem.setType(String.class);
 1896           }
 1897       }
 1898   
 1899       public int getTypeCode() {
 1900           return _val.getTypeCode();
 1901       }
 1902   
 1903       public void setTypeCode(int code) {
 1904           _val.setTypeCode(code);
 1905       }
 1906   
 1907       public boolean isTypePC() {
 1908           return _val.isTypePC();
 1909       }
 1910   
 1911       public ClassMetaData getTypeMetaData() {
 1912           return _val.getTypeMetaData();
 1913       }
 1914   
 1915       public Class getDeclaredType() {
 1916           return _val.getDeclaredType();
 1917       }
 1918   
 1919       public void setDeclaredType(Class type) {
 1920           _val.setDeclaredType(type);
 1921           if (type.isArray())
 1922               _elem.setDeclaredType(type.getComponentType());
 1923           else if (type == Properties.class) {
 1924               _key.setDeclaredType(String.class);
 1925               _elem.setDeclaredType(String.class);
 1926           }
 1927       }
 1928   
 1929       public int getDeclaredTypeCode() {
 1930           return _val.getDeclaredTypeCode();
 1931       }
 1932   
 1933       public void setDeclaredTypeCode(int type) {
 1934           _val.setDeclaredTypeCode(type);
 1935       }
 1936   
 1937       public boolean isDeclaredTypePC() {
 1938           return _val.isDeclaredTypePC();
 1939       }
 1940   
 1941       public ClassMetaData getDeclaredTypeMetaData() {
 1942           return _val.getDeclaredTypeMetaData();
 1943       }
 1944   
 1945       public boolean isEmbedded() {
 1946           return _val.isEmbedded();
 1947       }
 1948   
 1949       public void setEmbedded(boolean embedded) {
 1950           _val.setEmbedded(embedded);
 1951       }
 1952   
 1953       public boolean isEmbeddedPC() {
 1954           return _val.isEmbeddedPC();
 1955       }
 1956   
 1957       public ClassMetaData getEmbeddedMetaData() {
 1958           return _val.getEmbeddedMetaData();
 1959       }
 1960   
 1961       public ClassMetaData addEmbeddedMetaData() {
 1962           return _val.addEmbeddedMetaData();
 1963       }
 1964   
 1965       public int getCascadeDelete() {
 1966           return _val.getCascadeDelete();
 1967       }
 1968   
 1969       public void setCascadeDelete(int delete) {
 1970           _val.setCascadeDelete(delete);
 1971       }
 1972   
 1973       public int getCascadePersist() {
 1974           return _val.getCascadePersist();
 1975       }
 1976   
 1977       public void setCascadePersist(int persist) {
 1978           _val.setCascadePersist(persist);
 1979       }
 1980   
 1981       public int getCascadeAttach() {
 1982           return _val.getCascadeAttach();
 1983       }
 1984   
 1985       public void setCascadeAttach(int attach) {
 1986           _val.setCascadeAttach(attach);
 1987       }
 1988   
 1989       public int getCascadeRefresh() {
 1990           return _val.getCascadeRefresh();
 1991       }
 1992   
 1993       public void setCascadeRefresh(int refresh) {
 1994           _val.setCascadeRefresh(refresh);
 1995       }
 1996   
 1997       public boolean isSerialized() {
 1998           return _val.isSerialized();
 1999       }
 2000   
 2001       public void setSerialized(boolean serialized) {
 2002           _val.setSerialized(serialized);
 2003       }
 2004   
 2005       public String getValueMappedBy() {
 2006           return _val.getValueMappedBy();
 2007       }
 2008   
 2009       public void setValueMappedBy(String mapped) {
 2010           _val.setValueMappedBy(mapped);
 2011       }
 2012   
 2013       public FieldMetaData getValueMappedByMetaData ()
 2014   	{
 2015   		return _val.getValueMappedByMetaData ();
 2016   	}
 2017   
 2018   	public Class getTypeOverride ()
 2019   	{
 2020   		return _val.getTypeOverride ();
 2021   	}
 2022   
 2023   	public void setTypeOverride (Class type)
 2024   	{
 2025   		_val.setTypeOverride (type);
 2026   	}
 2027   
 2028   	public void copy (ValueMetaData vmd)
 2029   	{
 2030   		_val.copy (vmd);
 2031   	}
 2032   
 2033       /**
 2034        * Check if this field is used by other field as "order by" value.
 2035        *
 2036        * @since 1.1.0
 2037        */
 2038       public boolean isUsedInOrderBy() {
 2039       	return _usedInOrderBy;
 2040       }
 2041       
 2042       /**
 2043        * Whether this field is used by other field as "order by" value .
 2044        *
 2045        * @since 1.1.0
 2046        */
 2047       public void setUsedInOrderBy(boolean isUsed) {
 2048       	_usedInOrderBy = isUsed;
 2049       }
 2050       
 2051       /**
 2052        * Serializable wrapper around a {@link Method} or {@link Field}. For 
 2053        * space considerations, this does not support {@link Constructor}s.
 2054        */
 2055   	public static class MemberProvider
 2056           implements Externalizable {
 2057   
 2058           private transient Member _member;
 2059   
 2060           public MemberProvider() {
 2061               // for externalization
 2062           }
 2063   
 2064           MemberProvider(Member member) {
 2065               if (member instanceof Constructor)
 2066                   throw new IllegalArgumentException();
 2067   
 2068               _member = member;
 2069           }
 2070   
 2071           public Member getMember() {
 2072               return _member;
 2073           }
 2074   
 2075           public void readExternal(ObjectInput in)
 2076               throws IOException, ClassNotFoundException {
 2077               boolean isField = in.readBoolean();
 2078               Class cls = (Class) in.readObject();
 2079               String memberName = (String) in.readObject();
 2080               try {
 2081                   if (isField)
 2082                       _member = (Field) AccessController.doPrivileged(
 2083                           J2DoPrivHelper.getDeclaredFieldAction(
 2084                               cls, memberName)); 
 2085                   else {
 2086                       Class[] parameterTypes = (Class[]) in.readObject();
 2087                       _member = (Method) AccessController.doPrivileged(
 2088                           J2DoPrivHelper.getDeclaredMethodAction(
 2089                               cls, memberName, parameterTypes));
 2090                   }
 2091               } catch (SecurityException e) {
 2092                   IOException ioe = new IOException(e.getMessage());
 2093                   ioe.initCause(e);
 2094                   throw ioe;
 2095               } catch (PrivilegedActionException pae) {
 2096                   IOException ioe = new IOException(
 2097                       pae.getException().getMessage());
 2098                   ioe.initCause(pae);
 2099                   throw ioe;
 2100               }
 2101           }
 2102   
 2103           public void writeExternal(ObjectOutput out)
 2104               throws IOException {
 2105               boolean isField = _member instanceof Field;
 2106               out.writeBoolean(isField);
 2107               out.writeObject(_member.getDeclaringClass());
 2108               out.writeObject(_member.getName());
 2109               if (!isField)
 2110                   out.writeObject(((Method) _member).getParameterTypes());
 2111           }
 2112       }
 2113   
 2114       public boolean isValueGenerated() {
 2115           return _generated;
 2116       }
 2117   
 2118       public void setValueGenerated(boolean generated) {
 2119           this._generated = generated;
 2120       }
 2121   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » meta » [javadoc | source]