Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**********************************************************************
Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2007 Xuan Baldauf - Implement solution for issue CORE-3272
2007 Xuan Baldauf - allow users to explictly state that an array whose component type is not PC may still have PC elements. See CORE-3274
...
**********************************************************************/
package org.datanucleus.metadata;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ClassNameConstants;
import org.datanucleus.api.ApiAdapter;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.store.types.TypeManager;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* Abstract representation of MetaData for a field/property of a class/interface.
* The term "member" is used to represent either a field or a method(property). The term
* property is used to represent the name after cutting off any Java-beans style "get" prefix.
* This class is extended for fields (FieldMetaData) and properties (PropertyMetaData) to provide
* the explicit support for those components.
*/
public abstract class AbstractMemberMetaData extends MetaData implements Comparable, ColumnMetaDataContainer
{
/** Whether we currently allow persistence of static fields. */
public static final boolean persistStatic = false;
/** Whether we currently allow persistence of final fields. */
public static final boolean persistFinal = false;
/** Whether we currently allow persistence of transient fields. */
public static final boolean persistTransient = false;
/** Contains the metadata for column(s). */
protected ColumnMetaData[] columnMetaData;
/** Meta-Data of any container. */
protected ContainerMetaData containerMetaData;
/** EmbeddedMetaData. */
protected EmbeddedMetaData embeddedMetaData;
/** JoinMetaData. */
protected JoinMetaData joinMetaData;
/** ElementMetaData. */
protected ElementMetaData elementMetaData;
/** KeyMetaData. */
protected KeyMetaData keyMetaData;
/** ValueMetaData. */
protected ValueMetaData valueMetaData;
/** IndexMetaData. */
protected IndexMetaData indexMetaData;
/** The indexing value */
protected IndexedValue indexed = null;
/** UniqueMetaData. */
protected UniqueMetaData uniqueMetaData;
/** Whether to add a unique constraint. */
protected boolean uniqueConstraint = false;
/** OrderMetaData. */
protected OrderMetaData orderMetaData;
/** ForeignKeyMetaData. */
protected ForeignKeyMetaData foreignKeyMetaData;
/** default-fetch-group tag value. */
protected Boolean defaultFetchGroup;
/** column tag value. */
protected String column;
/** mapped-by tag value. */
protected String mappedBy;
/** embedded tag value. */
protected Boolean embedded;
/** Whether this field contains a reference that should be deleted when deleting this field. */
protected Boolean dependent;
/** serialized tag value. */
protected Boolean serialized;
/** cacheable tag value. */
protected boolean cacheable = true;
/** Whether to persist this relation when persisting the owning object. */
protected Boolean cascadePersist;
/** Whether to update this relation when updating the owning object. */
protected Boolean cascadeUpdate;
/** Whether to delete this relation when deleting the owning object (JPA). TODO Link this to dependent */
protected Boolean cascadeDelete;
/** Whether to refresh this relation when refreshing the owning object (JPA). */
protected Boolean cascadeRefresh;
/** Whether to remove orphans when deleting the owning object (JPA). */
protected boolean cascadeRemoveOrphans = false;
/** load-fetch-group value. */
protected String loadFetchGroup;
/** Default recursion-depth according to proposed final draft spec, [12.7.2]. */
public static final int DEFAULT_RECURSION_DEPTH = 1;
/** Indicates the recursion-depth is not defined. Use default value. */
public static final int UNDEFINED_RECURSION_DEPTH = 0;
/** recursion-depth value. */
protected int recursionDepth = UNDEFINED_RECURSION_DEPTH;
/** Field name. */
protected final String name;
/** null-value tag value (default is NONE). */
protected NullValue nullValue = NullValue.NONE;
/** persistence-modifier tag value. */
protected FieldPersistenceModifier persistenceModifier = FieldPersistenceModifier.DEFAULT;
/** primary key tag value. */
protected Boolean primaryKey;
/** Table name for this field. */
protected String table;
/** Catalog for the table specified for this field. */
protected String catalog;
/** Schema for the table specified for this field. */
protected String schema;
/**
* The value-strategy attribute specifies the strategy used to generate
* values for the field. This attribute has the same values and meaning as
* the strategy attribute in datastoreidentity.
*/
protected IdentityStrategy valueStrategy;
/** Name of a value generator if the user wants to override the default generator. */
protected String valueGeneratorName;
/**
* If the value-strategy is sequence, the sequence attribute specifies the
* name of the sequence to use to automatically generate a value for the field.
*/
protected String sequence;
/**
* Name of the class to which this field really belongs. Will be null if the field belongs
* to the parent ClassMetaData, and will have a value if it is an overriding field.
*/
protected String className = null;
/** Cache result of {@link #getFullFieldName()}. */
protected String fullFieldName = null;
/**
* Specification of the possible type(s) that can be stored in this field. This is for the case where the
* field/property is declared as an interface, or Object and hence can contain derived types. This
* provides the restriction to a particular type.
*/
protected String[] fieldTypes;
/** Field type being represented. */
protected Class type;
/** The member (field/method) being represented here. Note, this prevents Serialization. */
protected Member memberRepresented;
/**
* Id of the field in its class (only for fields that are managed).
* If the value is -1, the field is NOT managed or the object hasn't been populated.
*/
protected int fieldId=-1;
/** The relation type of this field (1-1, 1-N, M-N, N-1). */
protected int relationType = -1;
/**
* MetaData for the other end of a relation when this member is a bidirectional relation.
* This may be multiple fields if the FK is shared.
*/
protected AbstractMemberMetaData[] relatedMemberMetaData = null;
/** Temporary flag to signify if the field is ordered. */
protected boolean ordered = false;
// -------------------------------------------------------------------------
// These fields are only used when the MetaData is read by the parser and
// elements are dynamically added to the other elements. At runtime, "columnMetaData"
// should be used.
/** Columns ColumnMetaData */
protected List columns = new ArrayList();
/** Name of the target entity (when used with JPA MetaData on OneToOne, OneToMany etc) */
protected String targetClassName = null;
/** Wrapper for the ugly JPA "lob" so that when being populated we should make this serialised in some way. */
protected boolean storeInLob = false;
/** Flags for use in enhancement process [see JDO spec 21.14] */
protected byte persistenceFlags;
/**
* Convenience constructor to copy the specification from the passed member.
* This is used when we have an overriding field and we make a copy of the baseline
* member as a starting point.
* @param parent The parent
* @param mmd The member metadata to copy
*/
public AbstractMemberMetaData(MetaData parent, AbstractMemberMetaData mmd)
{
super(parent, mmd);
// Copy the simple field values
this.name = mmd.name;
this.primaryKey = mmd.primaryKey;
this.defaultFetchGroup = mmd.defaultFetchGroup;
this.column = mmd.column;
this.mappedBy = mmd.mappedBy;
this.dependent = mmd.dependent;
this.embedded = mmd.embedded;
this.serialized = mmd.serialized;
this.cascadePersist = mmd.cascadePersist;
this.cascadeUpdate = mmd.cascadeUpdate;
this.cascadeDelete = mmd.cascadeDelete;
this.cascadeRefresh = mmd.cascadeRefresh;
this.nullValue = mmd.nullValue;
this.persistenceModifier = mmd.persistenceModifier;
this.table = mmd.table;
this.indexed = mmd.indexed;
this.valueStrategy = mmd.valueStrategy;
this.valueGeneratorName = mmd.valueGeneratorName;
this.sequence = mmd.sequence;
this.uniqueConstraint = mmd.uniqueConstraint;
this.loadFetchGroup = mmd.loadFetchGroup;
this.storeInLob = mmd.storeInLob;
if (mmd.fieldTypes != null)
{
this.fieldTypes = new String[mmd.fieldTypes.length];
for (int i=0;i= 0)
{
// TODO Check if this is a valid className
this.className = name.substring(0,name.lastIndexOf('.'));
this.name = name.substring(name.lastIndexOf('.')+1);
}
else
{
this.name = name;
}
}
/**
* Method to provide the details of the field being represented by this MetaData hence populating
* certain parts of the MetaData. This is used to firstly provide defaults for attributes that aren't
* specified in the MetaData, and secondly to report any errors with attributes that have been specifed
* that are inconsistent with the field being represented.
* Either a field or a method should be passed in (one or the other) depending on what is being represented
* by this "member".
* @param clr ClassLoaderResolver to use for any class loading
* @param field Field that we are representing (if it's a field)
* @param method Method(property) that we are representing (if it's a method).
* @param primary the primary ClassLoader to use (or null)
* @param mmgr MetaData manager
*/
public synchronized void populate(ClassLoaderResolver clr, Field field, Method method,
ClassLoader primary, MetaDataManager mmgr)
{
if (isPopulated() || isInitialised())
{
return;
}
if (mmgr != null)
{
// Set defaults for cascading when not yet set
ApiAdapter apiAdapter = mmgr.getNucleusContext().getApiAdapter();
if (cascadePersist == null)
{
cascadePersist = apiAdapter.getDefaultCascadePersistForField();
}
if (cascadeUpdate == null)
{
cascadeUpdate = apiAdapter.getDefaultCascadeUpdateForField();
}
if (cascadeDelete == null)
{
cascadeDelete = apiAdapter.getDefaultCascadeDeleteForField();
}
if (cascadeRefresh == null)
{
cascadeRefresh = apiAdapter.getDefaultCascadeRefreshForField();
}
}
if (field == null && method == null)
{
NucleusLogger.METADATA.error(LOCALISER.msg("044106",name,getClassName(false)));
throw new InvalidMetaDataException(LOCALISER, "MetaData.Field.PopulateWithNullError",
name,getClassName(false));
}
// No class loader, so use System
if (clr == null)
{
NucleusLogger.METADATA.warn(LOCALISER.msg("044067",name,getClassName(true)));
clr = mmgr.getNucleusContext().getClassLoaderResolver(null);
}
memberRepresented = (field != null ? field : method);
if (field != null)
{
this.type = field.getType();
}
else if (method != null)
{
this.type = method.getReturnType();
}
if (className != null)
{
// If the field is overriding a superclass field, check that it is valid
Class thisClass = null;
try
{
thisClass = clr.classForName(getAbstractClassMetaData().getPackageName() + "." + getAbstractClassMetaData().getName());
}
catch (ClassNotResolvedException cnre)
{
// Do nothing
}
Class fieldClass = null;
try
{
fieldClass = clr.classForName(className);
}
catch (ClassNotResolvedException cnre)
{
try
{
fieldClass = clr.classForName(getAbstractClassMetaData().getPackageName() + "." + className);
className = getAbstractClassMetaData().getPackageName() + "." + className;
}
catch (ClassNotResolvedException cnre2)
{
NucleusLogger.METADATA.error(LOCALISER.msg("044113", getAbstractClassMetaData().getName(), name, className));
throw new InvalidMetaDataException(LOCALISER, "044113", getAbstractClassMetaData().getName(), name, className);
}
}
if (fieldClass != null && !fieldClass.isAssignableFrom(thisClass))
{
// TODO We could also check if PersistenceCapable, but won't work when enhancing
NucleusLogger.METADATA.error(LOCALISER.msg("044114", getAbstractClassMetaData().getName(), name, className));
throw new InvalidMetaDataException(LOCALISER, "044114", getAbstractClassMetaData().getName(), name, className);
}
}
if (primaryKey == null)
{
// Primary key not set by user so initialise it to false
primaryKey = Boolean.FALSE;
}
// Update "embedded" based on type
if (primaryKey == Boolean.FALSE && embedded == null)
{
Class element_type=getType();
if (element_type.isArray())
{
element_type = element_type.getComponentType();
if (mmgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(element_type))
{
embedded = Boolean.TRUE;
}
}
else if (mmgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(element_type))
{
embedded = Boolean.TRUE;
}
}
if (embedded == null)
{
embedded = Boolean.FALSE;
}
// Update "persistence-modifier" according to type etc
if (FieldPersistenceModifier.DEFAULT.equals(persistenceModifier))
{
boolean isPcClass = getType().isArray() ? isFieldArrayTypePersistable(mmgr) : mmgr.isFieldTypePersistable(type);
if (!isPcClass)
{
if (getType().isArray() && getType().getComponentType().isInterface())
{
isPcClass =
(mmgr.getMetaDataForClassInternal(getType().getComponentType(), clr) != null);
}
else if (getType().isInterface())
{
isPcClass = (mmgr.getMetaDataForClassInternal(getType(), clr) != null);
}
}
persistenceModifier = getDefaultFieldPersistenceModifier(getType(), memberRepresented.getModifiers(),
isPcClass, mmgr);
}
// TODO If this field is NONE in superclass, make it NONE here too
// Check for array types supported by DataNucleus
// TODO This check is specific to mapped datastores since any array can be persisted to OODB for example
/* if (!persistenceModifier.equals(FieldPersistenceModifier.NONE) && getType().isArray() &&
!getAbstractClassMetaData().getMetaDataManager().isEnhancing())
{
// We support simple arrays, arrays of PCs, and arrays of reference types, and anything serialised
ApiAdapter api = getMetaDataManager().getNucleusContext().getApiAdapter();
if (!getMetaDataManager().getNucleusContext().getTypeManager().isSupportedMappedType(getType().getName()) &&
!api.isPersistable(getType().getComponentType()) &&
!getType().getComponentType().isInterface() &&
!getType().getComponentType().getName().equals("java.lang.Object")
&& serialized != Boolean.TRUE)
{
NucleusLogger.METADATA.warn(LOCALISER.msg("044111",name,getClassName(false)));
throw new InvalidMetaDataException(LOCALISER,
"044111", name,getClassName(false));
}
}*/
// Update "default-fetch-group" according to type
if (defaultFetchGroup == null && persistenceModifier.equals(FieldPersistenceModifier.NONE))
{
defaultFetchGroup = Boolean.FALSE;
}
else if (defaultFetchGroup == null && persistenceModifier.equals(FieldPersistenceModifier.TRANSACTIONAL))
{
defaultFetchGroup = Boolean.FALSE;
}
else if (defaultFetchGroup == null)
{
defaultFetchGroup = Boolean.FALSE;
if (!primaryKey.equals(Boolean.TRUE))
{
boolean foundGeneric = false;
TypeManager typeMgr = mmgr.getNucleusContext().getTypeManager();
if (Collection.class.isAssignableFrom(getType()))
{
String elementTypeName = null;
try
{
if (field != null)
{
elementTypeName = ClassUtils.getCollectionElementType(field);
}
else
{
elementTypeName = ClassUtils.getCollectionElementType(method);
}
}
catch (NucleusUserException nue)
{
// User has put some stupid type in so ignore it. Idiots
}
if (elementTypeName != null)
{
// Try to find using generic type specialisation
Class elementType = clr.classForName(elementTypeName);
if (typeMgr.isDefaultFetchGroupForCollection(getType(), elementType))
{
foundGeneric = true;
defaultFetchGroup = Boolean.TRUE;
}
}
}
if (!foundGeneric && typeMgr.isDefaultFetchGroup(getType()))
{
defaultFetchGroup = Boolean.TRUE;
}
}
}
// Field is not specified as "persistent" yet has DFG or primary-key !
if (persistenceModifier.equals(FieldPersistenceModifier.TRANSACTIONAL) ||
persistenceModifier.equals(FieldPersistenceModifier.NONE))
{
if (defaultFetchGroup == Boolean.TRUE || primaryKey == Boolean.TRUE)
{
throw new InvalidMetaDataException(LOCALISER, "044109",
name, getClassName(true), this.getType().getName(),
persistenceModifier.toString());
}
}
if (storeInLob)
{
// Set up the serialized flag according to the field type in line with JPA1
boolean useClob = false;
if (type == String.class ||
(type.isArray() && type.getComponentType() == Character.class) ||
(type.isArray() && type.getComponentType() == char.class))
{
useClob = true;
if (columns == null || columns.size() == 0)
{
// Create a CLOB column. What if the RDBMS doesn't support CLOB ?
ColumnMetaData colmd = new ColumnMetaData();
colmd.setName(column);
colmd.setJdbcType("CLOB");
addColumn(colmd);
}
else
{
ColumnMetaData colmd = columns.get(0);
colmd.setJdbcType("CLOB");
}
}
if (!useClob)
{
serialized = Boolean.TRUE;
}
}
if (containerMetaData == null)
{
// No container information, so generate if array/collection/map field
if (type.isArray())
{
// User hasn't specified but the field is an array so infer it
Class arrayCls = type.getComponentType();
ArrayMetaData arrmd = new ArrayMetaData();
arrmd.setElementType(arrayCls.getName());
setContainer(arrmd);
}
else if (java.util.Collection.class.isAssignableFrom(type))
{
if (targetClassName != null)
{
// User has specified target class name (JPA) so generate the
CollectionMetaData collmd = new CollectionMetaData();
collmd.setElementType(targetClassName);
setContainer(collmd);
}
else
{
// No collection element type specified but using JDK1.5 so try generics
String elementType = null;
if (field != null)
{
elementType = ClassUtils.getCollectionElementType(field);
}
else
{
elementType = ClassUtils.getCollectionElementType(method);
}
if (elementType != null)
{
// Use generics information to infer any missing parts
CollectionMetaData collmd = new CollectionMetaData();
collmd.setElementType(elementType);
setContainer(collmd);
}
else
{
// Default to "Object" as element type
CollectionMetaData collmd = new CollectionMetaData();
collmd.setElementType(Object.class.getName());
setContainer(collmd);
NucleusLogger.METADATA.debug(LOCALISER.msg("044003", getFullFieldName()));
}
}
}
else if (java.util.Map.class.isAssignableFrom(type))
{
if (targetClassName != null)
{
// User has specified target class name (JPA) so generate the