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.File;
   22   import java.lang.reflect.Field;
   23   import java.lang.reflect.Method;
   24   import java.lang.reflect.Modifier;
   25   import java.security.AccessController;
   26   import java.util.ArrayList;
   27   import java.util.Arrays;
   28   import java.util.Collection;
   29   import java.util.Comparator;
   30   import java.util.HashMap;
   31   import java.util.HashSet;
   32   import java.util.Iterator;
   33   import java.util.List;
   34   import java.util.Map;
   35   import java.util.Set;
   36   import java.util.TreeMap;
   37   
   38   import org.apache.commons.lang.StringUtils;
   39   import org.apache.openjpa.conf.OpenJPAConfiguration;
   40   import org.apache.openjpa.datacache.DataCache;
   41   import org.apache.openjpa.enhance.PCRegistry;
   42   import org.apache.openjpa.enhance.Reflection;
   43   import org.apache.openjpa.enhance.PersistenceCapable;
   44   import org.apache.openjpa.lib.log.Log;
   45   import org.apache.openjpa.lib.meta.SourceTracker;
   46   import org.apache.openjpa.lib.util.J2DoPrivHelper;
   47   import org.apache.openjpa.lib.util.Localizer;
   48   import org.apache.openjpa.lib.xml.Commentable;
   49   import org.apache.openjpa.util.BigDecimalId;
   50   import org.apache.openjpa.util.BigIntegerId;
   51   import org.apache.openjpa.util.ByteId;
   52   import org.apache.openjpa.util.CharId;
   53   import org.apache.openjpa.util.DateId;
   54   import org.apache.openjpa.util.DoubleId;
   55   import org.apache.openjpa.util.FloatId;
   56   import org.apache.openjpa.util.GeneralException;
   57   import org.apache.openjpa.util.IntId;
   58   import org.apache.openjpa.util.InternalException;
   59   import org.apache.openjpa.util.LongId;
   60   import org.apache.openjpa.util.MetaDataException;
   61   import org.apache.openjpa.util.ObjectId;
   62   import org.apache.openjpa.util.OpenJPAId;
   63   import org.apache.openjpa.util.ShortId;
   64   import org.apache.openjpa.util.StringId;
   65   import org.apache.openjpa.util.UnsupportedException;
   66   import org.apache.openjpa.util.ImplHelper;
   67   import serp.util.Strings;
   68   
   69   /**
   70    * Contains metadata about a persistent type.
   71    * This metadata is available both at enhancement time and runtime.
   72    *  Note that this class employs aggressive caching, and therefore it is
   73    * important to finalize the configuration of field metadatas before invoking
   74    * methods that depend on that configuration, such as
   75    * {@link #getPrimaryKeyFields}.
   76    *
   77    * @author Abe White
   78    */
   79   public class ClassMetaData
   80       extends Extensions
   81       implements Comparable, SourceTracker, MetaDataContext, MetaDataModes,
   82       Commentable {
   83   
   84       /**
   85        * Unkonwn identity type.
   86        */
   87       public static final int ID_UNKNOWN = 0;
   88   
   89       /**
   90        * Datastore identity type.
   91        */
   92       public static final int ID_DATASTORE = 1;
   93   
   94       /**
   95        * Application identity type.
   96        */
   97       public static final int ID_APPLICATION = 2;
   98   
   99       /**
  100        * Unknown access type.
  101        */
  102       public static final int ACCESS_UNKNOWN = 0;
  103   
  104       /**
  105        * Persistent attributes are accessed via direct field access. Bit flag.
  106        */
  107       public static final int ACCESS_FIELD = 2 << 0;
  108   
  109       /**
  110        * Persistent attributes are accessed via setters and getters. Bit flag.
  111        */
  112       public static final int ACCESS_PROPERTY = 2 << 1;
  113   
  114       /**
  115        * Value for using a synthetic detached state field, which is the default.
  116        */
  117       public static final String SYNTHETIC = "`syn";
  118   
  119       protected static final String DEFAULT_STRING = "`";
  120   
  121       private static final Localizer _loc = Localizer.forPackage
  122           (ClassMetaData.class);
  123   
  124       private static final FetchGroup[] EMPTY_FETCH_GROUP_ARRAY
  125           = new FetchGroup[0];
  126       private static final String[] EMPTY_STRING_ARRAY = new String[0];
  127   
  128       private MetaDataRepository _repos;
  129       private transient ClassLoader _loader = null;
  130   
  131       private final ValueMetaData _owner;
  132       private final LifecycleMetaData _lifeMeta = new LifecycleMetaData(this);
  133       private File _srcFile = null;
  134       private int _srcType = SRC_OTHER;
  135       private String[] _comments = null;
  136       private int _listIndex = -1;
  137       private int _srcMode = MODE_META | MODE_MAPPING;
  138       private int _resMode = MODE_NONE;
  139   
  140       private Class _type = Object.class;
  141       private final Map _fieldMap = new TreeMap();
  142       private Map _supFieldMap = null;
  143       private boolean _defSupFields = false;
  144       private Collection _staticFields = null;
  145       private int[] _fieldDataTable = null;
  146       private Map _fgMap = null;
  147   
  148       ////////////////////////////////////////////////////////////////////
  149       // Note: if you add additional state, make sure to add it to copy()
  150       ////////////////////////////////////////////////////////////////////
  151   
  152       private Class _objectId = null;
  153       private Boolean _objectIdShared = null;
  154       private Boolean _openjpaId = null;
  155       private Boolean _extent = null;
  156       private Boolean _embedded = null;
  157       private Boolean _interface = null;
  158       private Class _impl = null;
  159       private List _interfaces = null;
  160       private final Map _ifaceMap = new HashMap();
  161       private int _identity = ID_UNKNOWN;
  162       private int _idStrategy = ValueStrategies.NONE;
  163       private int _accessType = ACCESS_UNKNOWN;
  164   
  165       private String _seqName = DEFAULT_STRING;
  166       private SequenceMetaData _seqMeta = null;
  167       private String _cacheName = DEFAULT_STRING;
  168       private int _cacheTimeout = Integer.MIN_VALUE;
  169       private Boolean _detachable = null;
  170       private String _detachState = DEFAULT_STRING;
  171       private String _alias = null;
  172       private int _versionIdx = Integer.MIN_VALUE;
  173   
  174       private Class _super = null;
  175       private ClassMetaData _superMeta = null;
  176       private Class[] _subs = null;
  177       private ClassMetaData[] _subMetas = null;
  178       private ClassMetaData[] _mapSubMetas = null;
  179   
  180       private FieldMetaData[] _fields = null;
  181       private FieldMetaData[] _unmgdFields = null;
  182       private FieldMetaData[] _allFields = null;
  183       private FieldMetaData[] _allPKFields = null;
  184       private FieldMetaData[] _allDFGFields = null;
  185       private FieldMetaData[] _definedFields = null;
  186       private FieldMetaData[] _listingFields = null;
  187       private FieldMetaData[] _allListingFields = null;
  188       private FetchGroup[] _fgs = null;
  189       private FetchGroup[] _customFGs = null;
  190       private boolean _intercepting = false;
  191   
  192       /**
  193        * Constructor. Supply described type and repository.
  194        */
  195       protected ClassMetaData(Class type, MetaDataRepository repos) {
  196           _repos = repos;
  197           _owner = null;
  198           setDescribedType(type);
  199       }
  200   
  201       /**
  202        * Embedded constructor. Supply embedding value.
  203        */
  204       protected ClassMetaData(ValueMetaData owner) {
  205           _owner = owner;
  206           _repos = owner.getRepository();
  207           setEnvClassLoader(owner.getFieldMetaData().getDefiningMetaData().
  208               getEnvClassLoader());
  209       }
  210   
  211       /**
  212        * Return the owning repository.
  213        */
  214       public MetaDataRepository getRepository() {
  215           return _repos;
  216       }
  217   
  218       /**
  219        * If this metadata is for an embedded object, returning the owning value.
  220        */
  221       public ValueMetaData getEmbeddingMetaData() {
  222           return _owner;
  223       }
  224   
  225       /**
  226        * The persistence capable class described by this metadata.
  227        */
  228       public Class getDescribedType() {
  229           return _type;
  230       }
  231   
  232       /**
  233        * Set the class descibed by this metadata. The type may be reset when
  234        * an embedded value changes its declared type.
  235        */
  236       protected void setDescribedType(Class type) {
  237           if (type.getSuperclass() != null && "java.lang.Enum".equals
  238               (type.getSuperclass().getName()))
  239               throw new MetaDataException(_loc.get("enum", type));
  240           _type = type;
  241           if (PersistenceCapable.class.isAssignableFrom(type))
  242               setIntercepting(true);
  243       }
  244   
  245       /**
  246        * The environmental loader used when loading this metadata.
  247        * The class metadata should use this loader when loading metadata for
  248        * its superclass and field types.
  249        */
  250       public ClassLoader getEnvClassLoader() {
  251           return _loader;
  252       }
  253   
  254       /**
  255        * The class environmental loader used when loading this metadata.
  256        * The class metadata should use this loader when loading metadata for
  257        * its superclass and field types.
  258        */
  259       public void setEnvClassLoader(ClassLoader loader) {
  260           _loader = loader;
  261       }
  262   
  263       /**
  264        * The persistence capable superclass of the described type.
  265        */
  266       public Class getPCSuperclass() {
  267           return _super;
  268       }
  269   
  270       /**
  271        * The persistence capable superclass of the described type.
  272        */
  273       public void setPCSuperclass(Class pc) {
  274           clearAllFieldCache();
  275           _super = pc;
  276       }
  277   
  278       /**
  279        * The metadata for this class' superclass.
  280        */
  281       public ClassMetaData getPCSuperclassMetaData() {
  282           if (_superMeta == null && _super != null) {
  283               if (_owner != null) {
  284                   _superMeta = _repos.newEmbeddedClassMetaData(_owner);
  285                   _superMeta.setDescribedType(_super);
  286               } else
  287                   _superMeta = _repos.getMetaData(_super, _loader, true);
  288           }
  289           return _superMeta;
  290       }
  291   
  292       /**
  293        * The metadata for this class' superclass.
  294        */
  295       public void setPCSuperclassMetaData(ClassMetaData meta) {
  296           clearAllFieldCache();
  297           _superMeta = meta;
  298           if (meta != null)
  299               setPCSuperclass(meta.getDescribedType());
  300       }
  301   
  302       /**
  303        * Whether this class is mapped to the datastore. By default, only
  304        * returns false if class is embedded-only, but subclasses might override
  305        * to allow unmapped other types.
  306        */
  307       public boolean isMapped() {
  308           return _embedded != Boolean.TRUE;
  309       }
  310   
  311       /**
  312        * Return the closest mapped superclass.
  313        */
  314       public ClassMetaData getMappedPCSuperclassMetaData() {
  315           ClassMetaData sup = getPCSuperclassMetaData();
  316           if (sup == null || sup.isMapped())
  317               return sup;
  318           return sup.getMappedPCSuperclassMetaData();
  319       }
  320   
  321       /**
  322        * Return the known persistence capable subclasses of the described type,
  323        * or empty array if none or if this is embedded metadata.
  324        */
  325       public Class[] getPCSubclasses() {
  326           if (_owner != null)
  327               return _repos.EMPTY_CLASSES;
  328   
  329           _repos.processRegisteredClasses(_loader);
  330           if (_subs == null) {
  331               Collection subs = _repos.getPCSubclasses(_type);
  332               _subs = (Class[]) subs.toArray(new Class[subs.size()]);
  333           }
  334           return _subs;
  335       }
  336   
  337       /**
  338        * Return the metadata for the known persistence capable subclasses of
  339        * the described type, or empty array if none or if this is embedded
  340        * metadata.
  341        */
  342       public ClassMetaData[] getPCSubclassMetaDatas() {
  343           if (_owner != null)
  344               return _repos.EMPTY_METAS;
  345   
  346           Class[] subs = getPCSubclasses(); // checks for new
  347           if (_subMetas == null) {
  348               if (subs.length == 0)
  349                   _subMetas = _repos.EMPTY_METAS;
  350               else {
  351                   ClassMetaData[] metas = _repos.newClassMetaDataArray
  352                       (subs.length);
  353                   for (int i = 0; i < subs.length; i++)
  354                       metas[i] = _repos.getMetaData(subs[i], _loader, true);
  355                   _subMetas = metas;
  356               }
  357           }
  358           return _subMetas;
  359       }
  360   
  361       /**
  362        * Return all mapped subclasses.
  363        */
  364       public ClassMetaData[] getMappedPCSubclassMetaDatas() {
  365           if (_owner != null)
  366               return _repos.EMPTY_METAS;
  367   
  368           ClassMetaData[] subs = getPCSubclassMetaDatas(); // checks for new
  369           if (_mapSubMetas == null) {
  370               if (subs.length == 0)
  371                   _mapSubMetas = subs;
  372               else {
  373                   List mapped = new ArrayList(subs.length);
  374                   for (int i = 0; i < subs.length; i++)
  375                       if (subs[i].isMapped())
  376                           mapped.add(subs[i]);
  377                   _mapSubMetas = (ClassMetaData[]) mapped.toArray
  378                       (_repos.newClassMetaDataArray(mapped.size()));
  379               }
  380           }
  381           return _mapSubMetas;
  382       }
  383   
  384       /**
  385        * The type of identity being used. This will be one of:
  386        * <ul>
  387        * <li>{@link #ID_UNKNOWN}: unknown identity type</li>
  388        * <li>{@link #ID_DATASTORE}: identity managed by the data store and
  389        * independent	of the fields of the instance</li>
  390        * <li>{@link #ID_APPLICATION}: identity managed by the application and
  391        * defined by one or more fields of the instance</li>
  392        * </ul> If unspecified, defaults to {@link #ID_DATASTORE} if there are no
  393        * primary key fields, and {@link #ID_APPLICATION} otherwise.
  394        */
  395       public int getIdentityType() {
  396           if (_identity == ID_UNKNOWN) {
  397               ClassMetaData sup = getPCSuperclassMetaData();
  398               if (sup != null && sup.getIdentityType() != ID_UNKNOWN)
  399                   _identity = sup.getIdentityType();
  400               else if (getPrimaryKeyFields().length > 0)
  401                   _identity = ID_APPLICATION;
  402               else if (isMapped())
  403                   _identity = ID_DATASTORE;
  404               else
  405                   _identity = _repos.getMetaDataFactory().getDefaults().
  406                       getDefaultIdentityType();
  407           }
  408           return _identity;
  409       }
  410   
  411       /**
  412        * The type of identity being used. This will be one of:
  413        * <ul>
  414        * <li>{@link #ID_UNKNOWN}: unknown identity type</li>
  415        * <li>{@link #ID_DATASTORE}: identity managed by the data store and
  416        * independent	of the fields of the instance</li>
  417        * <li>{@link #ID_APPLICATION}: identity managed by the application and
  418        * defined by one or more fields of the instance</li>
  419        * </ul> If unspecified, defaults to {@link #ID_DATASTORE} if there are no
  420        * primary key fields, and {@link #ID_APPLICATION} otherwise.
  421        */
  422       public void setIdentityType(int type) {
  423           _identity = type;
  424           if (type != ID_APPLICATION) {
  425               _objectId = null;
  426               _openjpaId = null;
  427           }
  428       }
  429   
  430       /**
  431        * The metadata-specified class to use for the object ID.
  432        */
  433       public Class getObjectIdType() {
  434           if (_objectId != null)
  435               return _objectId;
  436           if (getIdentityType() != ID_APPLICATION)
  437               return null;
  438           ClassMetaData sup = getPCSuperclassMetaData();
  439           if (sup != null && sup.getIdentityType() != ID_UNKNOWN) {
  440               _objectId = sup.getObjectIdType();
  441               return _objectId;
  442           }
  443   
  444           // figure out openjpa identity type based on primary key field
  445           FieldMetaData[] pks = getPrimaryKeyFields();
  446           if (pks.length != 1)
  447               return null;
  448           switch (pks[0].getObjectIdFieldTypeCode()) {
  449               case JavaTypes.BYTE:
  450               case JavaTypes.BYTE_OBJ:
  451                   _objectId = ByteId.class;
  452                   break;
  453               case JavaTypes.CHAR:
  454               case JavaTypes.CHAR_OBJ:
  455                   _objectId = CharId.class;
  456                   break;
  457               case JavaTypes.DOUBLE:
  458               case JavaTypes.DOUBLE_OBJ:
  459                   _objectId = DoubleId.class;
  460                   break;
  461               case JavaTypes.FLOAT:
  462               case JavaTypes.FLOAT_OBJ:
  463                   _objectId = FloatId.class;
  464                   break;
  465               case JavaTypes.INT:
  466               case JavaTypes.INT_OBJ:
  467                   _objectId = IntId.class;
  468                   break;
  469               case JavaTypes.LONG:
  470               case JavaTypes.LONG_OBJ:
  471                   _objectId = LongId.class;
  472                   break;
  473               case JavaTypes.SHORT:
  474               case JavaTypes.SHORT_OBJ:
  475                   _objectId = ShortId.class;
  476                   break;
  477               case JavaTypes.STRING:
  478                   _objectId = StringId.class;
  479                   break;
  480               case JavaTypes.DATE:
  481                   _objectId = DateId.class;
  482                   break;
  483               case JavaTypes.OID:
  484               case JavaTypes.OBJECT:
  485                   _objectId = ObjectId.class;
  486                   break;
  487               case JavaTypes.BIGDECIMAL:
  488                   _objectId = BigDecimalId.class;
  489                   break;
  490               case JavaTypes.BIGINTEGER:
  491                   _objectId = BigIntegerId.class;
  492                   break;
  493           }
  494           return _objectId;
  495       }
  496   
  497       /**
  498        * The metadata-specified class to use for the object ID.
  499        */
  500       public void setObjectIdType(Class cls, boolean shared) {
  501           _objectId = null;
  502           _openjpaId = null;
  503           _objectIdShared = null;
  504           if (cls != null) {
  505               // don't let people assign OpenJPAId types; safer to calculate it
  506               // ourselves
  507               setIdentityType(ID_APPLICATION);
  508               if (!OpenJPAId.class.isAssignableFrom(cls)) {
  509                   _objectId = cls;
  510                   _objectIdShared = (shared) ? Boolean.TRUE : Boolean.FALSE;
  511               }
  512           }
  513       }
  514   
  515       /**
  516        * Whether this type uses an application identity class that is shared
  517        * with other classes, and is therefore wrapped in an {@link ObjectId}.
  518        */
  519       public boolean isObjectIdTypeShared() {
  520           if (_objectIdShared != null)
  521               return _objectIdShared.booleanValue();
  522           if (_super != null)
  523               return getPCSuperclassMetaData().isObjectIdTypeShared();
  524           return isOpenJPAIdentity();
  525       }
  526   
  527       /**
  528        * Whether this type uses OpenJPA identity.
  529        */
  530       public boolean isOpenJPAIdentity() {
  531           if (_openjpaId == null) {
  532               Class cls = getObjectIdType();
  533               if (cls == null)
  534                   return false;
  535               _openjpaId = (OpenJPAId.class.isAssignableFrom(cls)) ? Boolean.TRUE
  536                   : Boolean.FALSE;
  537           }
  538           return _openjpaId.booleanValue();
  539       }
  540   
  541       /**
  542        * The strategy to use for datastore identity generation.
  543        * One of the constants from {@link ValueStrategies}.
  544        */
  545       public int getIdentityStrategy() {
  546           if (getIdentityType() == ID_DATASTORE
  547               && _idStrategy == ValueStrategies.NONE) {
  548               ClassMetaData sup = getPCSuperclassMetaData();
  549               if (sup != null && sup.getIdentityType() != ID_UNKNOWN)
  550                   _idStrategy = sup.getIdentityStrategy();
  551               else
  552                   _idStrategy = ValueStrategies.NATIVE;
  553           }
  554           return _idStrategy;
  555       }
  556   
  557       /**
  558        * The strategy to use for datastore identity generation.
  559        * One of the constants from {@link ValueStrategies}.
  560        */
  561       public void setIdentityStrategy(int strategy) {
  562           _idStrategy = strategy;
  563           if (strategy != ValueStrategies.SEQUENCE)
  564               setIdentitySequenceName(null);
  565       }
  566   
  567       /**
  568        * The datastore identity sequence name, or null for none.
  569        */
  570       public String getIdentitySequenceName() {
  571           if (DEFAULT_STRING.equals(_seqName)) {
  572               if (_super != null)
  573                   _seqName = getPCSuperclassMetaData().getIdentitySequenceName();
  574               else
  575                   _seqName = null;
  576           }
  577           return _seqName;
  578       }
  579   
  580       /**
  581        * The datastore identity sequence name, or null for none.
  582        */
  583       public void setIdentitySequenceName(String seqName) {
  584           _seqName = seqName;
  585           _seqMeta = null;
  586           if (seqName != null)
  587               setIdentityStrategy(ValueStrategies.SEQUENCE);
  588       }
  589   
  590       /**
  591        * Metadata for the datastore identity sequence.
  592        */
  593       public SequenceMetaData getIdentitySequenceMetaData() {
  594           if (_seqMeta == null && getIdentitySequenceName() != null)
  595               _seqMeta = _repos.getSequenceMetaData(this,
  596                   getIdentitySequenceName(), true);
  597           return _seqMeta;
  598       }
  599   
  600       /**
  601        * Information about lifecycle callbacks for this class.
  602        */
  603       public LifecycleMetaData getLifecycleMetaData() {
  604           return _lifeMeta;
  605       }
  606   
  607       /**
  608        * Returns the alias for the described type, or <code>null</code> if none
  609        * has been set.
  610        * 
  611        * @see #setTypeAlias
  612        */
  613       public String getTypeAlias() {
  614           if (_alias == null)
  615               _alias = Strings.getClassName(_type);
  616           return _alias;
  617       }
  618   
  619       /**
  620        * Sets the alias for the described type. The alias can be
  621        * any arbitrary string that the implementation can later use to
  622        * refer to the class. Note that at runtime, only the alias
  623        * computed when the persistent type was enhanced is used.
  624        *
  625        * @param alias the alias name to apply to the described type
  626        */
  627       public void setTypeAlias(String alias) {
  628           _alias = alias;
  629       }
  630   
  631       /**
  632        * The access type used by this class. Either {@link #ACCESS_FIELD}
  633        * or {@link #ACCESS_PROPERTY}.
  634        */
  635       public int getAccessType() {
  636           if (_accessType == ACCESS_UNKNOWN) {
  637               ClassMetaData sup = getPCSuperclassMetaData();
  638               if (sup != null)
  639                   return sup.getAccessType();
  640               else {
  641                   return getRepository().getMetaDataFactory().
  642                       getDefaults().getDefaultAccessType();
  643               }
  644           }
  645           return _accessType;
  646       }
  647   
  648       /**
  649        * The access type used by this class. Must be either
  650        * {@link #ACCESS_FIELD} or {@link #ACCESS_PROPERTY}.
  651        */
  652       public void setAccessType(int type) {
  653           _accessType = type;
  654       }
  655   
  656       /**
  657        * Whether the type requires extent management.
  658        */
  659       public boolean getRequiresExtent() {
  660           if (_owner != null || isEmbeddedOnly())
  661               return false;
  662   
  663           if (_extent == null) {
  664               ClassMetaData sup = getPCSuperclassMetaData();
  665               if (sup != null)
  666                   _extent = (sup.getRequiresExtent()) ? Boolean.TRUE
  667                       : Boolean.FALSE;
  668               else
  669                   _extent = Boolean.TRUE;
  670           }
  671           return _extent.booleanValue();
  672       }
  673   
  674       /**
  675        * Whether the type requires extent management.
  676        */
  677       public void setRequiresExtent(boolean req) {
  678           _extent = (req) ? Boolean.TRUE : Boolean.FALSE;
  679       }
  680   
  681       /**
  682        * Whether the type can only be used as an embedded object.
  683        */
  684       public boolean isEmbeddedOnly() {
  685           if (_embedded == null) {
  686               ClassMetaData sup = getPCSuperclassMetaData();
  687               if (sup != null)
  688                   _embedded = (sup.isEmbeddedOnly()) ? Boolean.TRUE
  689                       : Boolean.FALSE;
  690               else
  691                   _embedded = Boolean.FALSE;
  692           }
  693           return _embedded.booleanValue();
  694       }
  695   
  696       /**
  697        * Whether the type can only be used as an embedded object.
  698        */
  699       public void setEmbeddedOnly(boolean embed) {
  700           _embedded = (embed) ? Boolean.TRUE : Boolean.FALSE;
  701       }
  702   
  703       /**
  704        * Whether the type's fields are actively intercepted, either by
  705        * redefinition or enhancement.
  706        */
  707       public boolean isIntercepting() {
  708           return _intercepting;
  709       }
  710   
  711       /**
  712        * Whether the type's fields are actively intercepted, either by
  713        * redefinition or enhancement.
  714        */
  715       public void setIntercepting(boolean intercepting) {
  716           _intercepting = intercepting;
  717       }
  718   
  719       /**
  720        * Whether the type is a managed interface.
  721        */
  722       public boolean isManagedInterface() {
  723           if (!_type.isInterface())
  724               return false;
  725           return _interface == null ? false : _interface.booleanValue();
  726       }
  727   
  728       /**
  729        * Whether the type is a managed interface
  730        */
  731       public void setManagedInterface(boolean managedInterface) {
  732           if (!_type.isInterface())
  733               throw new MetaDataException(_loc.get("not-interface", _type));
  734           _interface = managedInterface ? Boolean.TRUE : Boolean.FALSE;
  735   
  736           // managed interfaces always do proper interception; OpenJPA generates
  737           // the implementations.
  738           if (isManagedInterface())
  739               setIntercepting(true);
  740   
  741           // managed interfaces always use property access.
  742           setAccessType(ACCESS_PROPERTY);
  743       }
  744   
  745       /**
  746        * Return the managed interface implementor if any.
  747        */
  748       public Class getInterfaceImpl() {
  749           return _impl;
  750       }
  751   
  752       /**
  753        * Set the managed interface implementor class.
  754        */
  755       public void setInterfaceImpl(Class impl) {
  756           _impl = impl;
  757       }
  758   
  759       /**
  760        * Return all explicitly declared interfaces this class implements.
  761        */
  762       public Class[] getDeclaredInterfaces() {
  763           if (_interfaces == null)
  764               return MetaDataRepository.EMPTY_CLASSES;
  765           return (Class[]) _interfaces.toArray(new Class[_interfaces.size()]);
  766       }
  767   
  768       /**
  769        * Explicitly declare the given interface among the ones this
  770        * class implements.
  771        */
  772       public void addDeclaredInterface(Class iface) {
  773           if (iface == null || !iface.isInterface())
  774               throw new MetaDataException(_loc.get("declare-non-interface",
  775                   this, iface));
  776           if (_interfaces == null)
  777               _interfaces = new ArrayList();
  778           _interfaces.add(iface);
  779       }
  780   
  781       /**
  782        * Remove the given interface from the declared list.
  783        */
  784       public boolean removeDeclaredInterface(Class iface) {
  785           if (_interfaces == null)
  786               return false;
  787           return _interfaces.remove(iface);
  788       }
  789   
  790       /**
  791        * Alias properties from the given interface during  queries to
  792        * the local field.
  793        */
  794       public void setInterfacePropertyAlias(Class iface, String orig, 
  795           String local) {
  796           synchronized (_ifaceMap) {
  797               Map fields = (Map) _ifaceMap.get(iface);
  798               if (fields == null) {
  799                   fields = new HashMap();
  800                   _ifaceMap.put(iface, fields);
  801               }
  802               if (fields.containsKey(orig))
  803                   throw new MetaDataException(_loc.get("duplicate-iface-alias", 
  804                       this, orig, local));
  805               fields.put(orig, local);
  806           }
  807       }
  808       
  809       /**
  810        * Get local field alias for the given interface property.
  811        */
  812       public String getInterfacePropertyAlias(Class iface, String orig) {
  813           synchronized (_ifaceMap) {
  814               Map fields = (Map) _ifaceMap.get(iface);
  815               if (fields == null)
  816                   return null;
  817               return (String) fields.get(orig);
  818           }
  819       }
  820       
  821       /**
  822        * Return all aliases property named for the given interface.
  823        */
  824       public String[] getInterfaceAliasedProperties(Class iface) {
  825           synchronized (_ifaceMap) {
  826               Map fields = (Map) _ifaceMap.get(iface);
  827               if (fields == null)
  828                   return EMPTY_STRING_ARRAY;
  829               return (String[]) fields.keySet().toArray(
  830                   new String[fields.size()]);
  831           }
  832       }
  833       
  834       /**
  835        * Return the number of fields that use impl or intermediate data, in
  836        * order to create a compacted array for storage of said data.
  837        */
  838       public int getExtraFieldDataLength() {
  839           int[] table = getExtraFieldDataTable();
  840           for (int i = table.length - 1; i >= 0; i--)
  841               if (table[i] != -1)
  842                   return table[i] + 1;
  843           return 0;
  844       }
  845   
  846       /**
  847        * Return the impl / intermediate field data index of the given field
  848        * in the compacted array, or -1 if the field does not use extra data.
  849        *
  850        * @see #getExtraFieldDataLength
  851        */
  852       public int getExtraFieldDataIndex(int field) {
  853           return getExtraFieldDataTable()[field];
  854       }
  855   
  856       /**
  857        * Creates a table mapping each field index to its extra data index.
  858        */
  859       private int[] getExtraFieldDataTable() {
  860           if (_fieldDataTable == null) {
  861               FieldMetaData[] fmds = getFields();
  862               int[] table = new int[fmds.length];
  863               int idx = 0;
  864               for (int i = 0; i < fmds.length; i++) {
  865                   if (fmds[i].usesIntermediate()
  866                       || fmds[i].usesImplData() != Boolean.FALSE)
  867                       table[i] = idx++;
  868                   else
  869                       table[i] = -1;
  870               }
  871               _fieldDataTable = table;
  872           }
  873           return _fieldDataTable;
  874       }
  875   
  876       /**
  877        * Return whether the given name represents a managed or static field of
  878        * this class, including superclass fields.
  879        */
  880       public boolean isAccessibleField(String field) {
  881           if (getDeclaredField(field) != null)
  882               return true;
  883           if (_staticFields == null) {
  884               Field[] fields = (Field[]) AccessController.doPrivileged(
  885                   J2DoPrivHelper.getDeclaredFieldsAction(_type)); 
  886               Set names = new HashSet((int) (fields.length * 1.33 + 1));
  887               for (int i = 0; i < fields.length; i++)
  888                   if (Modifier.isStatic(fields[i].getModifiers()))
  889                       names.add(fields[i].getName());
  890               _staticFields = names;
  891           }
  892           if (_staticFields.contains(field))
  893               return true;
  894           if (_super != null)
  895               return getPCSuperclassMetaData().isAccessibleField(field);
  896           return false;
  897       }
  898   
  899       /**
  900        * Return all field metadatas, including superclass fields.
  901        */
  902       public FieldMetaData[] getFields() {
  903           if (_allFields == null) {
  904               if (_super == null)
  905                   _allFields = getDeclaredFields();
  906               else {
  907                   FieldMetaData[] fields = getDeclaredFields();
  908                   FieldMetaData[] supFields = getPCSuperclassMetaData().
  909                       getFields();
  910   
  911                   FieldMetaData[] allFields = _repos.newFieldMetaDataArray
  912                       (fields.length + supFields.length);
  913                   System.arraycopy(supFields, 0, allFields, 0, supFields.length);
  914                   replaceDefinedSuperclassFields(allFields, supFields.length);
  915   
  916                   for (int i = 0; i < fields.length; i++) {
  917                       fields[i].setIndex(supFields.length + i);
  918                       allFields[supFields.length + i] = fields[i];
  919                   }
  920                   _allFields = allFields;
  921               }
  922           }
  923           return _allFields;
  924       }
  925   
  926       /**
  927        * Replace superclass fields that we define with our version.
  928        */
  929       private void replaceDefinedSuperclassFields(FieldMetaData[] fields,
  930           int len) {
  931           if (_supFieldMap == null || !_defSupFields)
  932               return;
  933   
  934           // don't assume fields are in order; this method is used for
  935           // listing order as well
  936           FieldMetaData supField;
  937           for (int i = 0; i < len; i++) {
  938               supField = (FieldMetaData) _supFieldMap.get(fields[i].getName());
  939               if (supField != null) {
  940                   fields[i] = supField;
  941                   supField.setIndex(i);
  942               }
  943           }
  944       }
  945   
  946       /**
  947        * Return the superclass copy of the given field.
  948        */
  949       protected FieldMetaData getSuperclassField(FieldMetaData supField) {
  950           ClassMetaData sm = getPCSuperclassMetaData();
  951           FieldMetaData fmd = sm == null ? null : sm.getField(supField.getName());
  952           if (fmd == null || fmd.getManagement() != fmd.MANAGE_PERSISTENT)
  953               throw new MetaDataException(_loc.get("unmanaged-sup-field",
  954                   supField, this));
  955           return fmd;
  956       }
  957   
  958       /**
  959        * Return only the fields for this class, without superclass fields.
  960        */
  961       public FieldMetaData[] getDeclaredFields() {
  962           if (_fields == null) {
  963               List fields = new ArrayList(_fieldMap.size());
  964               FieldMetaData fmd;
  965               for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
  966                   fmd = (FieldMetaData) itr.next();
  967                   if (fmd.getManagement() != FieldMetaData.MANAGE_NONE) {
  968                       fmd.setDeclaredIndex(fields.size());
  969                       fmd.setIndex(fmd.getDeclaredIndex());
  970                       fields.add(fmd);
  971                   }
  972               }
  973               _fields = (FieldMetaData[]) fields.toArray
  974                   (_repos.newFieldMetaDataArray(fields.size()));
  975           }
  976           return _fields;
  977       }
  978   
  979       /**
  980        * Return primary key fields, or empty array if none. The order
  981        * in which the keys are returned will be the order in which
  982        * the fields are declared, starting at the least-derived superclass
  983        * and ending with the primary key fields of the most-derived subclass.
  984        */
  985       public FieldMetaData[] getPrimaryKeyFields() {
  986           // check for pk fields even if not set to ID_APPLICATION so that
  987           // app id tool sees them even when user doesn't declare app id
  988           if (_allPKFields == null) {
  989               FieldMetaData[] fields = getFields();
  990               int num = 0;
  991               for (int i = 0; i < fields.length; i++)
  992                   if (fields[i].isPrimaryKey())
  993                       num++;
  994   
  995               if (num == 0)
  996                   _allPKFields = _repos.EMPTY_FIELDS;
  997               else {
  998                   FieldMetaData[] pks = _repos.newFieldMetaDataArray(num);
  999                   num = 0;
 1000                   for (int i = 0; i < fields.length; i++) {
 1001                       if (fields[i].isPrimaryKey()) {
 1002                           fields[i].setPrimaryKeyIndex(num);
 1003                           pks[num] = fields[i];
 1004                           num++;
 1005                       }
 1006                   }
 1007                   _allPKFields = pks;
 1008               }
 1009           }
 1010           return _allPKFields;
 1011       }
 1012   
 1013       /**
 1014        * Return the list of fields in the default fetch group,
 1015        * including superclass fields, or an empty array if none.
 1016        */
 1017       public FieldMetaData[] getDefaultFetchGroupFields() {
 1018           if (_allDFGFields == null) {
 1019               FieldMetaData[] fields = getFields();
 1020               int num = 0;
 1021               for (int i = 0; i < fields.length; i++)
 1022                   if (fields[i].isInDefaultFetchGroup())
 1023                       num++;
 1024   
 1025               FieldMetaData[] dfgs = _repos.newFieldMetaDataArray(num);
 1026               num = 0;
 1027               for (int i = 0; i < fields.length; i++)
 1028                   if (fields[i].isInDefaultFetchGroup())
 1029                       dfgs[num++] = fields[i];
 1030               _allDFGFields = dfgs;
 1031           }
 1032           return _allDFGFields;
 1033       }
 1034   
 1035       /**
 1036        * Return the version field for this class, if any.
 1037        */
 1038       public FieldMetaData getVersionField() {
 1039           if (_versionIdx == Integer.MIN_VALUE) {
 1040               FieldMetaData[] fields = getFields();
 1041               int idx = -1;
 1042               for (int i = 0; i < fields.length; i++) {
 1043                   if (fields[i].isVersion()) {
 1044                       if (idx != -1)
 1045                           throw new MetaDataException(_loc.get
 1046                               ("mult-vers-fields", this, fields[idx], fields[i]));
 1047                       idx = i;
 1048                   }
 1049               }
 1050               _versionIdx = idx;
 1051           }
 1052           if (_versionIdx == -1)
 1053               return null;
 1054           return getFields()[_versionIdx];
 1055       }
 1056   
 1057       /**
 1058        * Return the metadata for the persistent or transactional field with
 1059        * the given absolute index.
 1060        *
 1061        * @return the field's metadata, or null if not found
 1062        */
 1063       public FieldMetaData getField(int index) {
 1064           FieldMetaData[] fields = getFields();
 1065           if (index < 0 || index >= fields.length)
 1066               return null;
 1067           return fields[index];
 1068       }
 1069   
 1070       /**
 1071        * Return the metadata for the persistent or transactional field with
 1072        * the given relative index.
 1073        *
 1074        * @return the field's metadata, or null if not found
 1075        */
 1076       public FieldMetaData getDeclaredField(int index) {
 1077           FieldMetaData[] fields = getDeclaredFields();
 1078           if (index < 0 || index >= fields.length)
 1079               return null;
 1080           return fields[index];
 1081       }
 1082   
 1083       /**
 1084        * Return the metadata for the persistent or transactional field with
 1085        * the given name.
 1086        *
 1087        * @return the field's metadata, or null if not found
 1088        */
 1089       public FieldMetaData getField(String name) {
 1090           FieldMetaData fmd = getDeclaredField(name);
 1091           if (fmd != null)
 1092               return fmd;
 1093           if (_supFieldMap != null && _defSupFields) {
 1094               fmd = (FieldMetaData) _supFieldMap.get(name);
 1095               if (fmd != null)
 1096                   return fmd;
 1097           }
 1098           if (_super != null)
 1099               return getPCSuperclassMetaData().getField(name);
 1100           return null;
 1101       }
 1102   
 1103       /**
 1104        * Return the metadata for the persistent or transactional field with
 1105        * the given name, without including superclass fields.
 1106        *
 1107        * @return the field's metadata, or null if not found
 1108        */
 1109       public FieldMetaData getDeclaredField(String name) {
 1110           FieldMetaData field = (FieldMetaData) _fieldMap.get(name);
 1111           if (field == null || field.getManagement() == field.MANAGE_NONE)
 1112               return null;
 1113           return field;
 1114       }
 1115   
 1116       /**
 1117        * Return any fields that were added as non-managed.
 1118        * All other methods to get fields return only those that are managed.
 1119        */
 1120       public FieldMetaData[] getDeclaredUnmanagedFields() {
 1121           if (_unmgdFields == null) {
 1122               Collection unmanaged = new ArrayList(3);
 1123               FieldMetaData field;
 1124               for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
 1125                   field = (FieldMetaData) itr.next();
 1126                   if (field.getManagement() == FieldMetaData.MANAGE_NONE)
 1127                       unmanaged.add(field);
 1128               }
 1129               _unmgdFields = (FieldMetaData[]) unmanaged.toArray
 1130                   (_repos.newFieldMetaDataArray(unmanaged.size()));
 1131           }
 1132           return _unmgdFields;
 1133       }
 1134   
 1135       /**
 1136        * Add a new field metadata to this class.
 1137        */
 1138       public FieldMetaData addDeclaredField(String name, Class type) {
 1139           FieldMetaData fmd = _repos.newFieldMetaData(name, type, this);
 1140           clearFieldCache();
 1141           _fieldMap.put(name, fmd);
 1142           return fmd;
 1143       }
 1144   
 1145       /**
 1146        * Remove the given field from management.
 1147        *
 1148        * @return true if the field was removed, false otherwise
 1149        */
 1150       public boolean removeDeclaredField(FieldMetaData field) {
 1151           if (field != null && _fieldMap.remove(field.getName()) != null) {
 1152               clearFieldCache();
 1153               return true;
 1154           }
 1155           return false;
 1156       }
 1157   
 1158       /**
 1159        * Return the defined superclass field with the given name, or null if none.
 1160        */
 1161       public FieldMetaData getDefinedSuperclassField(String name) {
 1162           if (_supFieldMap == null)
 1163               return null;
 1164           return (FieldMetaData) _supFieldMap.get(name);
 1165       }
 1166   
 1167       /**
 1168        * Add a new defined superclass field metadata to this class.
 1169        */
 1170       public FieldMetaData addDefinedSuperclassField(String name, Class type,
 1171           Class sup) {
 1172           FieldMetaData fmd = _repos.newFieldMetaData(name, type, this);
 1173           fmd.setDeclaringType(sup);
 1174           clearAllFieldCache();
 1175           _defSupFields = false;
 1176           if (_supFieldMap == null)
 1177               _supFieldMap = new HashMap();
 1178           _supFieldMap.put(name, fmd);
 1179           return fmd;
 1180       }
 1181   
 1182       /**
 1183        * Remove the given field from management.
 1184        *
 1185        * @return true if the field was removed, false otherwise
 1186        */
 1187       public boolean removeDefinedSuperclassField(FieldMetaData field) {
 1188           if (field != null && _supFieldMap != null
 1189               && _supFieldMap.remove(field.getName()) != null) {
 1190               clearAllFieldCache();
 1191               _defSupFields = false;
 1192               return true;
 1193           }
 1194           return false;
 1195       }
 1196   
 1197       /**
 1198        * Incorporate superclass fields redefined in this subclass into this
 1199        * metadata. This method is generally called after metadata is resolved
 1200        * and mapping information is loaded, but before mapping resolve.
 1201        *
 1202        * @param force whether to force re-mapping of even mapped superclass fields
 1203        */
 1204       public void defineSuperclassFields(boolean force) {
 1205           if (_defSupFields)
 1206               return;
 1207   
 1208           ClassMetaData sup = getPCSuperclassMetaData();
 1209           if (isMapped() && sup != null) {
 1210               // redefine all unmapped superclass fields
 1211               FieldMetaData[] sups = sup.getFields();
 1212               for (int i = 0; i < sups.length; i++) {
 1213                   if ((force || !sups[i].getDefiningMetaData().isMapped())
 1214                       && getDefinedSuperclassField(sups[i].getName()) == null) {
 1215                       addDefinedSuperclassField(sups[i].getName(),
 1216                           sups[i].getDeclaredType(), sups[i].getDeclaringType());
 1217                   }
 1218               }
 1219           }
 1220           resolveDefinedSuperclassFields();
 1221   
 1222           // this ensures that all field indexes get set when fields are cached.
 1223           // I don't like doing this twice (it's also done in resolveMeta), but
 1224           // we have to re-cache in case this class or any superclass replaced
 1225           // some fields with redefined versions, and I don't want outside code
 1226           // to have to call this method after resolve just to get field indexes,
 1227           // etc set correctly
 1228           clearAllFieldCache();
 1229           cacheFields();
 1230       }
 1231   
 1232       /**
 1233        * Resolve superclass fields we've redefined.
 1234        */
 1235       private void resolveDefinedSuperclassFields() {
 1236           _defSupFields = true;
 1237           if (_supFieldMap == null)
 1238               return;
 1239   
 1240           FieldMetaData fmd;
 1241           FieldMetaData sup;
 1242           for (Iterator itr = _supFieldMap.values().iterator(); itr.hasNext();) {
 1243               fmd = (FieldMetaData) itr.next();
 1244               sup = getSuperclassField(fmd);
 1245   
 1246               // jpa metadata doesn't qualify superclass field names, so we
 1247               // might not know the declaring type until now
 1248               if (fmd.getDeclaringType() == Object.class) {
 1249                   fmd.setDeclaringType(sup.getDeclaringType());
 1250                   fmd.backingMember(getRepository().getMetaDataFactory().
 1251                       getDefaults().getBackingMember(fmd));
 1252               }
 1253               fmd.copy(sup);
 1254               fmd.resolve(MODE_META);
 1255           }
 1256       }
 1257   
 1258       /**
 1259        * Returns an array of all the fields defined by this class.
 1260        * This includes mapped declared fields and any concrete mapping of
 1261        * unmapped superclass fields performed by this class.
 1262        */
 1263       public FieldMetaData[] getDefinedFields() {
 1264           if (_definedFields == null) {
 1265               FieldMetaData[] fields = getFields();
 1266               List defined = new ArrayList(fields.length);
 1267               for (int i = 0; i < fields.length; i++) {
 1268                   if (fields[i].isMapped()
 1269                       && fields[i].getDefiningMetaData() == this)
 1270                       defined.add(fields[i]);
 1271               }
 1272               _definedFields = (FieldMetaData[]) defined.toArray
 1273                   (_repos.newFieldMetaDataArray(defined.size()));
 1274           }
 1275           return _definedFields;
 1276       }
 1277   
 1278       /**
 1279        * Returns all fields in the order they are listed in the metadata
 1280        * file. Unlisted fields are placed after listed ones.
 1281        */
 1282       public FieldMetaData[] getFieldsInListingOrder() {
 1283           if (_allListingFields == null) {
 1284               // combine declared and unmanaged fields into listing order array
 1285               FieldMetaData[] dec = getDeclaredFields();
 1286               FieldMetaData[] unmgd = getDeclaredUnmanagedFields();
 1287               FieldMetaData[] decListing = _repos.newFieldMetaDataArray
 1288                   (dec.length + unmgd.length);
 1289               System.arraycopy(dec, 0, decListing, 0, dec.length);
 1290               System.arraycopy(unmgd, 0, decListing, dec.length, unmgd.length);
 1291               Arrays.sort(decListing, ListingOrderComparator.getInstance());
 1292   
 1293               if (_super == null)
 1294                   _allListingFields = decListing;
 1295               else {
 1296                   // place superclass fields in listing order before our
 1297                   // listing-order declared fields
 1298                   FieldMetaData[] sup = getPCSuperclassMetaData().
 1299                       getFieldsInListingOrder();
 1300                   FieldMetaData[] listing = _repos.newFieldMetaDataArray
 1301                       (sup.length + decListing.length);
 1302                   System.arraycopy(sup, 0, listing, 0, sup.length);
 1303                   replaceDefinedSuperclassFields(listing, sup.length);
 1304                   System.arraycopy(decListing, 0, listing, sup.length,
 1305                       decListing.length);
 1306                   _allListingFields = listing;
 1307               }
 1308           }
 1309           return _allListingFields;
 1310       }
 1311   
 1312       /**
 1313        * Returns all fields defined by this class in the order they are listed
 1314        * in the metadata file. Unlisted fields are placed after listed ones.
 1315        * This array includes declared transactional and unmanaged fields.
 1316        */
 1317       public FieldMetaData[] getDefinedFieldsInListingOrder() {
 1318           if (_listingFields == null) {
 1319               FieldMetaData[] fields = getFields();
 1320               List defined = new ArrayList(fields.length);
 1321               for (int i = 0; i < fields.length; i++)
 1322                   if (fields[i].getDefiningMetaData() == this)
 1323                       defined.add(fields[i]);
 1324               FieldMetaData[] unmgd = getDeclaredUnmanagedFields();
 1325               FieldMetaData[] listing = _repos.newFieldMetaDataArray
 1326                   (defined.size() + unmgd.length);
 1327               for (int i = 0; i < defined.size(); i++)
 1328                   listing[i] = (FieldMetaData) defined.get(i);
 1329               System.arraycopy(unmgd, 0, listing, defined.size(), unmgd.length);
 1330               Arrays.sort(listing, ListingOrderComparator.getInstance());
 1331               _listingFields = listing;
 1332           }
 1333           return _listingFields;
 1334       }
 1335   
 1336       /**
 1337        * The name of the datacache to use for this class, or null if none.
 1338        */
 1339       public String getDataCacheName() {
 1340           if (DEFAULT_STRING.equals(_cacheName)) {
 1341               if (_super != null)
 1342                   _cacheName = getPCSuperclassMetaData().getDataCacheName();
 1343               else
 1344                   _cacheName = DataCache.NAME_DEFAULT;
 1345           }
 1346           return _cacheName;
 1347       }
 1348   
 1349       /**
 1350        * Set the cache name for this class. Set to null to disable caching.
 1351        */
 1352       public void setDataCacheName(String name) {
 1353           _cacheName = name;
 1354       }
 1355   
 1356       /**
 1357        * The cache timeout for this class. -1 indicates no timeout.
 1358        */
 1359       public int getDataCacheTimeout() {
 1360           if (_cacheTimeout == Integer.MIN_VALUE) {
 1361               if (_super != null)
 1362                   _cacheTimeout = getPCSuperclassMetaData().
 1363                       getDataCacheTimeout();
 1364               else
 1365                   _cacheTimeout = _repos.getConfiguration().
 1366                       getDataCacheTimeout();
 1367           }
 1368           return _cacheTimeout;
 1369       }
 1370   
 1371       /**
 1372        * The cache timeout for this class. -1 indicates no timeout.
 1373        */
 1374       public void setDataCacheTimeout(int timeout) {
 1375           _cacheTimeout = timeout;
 1376       }
 1377   
 1378       /**
 1379        * Return the data cache for this class, or null if it is not cachable.
 1380        */
 1381       public DataCache getDataCache() {
 1382           String name = getDataCacheName();
 1383           if (name == null)
 1384               return null;
 1385           return _repos.getConfiguration().getDataCacheManagerInstance().
 1386               getDataCache(name, true);
 1387       }
 1388   
 1389       /**
 1390        * Whether instances are detachable.
 1391        */
 1392       public boolean isDetachable() {
 1393           if (_detachable == null) {
 1394               if (_super != null)
 1395                   _detachable = (getPCSuperclassMetaData().isDetachable())
 1396                       ? Boolean.TRUE : Boolean.FALSE;
 1397               else
 1398                   _detachable = Boolean.FALSE;
 1399           }
 1400           return _detachable.booleanValue();
 1401       }
 1402   
 1403       /**
 1404        * Whether instances are detachable.
 1405        */
 1406       public void setDetachable(boolean detachable) {
 1407           _detachable = (detachable) ? Boolean.TRUE : Boolean.FALSE;
 1408       }
 1409   
 1410       /**
 1411        * The name of the detach state field, or null if none.
 1412        */
 1413       public String getDetachedState() {
 1414           if (DEFAULT_STRING.equals(_detachState)) {
 1415               ClassMetaData sup = getPCSuperclassMetaData();
 1416               if (sup != null && sup.isDetachable() == isDetachable())
 1417                   _detachState = sup.getDetachedState();
 1418               else {
 1419                   Boolean use = usesDetachedState(SYNTHETIC, true);
 1420                   _detachState = (Boolean.FALSE.equals(use)) ? null : SYNTHETIC;
 1421               }
 1422           }
 1423           return _detachState;
 1424       }
 1425   
 1426       /**
 1427        * The name of the detach state field, or null if none.
 1428        */
 1429       public void setDetachedState(String field) {
 1430           _detachState = field;
 1431       }
 1432   
 1433       /**
 1434        * Return the detach state field, or null if none.
 1435        */
 1436       public Field getDetachedStateField() {
 1437           // no caching; only used at enhancement
 1438           String fieldName = getDetachedState();
 1439           if (fieldName == null || SYNTHETIC.equals(fieldName))
 1440               return null;
 1441   
 1442           Field f = Reflection.findField(_type, fieldName, false);
 1443           if (f != null)
 1444               return f;
 1445           else
 1446               throw new MetaDataException(
 1447                   _loc.get("no-detach-state", fieldName, _type));
 1448       }
 1449   
 1450       /**
 1451        * Whether an instance of this type has detached state.
 1452        *
 1453        * @return true if a detached instance must have detached state, false
 1454        * if it does not, and null if it may use a
 1455        * manually-constructed instance without detached state
 1456        */
 1457       public Boolean usesDetachedState() {
 1458           // no need to let conf disallow because it's taken into account in
 1459           // getDetachedState() call
 1460           return usesDetachedState(getDetachedState(), false);
 1461       }
 1462   
 1463       /**
 1464        * Whether an instance of this type has detached state, assuming the given
 1465        * detached state field.
 1466        *
 1467        * @return true if a detached instance must have detached state, false
 1468        * if it does not, and null if it may use a
 1469        * manually-constructed instance without detached state
 1470        */
 1471       private Boolean usesDetachedState(String detachedField,
 1472           boolean confDisallows) {
 1473           if (!isDetachable())
 1474               return Boolean.FALSE;
 1475   
 1476           // if we declare a detached state field, have to use it
 1477           if (detachedField == null)
 1478               return Boolean.FALSE;
 1479           if (!SYNTHETIC.equals(detachedField))
 1480               return Boolean.TRUE;
 1481   
 1482           // allow conf to disallow
 1483           if (confDisallows && !_repos.getConfiguration().
 1484               getDetachStateInstance().getDetachedStateField())
 1485               return Boolean.FALSE;
 1486   
 1487           // have to use detached state to store datastore id
 1488           if (getIdentityType() == ID_DATASTORE)
 1489               return Boolean.TRUE;
 1490   
 1491           // allow detached state use, but don't require
 1492           return null;
 1493       }
 1494   
 1495       /**
 1496        * Clear cached field data.
 1497        */
 1498       protected void clearAllFieldCache() {
 1499           _allFields = null;
 1500           _allDFGFields = null;
 1501           _allPKFields = null;
 1502           _definedFields = null;
 1503           _listingFields = null;
 1504           _allListingFields = null;
 1505           _fieldDataTable = null;
 1506       }
 1507   
 1508       /**
 1509        * Clear defined field data.
 1510        */
 1511       protected void clearDefinedFieldCache() {
 1512           _definedFields = null;
 1513           _listingFields = null;
 1514       }
 1515   
 1516       /**
 1517        * Clear cached field data.
 1518        */
 1519       protected void clearFieldCache() {
 1520           clearAllFieldCache();
 1521           _fields = null;
 1522           _unmgdFields = null;
 1523           _versionIdx = Integer.MIN_VALUE;
 1524       }
 1525   
 1526       /**
 1527        * Clear cached subclass data.
 1528        */
 1529       protected void clearSubclassCache() {
 1530           _subs = null;
 1531           _subMetas = null;
 1532           _mapSubMetas = null;
 1533       }
 1534   
 1535       /**
 1536        * Clear impl data and intermediate data table.
 1537        */
 1538       void clearExtraFieldDataTable() {
 1539           _fieldDataTable = null;
 1540       }
 1541   
 1542       /**
 1543        * Cache field arrays.
 1544        */
 1545       private void cacheFields() {
 1546           getFields();
 1547           getPrimaryKeyFields();
 1548       }
 1549   
 1550       public int hashCode() {
 1551           return _type.getName().hashCode();
 1552       }
 1553   
 1554       public boolean equals(Object other) {
 1555           if (other == this)
 1556               return true;
 1557           if (!(other instanceof ClassMetaData))
 1558               return false;
 1559           return _type == ((ClassMetaData) other).getDescribedType();
 1560       }
 1561   
 1562       public int compareTo(Object other) {
 1563           if (other == this)
 1564               return 0;
 1565           return _type.getName().compareTo(((ClassMetaData) other).
 1566               getDescribedType().getName());
 1567       }
 1568   
 1569       public String toString() {
 1570           return getDescribedType().getName();
 1571       }
 1572   
 1573       ////////////////////////
 1574       // Resolve and validate
 1575       ////////////////////////
 1576   
 1577       /**
 1578        * The resolve mode for this metadata.
 1579        */
 1580       public int getResolve() {
 1581           return _resMode;
 1582       }
 1583   
 1584       /**
 1585        * The resolve mode for this metadata.
 1586        */
 1587       public void setResolve(int mode) {
 1588           _resMode = mode;
 1589       }
 1590   
 1591       /**
 1592        * The resolve mode for this metadata.
 1593        */
 1594       public void setResolve(int mode, boolean on) {
 1595           if (mode == MODE_NONE)
 1596               _resMode = mode;
 1597           else if (on)
 1598               _resMode |= mode;
 1599           else
 1600               _resMode &= ~mode;
 1601       }
 1602   
 1603       /**
 1604        * Resolve and validate metadata. Return true if already resolved.
 1605        */
 1606       public boolean resolve(int mode) {
 1607           if ((_resMode & mode) == mode)
 1608               return true;
 1609           int cur = _resMode;
 1610           _resMode |= mode;
 1611   
 1612           int val = _repos.getValidate();
 1613           boolean runtime = (val & _repos.VALIDATE_RUNTIME) != 0;
 1614           boolean validate =
 1615               !ImplHelper.isManagedType(getRepository().getConfiguration(), _type)
 1616               || (val & MetaDataRepository.VALIDATE_UNENHANCED) == 0;
 1617   
 1618           // we only do any actions for metadata mode
 1619           if ((mode & MODE_META) != 0 && (cur & MODE_META) == 0) {
 1620               resolveMeta(runtime);
 1621               if (validate && (val & _repos.VALIDATE_META) != 0)
 1622                   validateMeta(runtime);
 1623           }
 1624           if ((mode & MODE_MAPPING) != 0 && (cur & MODE_MAPPING) == 0) {
 1625               resolveMapping(runtime);
 1626               if (validate && (val & _repos.VALIDATE_MAPPING) != 0)
 1627                   validateMapping(runtime);
 1628           }
 1629           if ((mode & MODE_MAPPING_INIT) != 0 && (cur & MODE_MAPPING_INIT) == 0)
 1630               initializeMapping();
 1631           return false;
 1632       }
 1633   
 1634       /**
 1635        * Resolve metadata.
 1636        */
 1637       protected void resolveMeta(boolean runtime) {
 1638           boolean embed = _owner != null && _owner.getDeclaredType() == _type;
 1639           Log log = _repos.getLog();
 1640           if (log.isTraceEnabled())
 1641               log.trace(_loc.get((embed) ? "resolve-embed-meta" : "resolve-meta",
 1642                   this + "@" + System.identityHashCode(this)));
 1643   
 1644           if (runtime && !_type.isInterface() &&
 1645               !ImplHelper.isManagedType(getRepository().getConfiguration(),_type))
 1646               throw new MetaDataException(_loc.get("not-enhanced", _type));
 1647   
 1648           // are we the target of an embedded value?
 1649           if (embed) {
 1650               if (_owner.getFieldMetaData().getDefiningMetaData().
 1651                   getDescribedType().isAssignableFrom(_type))
 1652                   throw new MetaDataException(_loc.get("recurse-embed", _owner));
 1653   
 1654               // copy info from the "real" metadata for this type
 1655               ClassMetaData meta = _repos.getMetaData(_type, _loader, true);
 1656               meta.resolve(MODE_META);
 1657               copy(this, meta);
 1658               _embedded = Boolean.FALSE; // embedded instance isn't embedded-only
 1659           }
 1660   
 1661           // make sure superclass is resolved
 1662           ClassMetaData sup = getPCSuperclassMetaData();
 1663           if (sup != null) {
 1664               sup.resolve(MODE_META);
 1665               if (embed) {
 1666                   // embedded instance always redefine all superclass fields
 1667                   FieldMetaData[] sups = sup.getFields();
 1668                   for (int i = 0; i < sups.length; i++) {
 1669                       if (_supFieldMap == null
 1670                           || !_supFieldMap.containsKey(sups[i].getName())) {
 1671                           addDefinedSuperclassField(sups[i].getName(),
 1672                               sups[i].getDeclaredType(),
 1673                               sups[i].getDeclaringType());
 1674                       }
 1675                   }
 1676               }
 1677           }
 1678   
 1679           // resolve fields and remove invalids
 1680           FieldMetaData fmd;
 1681           for (Iterator itr = _fieldMap.values().iterator(); itr.hasNext();) {
 1682               // only pass on metadata resolve mode so that metadata is always
 1683               // resolved before any other resolve modes our subclasses pass along
 1684               fmd = (FieldMetaData) itr.next();
 1685               fmd.resolve(MODE_META);
 1686   
 1687               if (!fmd.isExplicit()
 1688                   && (fmd.getDeclaredTypeCode() == JavaTypes.OBJECT
 1689                   || fmd.getDeclaredTypeCode() == JavaTypes.PC_UNTYPED
 1690                   || (fmd.getDeclaredTypeCode() == JavaTypes.ARRAY
 1691                   && fmd.getElement().getDeclaredTypeCode()
 1692                   == JavaTypes.OBJECT))) {
 1693                   _repos.getLog().warn(_loc.get("rm-field", fmd));
 1694                   if (fmd.getListingIndex() != -1)
 1695                       fmd.setManagement(fmd.MANAGE_NONE);
 1696                   else
 1697                       itr.remove();
 1698                   clearFieldCache();
 1699               }
 1700           }
 1701   
 1702           // embedded instances must embed all superclass fields too
 1703           if (embed) {
 1704               clearAllFieldCache();
 1705               resolveDefinedSuperclassFields();
 1706           }
 1707   
 1708           // this ensures that all field indexes get set when fields are cached
 1709           cacheFields();
 1710   
 1711           // resolve lifecycle metadata now to prevent lazy threading problems
 1712           _lifeMeta.resolve();
 1713   
 1714           // record implements in the repository
 1715           if (_interfaces != null) {
 1716               for (Iterator it = _interfaces.iterator(); it.hasNext();)
 1717                   _repos.addDeclaredInterfaceImpl(this, (Class) it.next());
 1718           }
 1719   
 1720           // resolve fetch groups
 1721           if (_fgMap != null)
 1722               for (Iterator itr = _fgMap.values().iterator(); itr.hasNext();)
 1723                   ((FetchGroup) itr.next()).resolve();
 1724   
 1725           if (!embed && _type.isInterface()) {
 1726               if (_interface != Boolean.TRUE)
 1727                   throw new MetaDataException(_loc.get("interface", _type));
 1728   
 1729               if (runtime) {
 1730                   _impl = _repos.getImplGenerator().createImpl(this);
 1731                   _repos.setInterfaceImpl(this, _impl);
 1732               }
 1733           }
 1734   
 1735           // if this is runtime, create a pc instance and scan it for comparators
 1736           if (runtime && !Modifier.isAbstract(_type.getModifiers())) {
 1737               ProxySetupStateManager sm = new ProxySetupStateManager();
 1738               sm.setProxyData(PCRegistry.newInstance(_type, sm, false), this);
 1739           }
 1740       }
 1741   
 1742       /**
 1743        * Validate resolved metadata.
 1744        */
 1745       protected void validateMeta(boolean runtime) {
 1746           validateDataCache();
 1747           validateDetachable();
 1748           validateExtensionKeys();
 1749           validateIdentity();
 1750           validateAccessType();
 1751       }
 1752   
 1753       /**
 1754        * Resolve mapping data. Logs resolve message and resolves super by default.
 1755        */
 1756       protected void resolveMapping(boolean runtime) {
 1757           Log log = _repos.getLog();
 1758           if (log.isTraceEnabled())
 1759               log.trace(_loc.get("resolve-mapping", this + "@"
 1760                   + System.identityHashCode(this)));
 1761   
 1762           // make sure superclass is resolved first
 1763           ClassMetaData sup = getPCSuperclassMetaData();
 1764           if (sup != null)
 1765               sup.resolve(MODE_MAPPING);
 1766       }
 1767   
 1768       /**
 1769        * Validate mapping data.
 1770        */
 1771       protected void validateMapping(boolean runtime) {
 1772       }
 1773   
 1774       /**
 1775        * Initialize mapping. Logs init message by default.
 1776        */
 1777       protected void initializeMapping() {
 1778           Log log = _repos.getLog();
 1779           if (log.isTraceEnabled())
 1780               log.trace(_loc.get("init-mapping", this + "@"
 1781                   + System.identityHashCode(this)));
 1782       }
 1783   
 1784       /**
 1785        * Validate data cache settings.
 1786        */
 1787       private void validateDataCache() {
 1788           int timeout = getDataCacheTimeout();
 1789           if (timeout < -1 || timeout == 0)
 1790               throw new MetaDataException(_loc.get("cache-timeout-invalid",
 1791                   _type, String.valueOf(timeout)));
 1792   
 1793           if (_super == null)
 1794               return;
 1795           String cache = getDataCacheName();
 1796           if (cache == null)
 1797               return;
 1798   
 1799           String superCache = getPCSuperclassMetaData().getDataCacheName();
 1800           if (!StringUtils.equals(cache, superCache))
 1801               throw new MetaDataException(_loc.get("cache-names", new Object[]
 1802                   { _type, cache, _super, superCache }));
 1803       }
 1804   
 1805       /**
 1806        * Assert that the identity handling for this class is valid.
 1807        */
 1808       private void validateIdentity() {
 1809           // make sure identity types are consistent
 1810           ClassMetaData sup = getPCSuperclassMetaData();
 1811           int id = getIdentityType();
 1812           if (sup != null && sup.getIdentityType() != ID_UNKNOWN
 1813               && sup.getIdentityType() != id)
 1814               throw new MetaDataException(_loc.get("id-types", _type));
 1815   
 1816           // check for things the data store doesn't support
 1817           Collection opts = _repos.getConfiguration().supportedOptions();
 1818           if (id == ID_APPLICATION
 1819               && !opts.contains(OpenJPAConfiguration.OPTION_ID_APPLICATION)) {
 1820               throw new UnsupportedException(_loc.get("appid-not-supported",
 1821                   _type));
 1822           }
 1823           if (id == ID_DATASTORE
 1824               && !opts.contains(OpenJPAConfiguration.OPTION_ID_DATASTORE)) {
 1825               throw new UnsupportedException(_loc.get
 1826                   ("datastoreid-not-supported", _type));
 1827           }
 1828   
 1829           if (id == ID_APPLICATION) {
 1830               if (_idStrategy != ValueStrategies.NONE)
 1831                   throw new MetaDataException(_loc.get("appid-strategy", _type));
 1832               validateAppIdClass();
 1833           } else if (id != ID_UNKNOWN)
 1834               validateNoPKFields();
 1835   
 1836           int strategy = getIdentityStrategy();
 1837           if (strategy == ValueStrategies.SEQUENCE
 1838               && getIdentitySequenceName() == null)
 1839               throw new MetaDataException(_loc.get("no-seq-name", _type));
 1840   
 1841           ValueStrategies.assertSupported(strategy, this,
 1842               "datastore identity strategy");
 1843       }
 1844   
 1845       /**
 1846        * Make sure the application identity class is valid.
 1847        */
 1848       private void validateAppIdClass() {
 1849           // base types must declare an oid class if not single-field identity
 1850           FieldMetaData[] pks = getPrimaryKeyFields();
 1851           if (getObjectIdType() == null) {
 1852               if (pks.length == 1)
 1853                   throw new MetaDataException(_loc.get("unsupported-id-type",
 1854                       _type, pks[0].getName(),
 1855                       pks[0].getDeclaredType().getName()));
 1856               throw new MetaDataException(_loc.get("no-id-class", _type));
 1857           }
 1858           if (_objectId == null)
 1859               return;
 1860   
 1861           if (isOpenJPAIdentity()) {
 1862               if (pks[0].getDeclaredTypeCode() == JavaTypes.OID) {
 1863                   ClassMetaData embed = pks[0].getEmbeddedMetaData();
 1864                   validateAppIdClassMethods(embed.getDescribedType());
 1865                   validateAppIdClassPKs(embed, embed.getFields(),
 1866                       embed.getDescribedType());
 1867               }
 1868               return;
 1869           }
 1870   
 1871           if (_super != null) {
 1872               // concrete superclass oids must match or be parent of ours
 1873               ClassMetaData sup = getPCSuperclassMetaData();
 1874               if (!sup.getObjectIdType().isAssignableFrom(_objectId))
 1875                   throw new MetaDataException(_loc.get("id-classes",
 1876                       new Object[]{ _type, _objectId, _super,
 1877                           sup.getObjectIdType() }));
 1878   
 1879               // validate that no other pks are declared if we have a
 1880               // concrete PC superclass
 1881               if (hasConcretePCSuperclass())
 1882                   validateNoPKFields();
 1883           }
 1884   
 1885           // if this class has its own oid class, do some more validation
 1886           if (_super == null
 1887               || _objectId != getPCSuperclassMetaData().getObjectIdType()) {
 1888               // make sure non-abstract oid classes override the proper methods
 1889               if (!Modifier.isAbstract(_objectId.getModifiers()))
 1890                   validateAppIdClassMethods(_objectId);
 1891   
 1892               // make sure the app id class has all pk fields
 1893               validateAppIdClassPKs(this, pks, _objectId);
 1894           }
 1895       }
 1896   
 1897       /**
 1898        * Return true if this class has a concrete persistent superclass.
 1899        */
 1900       private boolean hasConcretePCSuperclass() {
 1901           if (_super == null)
 1902               return false;
 1903           if (!Modifier.isAbstract(_super.getModifiers()))
 1904               return true;
 1905           return getPCSuperclassMetaData().hasConcretePCSuperclass();
 1906       }
 1907   
 1908       /**
 1909        * Ensure that the user has overridden the equals and hashCode methods,
 1910        * and has the proper constructors.
 1911        */
 1912       private void validateAppIdClassMethods(Class oid) {
 1913           try {
 1914               oid.getConstructor((Class[]) null);
 1915           } catch (Exception e) {
 1916               throw new MetaDataException(_loc.get("null-cons", _type)).
 1917                   setCause(e);
 1918           }
 1919   
 1920           // check for equals and hashcode overrides; don't enforce it
 1921           // for abstract app id classes, since they may not necessarily
 1922           // declare primary key fields
 1923           Method method;
 1924           try {
 1925               method = oid.getMethod("equals", new Class[]{ Object.class });
 1926           } catch (Exception e) {
 1927               throw new GeneralException(e).setFatal(true);
 1928           }
 1929   
 1930           boolean abs = Modifier.isAbstract(_type.getModifiers());
 1931           if (!abs && method.getDeclaringClass() == Object.class)
 1932               throw new MetaDataException(_loc.get("eq-method", _type));
 1933   
 1934           try {
 1935               method = oid.getMethod("hashCode", (Class[]) null);
 1936           } catch (Exception e) {
 1937               throw new GeneralException(e).setFatal(true);
 1938           }
 1939           if (!abs && method.getDeclaringClass() == Object.class)
 1940               throw new MetaDataException(_loc.get("hc-method", _type));
 1941       }
 1942   
 1943       /**
 1944        * Validate that the primary key class has all pk fields.
 1945        */
 1946       private void validateAppIdClassPKs(ClassMetaData meta,
 1947           FieldMetaData[] fmds, Class oid) {
 1948           if (fmds.length == 0 && !Modifier.isAbstract(meta.getDescribedType().
 1949               getModifiers()))
 1950               throw new MetaDataException(_loc.get("no-pk", _type));
 1951   
 1952           // check that the oid type contains all pk fields
 1953           Field f;
 1954           Method m;
 1955           Class c;
 1956           for (int i = 0; i < fmds.length; i++) {
 1957               switch (fmds[i].getDeclaredTypeCode()) {
 1958                   case JavaTypes.ARRAY:
 1959                       c = fmds[i].getDeclaredType().getComponentType();
 1960                       if (c == byte.class || c == Byte.class
 1961                           || c == char.class || c == Character.class) {
 1962                           c = fmds[i].getDeclaredType();
 1963                           break;
 1964                       }
 1965                       // else no break
 1966                   case JavaTypes.PC_UNTYPED:
 1967                   case JavaTypes.COLLECTION:
 1968                   case JavaTypes.MAP:
 1969                   case JavaTypes.OID: // we're validating embedded fields
 1970                       throw new MetaDataException(_loc.get("bad-pk-type",
 1971                           fmds[i]));
 1972                   default:
 1973                       c = fmds[i].getObjectIdFieldType();
 1974               }
 1975   
 1976               if (meta.getAccessType() == ACCESS_FIELD) {
 1977                   f = Reflection.findField(oid, fmds[i].getName(), false);
 1978                   if (f == null || !f.getType().isAssignableFrom(c))
 1979                       throw new MetaDataException(_loc.get("invalid-id",
 1980                           _type, fmds[i].getName()));
 1981               } else if (meta.getAccessType() == ACCESS_PROPERTY) {
 1982                   m = Reflection.findGetter(oid, fmds[i].getName(), false);
 1983                   if (m == null || !m.getReturnType().isAssignableFrom(c))
 1984                       throw new MetaDataException(_loc.get("invalid-id",
 1985                           _type, fmds[i].getName()));
 1986                   m = Reflection.findSetter(oid, fmds[i].getName(),
 1987                       fmds[i].getObjectIdFieldType(), false);
 1988                   if (m == null || m.getReturnType() != void.class)
 1989                       throw new MetaDataException(_loc.get("invalid-id",
 1990                           _type, fmds[i].getName()));
 1991               }
 1992           }
 1993       }
 1994   
 1995       /**
 1996        * Validate that this class doesn't declare any primary key fields.
 1997        */
 1998       private void validateNoPKFields() {
 1999           FieldMetaData[] fields = getDeclaredFields();
 2000           for (int i = 0; i < fields.length; i++)
 2001               if (fields[i].isPrimaryKey())
 2002                   throw new MetaDataException(_loc.get("bad-pk", fields[i]));
 2003       }
 2004   
 2005       /**
 2006        * Assert that this class' access type is allowed.
 2007        */
 2008       private void validateAccessType() {
 2009           if (_accessType == ACCESS_UNKNOWN)
 2010               return;
 2011           ClassMetaData sup = getPCSuperclassMetaData();
 2012           if (sup != null && sup.getAccessType() != ACCESS_UNKNOWN
 2013               && sup.getAccessType() != _accessType &&
 2014               getPCSuperclassMetaData().getFields().length > 0) {
 2015               throw new MetaDataException(_loc.get("inconsistent-access",
 2016                   this, sup));
 2017           }
 2018       }
 2019   
 2020       /**
 2021        * Assert that detachment configuration is valid.
 2022        */
 2023       private void validateDetachable() {
 2024           boolean first = true;
 2025           for (ClassMetaData parent = getPCSuperclassMetaData();
 2026               first && parent != null; parent = parent.getPCSuperclassMetaData())
 2027           {
 2028               if (parent.isDetachable())
 2029                   first = false;
 2030           }
 2031   
 2032           Field field = getDetachedStateField();
 2033           if (field != null) {
 2034               if (!first)
 2035                   throw new MetaDataException(_loc.get("parent-detach-state",
 2036                       _type));
 2037               if (getField(field.getName()) != null)
 2038                   throw new MetaDataException(_loc.get("managed-detach-state",
 2039                       field.getName(), _type));
 2040               if (field.getType() != Object.class)
 2041                   throw new MetaDataException(_loc.get("bad-detach-state",
 2042                       field.getName(), _type));
 2043           }
 2044       }
 2045   
 2046       ///////////////
 2047       // Fetch Group
 2048       ///////////////
 2049   
 2050       /**
 2051        * Return the fetch groups declared explicitly in this type.
 2052        */
 2053       public FetchGroup[] getDeclaredFetchGroups() {
 2054           if (_fgs == null)
 2055               _fgs = (_fgMap == null) ? EMPTY_FETCH_GROUP_ARRAY : (FetchGroup[])
 2056                   _fgMap.values().toArray(new FetchGroup[_fgMap.size()]); 
 2057           return _fgs;
 2058       }
 2059   
 2060       /**
 2061        * Return all fetch groups for this type, including superclass groups.
 2062        */
 2063       public FetchGroup[] getCustomFetchGroups() {
 2064           if (_customFGs == null) {
 2065               // map fgs to names, allowing our fgs to override supers
 2066               Map fgs = new HashMap();
 2067               ClassMetaData sup = getPCSuperclassMetaData();
 2068               if (sup != null)
 2069               {
 2070                   FetchGroup[] supFGs = sup.getCustomFetchGroups();
 2071                   for (int i = 0; i < supFGs.length; i++)
 2072                       fgs.put(supFGs[i].getName(), supFGs[i]);
 2073               }
 2074               FetchGroup[] decs = getDeclaredFetchGroups();
 2075               for (int i = 0; i < decs.length; i++)
 2076                   fgs.put(decs[i].getName(), decs[i]);
 2077               
 2078               // remove std groups
 2079               fgs.remove(FetchGroup.NAME_DEFAULT);
 2080               fgs.remove(FetchGroup.NAME_ALL);
 2081   
 2082               _customFGs = (FetchGroup[]) fgs.values().toArray
 2083                   (new FetchGroup[fgs.size()]);
 2084           }
 2085           return _customFGs;
 2086       }
 2087   
 2088       /**
 2089        * Gets a named fecth group. If not available in this receiver then looks
 2090        * up the inheritence hierarchy. 
 2091        *
 2092        * @param name name of a fetch group.
 2093        * @return an existing fecth group of the given name if known to this 
 2094        * receiver or any of its superclasses. Otherwise null.
 2095        */
 2096       public FetchGroup getFetchGroup(String name) {
 2097           FetchGroup fg = (_fgMap == null) ? null : (FetchGroup) _fgMap.get(name);
 2098           if (fg != null)
 2099               return fg;
 2100           ClassMetaData sup = getPCSuperclassMetaData();
 2101           if (sup != null)
 2102               return sup.getFetchGroup(name);
 2103           if (FetchGroup.NAME_DEFAULT.equals(name))
 2104               return FetchGroup.DEFAULT;
 2105           if (FetchGroup.NAME_ALL.equals(name))
 2106               return FetchGroup.ALL;
 2107           return null;
 2108       }
 2109   
 2110       /**
 2111        * Adds fetch group of the given name, or returns existing instance.
 2112        *
 2113        * @param name a non-null, non-empty name. Must be unique within this
 2114        * receiver's scope. The super class <em>may</em> have a group with
 2115        * the same name.
 2116        */
 2117       public FetchGroup addDeclaredFetchGroup(String name) {
 2118       	if (StringUtils.isEmpty(name))
 2119       		throw new MetaDataException(_loc.get("empty-fg-name", this));
 2120           if (_fgMap == null)
 2121               _fgMap = new HashMap();
 2122           FetchGroup fg = (FetchGroup) _fgMap.get(name);
 2123           if (fg == null) {
 2124           	fg = new FetchGroup(this, name);
 2125           	_fgMap.put(name, fg);
 2126               _fgs = null;
 2127               _customFGs = null;
 2128           }
 2129           return fg;
 2130       }
 2131   
 2132       /**
 2133        * Remove a declared fetch group.
 2134        */
 2135       public boolean removeDeclaredFetchGroup(FetchGroup fg) {
 2136           if (fg == null)
 2137               return false;
 2138           if (_fgMap.remove(fg.getName()) != null) {
 2139               _fgs = null;
 2140               _customFGs = null;
 2141               return true;
 2142           }
 2143           return false;
 2144       }
 2145   
 2146       /////////////////
 2147       // SourceTracker
 2148       /////////////////
 2149   
 2150       public File getSourceFile() {
 2151           return _srcFile;
 2152       }
 2153   
 2154       public Object getSourceScope() {
 2155           return null;
 2156       }
 2157   
 2158       public int getSourceType() {
 2159           return _srcType;
 2160       }
 2161   
 2162       public void setSource(File file, int srcType) {
 2163           _srcFile = file;
 2164           _srcType = srcType;
 2165       }
 2166   
 2167       public String getResourceName() {
 2168           return _type.getName();
 2169       }
 2170   
 2171       /**
 2172        * The source mode this metadata has been loaded under.
 2173        */
 2174       public int getSourceMode() {
 2175           return _srcMode;
 2176       }
 2177   
 2178       /**
 2179        * The source mode this metadata has been loaded under.
 2180        */
 2181       public void setSourceMode(int mode) {
 2182           _srcMode = mode;
 2183       }
 2184   
 2185       /**
 2186        * The source mode this metadata has been loaded under.
 2187        */
 2188       public void setSourceMode(int mode, boolean on) {
 2189           if (mode == MODE_NONE)
 2190               _srcMode = mode;
 2191           else if (on)
 2192               _srcMode |= mode;
 2193           else
 2194               _srcMode &= ~mode;
 2195       }
 2196   
 2197       /**
 2198        * The index in which this class was listed in the metadata. Defaults to
 2199        * <code>-1</code> if this class was not listed in the metadata.
 2200        */
 2201       public int getListingIndex() {
 2202           return _listIndex;
 2203       }
 2204   
 2205       /**
 2206        * The index in which this field was listed in the metadata. Defaults to
 2207        * <code>-1</code> if this class was not listed in the metadata.
 2208        */
 2209       public void setListingIndex(int index) {
 2210           _listIndex = index;
 2211       }
 2212   
 2213       ///////////////
 2214       // Commentable
 2215       ///////////////
 2216   
 2217       public String[] getComments() {
 2218           return (_comments == null) ? EMPTY_COMMENTS : _comments;
 2219       }
 2220   
 2221       public void setComments(String[] comments) {
 2222           _comments = comments;
 2223       }
 2224   
 2225       //////////////
 2226       // State copy
 2227       //////////////
 2228   
 2229       /**
 2230        * Copy the metadata from the given instance to this one. Do not
 2231        * copy mapping information.
 2232        */
 2233       public void copy(ClassMetaData meta) {
 2234           if (meta.getDescribedType() != _type)
 2235               throw new InternalException();
 2236           super.copy(meta);
 2237   
 2238           // copy class-level info; use get methods to force resolution of
 2239           // lazy data
 2240           _super = meta.getPCSuperclass();
 2241           _objectId = meta.getObjectIdType();
 2242           _extent = (meta.getRequiresExtent()) ? Boolean.TRUE : Boolean.FALSE;
 2243           _embedded = (meta.isEmbeddedOnly()) ? Boolean.TRUE : Boolean.FALSE;
 2244           _interface = (meta.isManagedInterface()) ? Boolean.TRUE : Boolean.FALSE;
 2245           setIntercepting(meta.isIntercepting());
 2246           _impl = meta.getInterfaceImpl();
 2247           _identity = meta.getIdentityType();
 2248           _idStrategy = meta.getIdentityStrategy();
 2249           _seqName = meta.getIdentitySequenceName();
 2250           _seqMeta = null;
 2251           _alias = meta.getTypeAlias();
 2252           _accessType = meta.getAccessType();
 2253   
 2254           // only copy this information if it wasn't set explicitly for this
 2255           // instance
 2256           if (DEFAULT_STRING.equals(_cacheName))
 2257               _cacheName = meta.getDataCacheName();
 2258           if (_cacheTimeout == Integer.MIN_VALUE)
 2259               _cacheTimeout = meta.getDataCacheTimeout();
 2260           if (_detachable == null)
 2261               _detachable = meta._detachable;
 2262           if (DEFAULT_STRING.equals(_detachState))
 2263               _detachState = meta.getDetachedState();
 2264   
 2265           // synch field information; first remove extra fields
 2266           clearFieldCache();
 2267           _fieldMap.keySet().retainAll(meta._fieldMap.keySet());
 2268   
 2269           // add copies of declared fields; other defined fields already copied
 2270           FieldMetaData[] fields = meta.getDeclaredFields();
 2271           FieldMetaData field;
 2272           for (int i = 0; i < fields.length; i++) {
 2273               field = getDeclaredField(fields[i].getName());
 2274               if (field == null)
 2275                   field = addDeclaredField(fields[i].getName(),
 2276                       fields[i].getDeclaredType());
 2277               field.setDeclaredIndex(-1);
 2278               field.setIndex(-1);
 2279               field.copy(fields[i]);
 2280           }
 2281   
 2282           // copy fetch groups
 2283           FetchGroup[] fgs = meta.getDeclaredFetchGroups();
 2284           FetchGroup fg;
 2285           for (int i = 0; i < fgs.length; i++) {
 2286               fg = addDeclaredFetchGroup(fgs[i].getName());
 2287               fg.copy(fgs[i]); 
 2288           }
 2289   
 2290           // copy iface re-mapping
 2291           _ifaceMap.clear();
 2292           _ifaceMap.putAll(meta._ifaceMap);
 2293       }
 2294   
 2295       /**
 2296        * Recursive helper to copy embedded metadata.
 2297        */
 2298       private static void copy(ClassMetaData embed, ClassMetaData dec) {
 2299           ClassMetaData sup = dec.getPCSuperclassMetaData();
 2300           if (sup != null) {
 2301               embed.setPCSuperclass(sup.getDescribedType());
 2302               copy(embed.getPCSuperclassMetaData(), sup);
 2303           }
 2304           embed.copy(dec);
 2305       }
 2306   
 2307       protected void addExtensionKeys(Collection exts) {
 2308           _repos.getMetaDataFactory().addClassExtensionKeys(exts);
 2309       }
 2310   
 2311       /**
 2312        * Comparator used to put field metadatas into listing order.
 2313        */
 2314       private static class ListingOrderComparator
 2315           implements Comparator {
 2316   
 2317           private static final ListingOrderComparator _instance
 2318               = new ListingOrderComparator();
 2319   
 2320           /**
 2321            * Access singleton instance.
 2322            */
 2323           public static ListingOrderComparator getInstance() {
 2324               return _instance;
 2325           }
 2326   
 2327           public int compare(Object o1, Object o2) {
 2328               if (o1 == o2)
 2329                   return 0;
 2330               if (o1 == null)
 2331                   return 1;
 2332               if (o2 == null)
 2333                   return -1;
 2334   
 2335               FieldMetaData f1 = (FieldMetaData) o1;
 2336               FieldMetaData f2 = (FieldMetaData) o2;
 2337               if (f1.getListingIndex() == f2.getListingIndex()) {
 2338                   if (f1.getIndex() == f2.getIndex())
 2339                       return f1.getFullName(false).compareTo
 2340                           (f2.getFullName(false));
 2341   				if (f1.getIndex () == -1)
 2342   					return 1;
 2343   				if (f2.getIndex () == -1)
 2344   					return -1;
 2345   				return f1.getIndex () - f2.getIndex ();
 2346   			}	
 2347   			if (f1.getListingIndex () == -1)
 2348   				return 1;
 2349   			if (f2.getListingIndex () == -1)
 2350   				return -1;
 2351   			return f1.getListingIndex () - f2.getListingIndex ();
 2352   		}
 2353   	}
 2354   }

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