blob: 740353db2ca8fb3e7a9612f79af6df8b01696793 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 05/23/2008-1.0M8 Guy Pelletier
// - 211330: Add attributes-complete support to the EclipseLink-ORM.XML Schema
// 05/30/2008-1.0M8 Guy Pelletier
// - 230213: ValidationException when mapping to attribute in MappedSuperClass
// 07/15/2008-1.0.1 Guy Pelletier
// - 240679: MappedSuperclass Id not picked when on get() method accessor
// 09/23/2008-1.1 Guy Pelletier
// - 241651: JPA 2.0 Access Type support
// 10/01/2008-1.1 Guy Pelletier
// - 249329: To remain JPA 1.0 compliant, any new JPA 2.0 annotations should be referenced by name
// 12/12/2008-1.1 Guy Pelletier
// - 249860: Implement table per class inheritance support.
// 01/28/2009-2.0 Guy Pelletier
// - 248293: JPA 2.0 Element Collections (part 1)
// 02/06/2009-2.0 Guy Pelletier
// - 248293: JPA 2.0 Element Collections (part 2)
// 02/26/2009-2.0 Guy Pelletier
// - 264001: dot notation for mapped-by and order-by
// 03/27/2009-2.0 Guy Pelletier
// - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
// 04/03/2009-2.0 Guy Pelletier
// - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
// 04/24/2009-2.0 Guy Pelletier
// - 270011: JPA 2.0 MappedById support
// 10/21/2009-2.0 Guy Pelletier
// - 290567: mappedbyid support incomplete
// 11/06/2009-2.0 Guy Pelletier
// - 286317: UniqueConstraint xml element is changing (plus couple other fixes, see bug)
// 01/22/2010-2.0.1 Guy Pelletier
// - 294361: incorrect generated table for element collection attribute overrides
// 01/26/2010-2.0.1 Guy Pelletier
// - 299893: @MapKeyClass does not work with ElementCollection
// 03/29/2010-2.1 Guy Pelletier
// - 267217: Add Named Access Type to EclipseLink-ORM
// 04/09/2010-2.1 Guy Pelletier
// - 307050: Add defaults for access methods of a VIRTUAL access type
// 04/27/2010-2.1 Guy Pelletier
// - 309856: MappedSuperclasses from XML are not being initialized properly
// 05/04/2010-2.1 Guy Pelletier
// - 309373: Add parent class attribute to EclipseLink-ORM
// 05/14/2010-2.1 Guy Pelletier
// - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
// 06/14/2010-2.2 Guy Pelletier
// - 264417: Table generation is incorrect for JoinTables in AssociationOverrides
// 06/22/2010-2.2 Guy Pelletier
// - 308729: Persistent Unit deployment exception when mappedsuperclass has no annotations but has lifecycle callbacks
// 07/05/2010-2.1.1 Guy Pelletier
// - 317708: Exception thrown when using LAZY fetch on VIRTUAL mapping
// 09/16/2010-2.2 Guy Pelletier
// - 283028: Add support for letting an @Embeddable extend a @MappedSuperclass
// 12/01/2010-2.2 Guy Pelletier
// - 331234: xml-mapping-metadata-complete overriden by metadata-complete specification
// 12/02/2010-2.2 Guy Pelletier
// - 251554: ExcludeDefaultMapping annotation needed
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 1)
// 04/04/2012-2.3.3 Guy Pelletier
// - 362180: ConcurrentModificationException on predeploy for AttributeOverride
// 04/07/2012-2.5 Guy Pelletier
// - 384275: Customizer from a mapped superclass is not overridden by an entity customizer
// 10/25/2012-2.5 Guy Pelletier
// - 3746888: JPA 2.1 Converter support
// 11/19/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
package org.eclipse.persistence.internal.jpa.metadata.accessors.classes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import org.eclipse.persistence.annotations.Array;
import org.eclipse.persistence.annotations.BasicCollection;
import org.eclipse.persistence.annotations.BasicMap;
import org.eclipse.persistence.annotations.ChangeTracking;
import org.eclipse.persistence.annotations.Customizer;
import org.eclipse.persistence.annotations.CopyPolicy;
import org.eclipse.persistence.annotations.ExcludeDefaultMappings;
import org.eclipse.persistence.annotations.InstantiationCopyPolicy;
import org.eclipse.persistence.annotations.CloneCopyPolicy;
//import org.eclipse.persistence.annotations.NoSql;
import org.eclipse.persistence.annotations.Properties;
import org.eclipse.persistence.annotations.Property;
import org.eclipse.persistence.annotations.Struct;
import org.eclipse.persistence.annotations.Structure;
import org.eclipse.persistence.annotations.Transformation;
import org.eclipse.persistence.annotations.VariableOneToOne;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.metadata.accessors.PropertyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ElementCollectionAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.EmbeddedIdAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.DerivedIdClassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToManyAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ManyToOneAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicCollectionAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicMapAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.EmbeddedAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.IdAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappedKeyMapAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToManyAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.OneToOneAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.TransformationAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.VariableOneToOneAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.VersionAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.MetadataAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotatedElement;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataField;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataMethod;
import org.eclipse.persistence.internal.jpa.metadata.structures.ArrayAccessor;
import org.eclipse.persistence.internal.jpa.metadata.changetracking.ChangeTrackingMetadata;
import org.eclipse.persistence.internal.jpa.metadata.columns.AssociationOverrideMetadata;
import org.eclipse.persistence.internal.jpa.metadata.columns.AttributeOverrideMetadata;
import org.eclipse.persistence.internal.jpa.metadata.copypolicy.CopyPolicyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.copypolicy.CustomCopyPolicyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.copypolicy.InstantiationCopyPolicyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.copypolicy.CloneCopyPolicyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.nosql.NoSqlMetadata;
import org.eclipse.persistence.internal.jpa.metadata.queries.OracleArrayTypeMetadata;
import org.eclipse.persistence.internal.jpa.metadata.queries.OracleObjectTypeMetadata;
import org.eclipse.persistence.internal.jpa.metadata.queries.PLSQLRecordMetadata;
import org.eclipse.persistence.internal.jpa.metadata.queries.PLSQLTableMetadata;
import org.eclipse.persistence.internal.jpa.metadata.structures.StructMetadata;
import org.eclipse.persistence.internal.jpa.metadata.structures.StructureAccessor;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor;
import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.jpa.metadata.MetadataProject;
import org.eclipse.persistence.internal.jpa.metadata.ORMetadata;
import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.ObjectAccessor;
import org.eclipse.persistence.platform.database.oracle.annotations.OracleArray;
import org.eclipse.persistence.platform.database.oracle.annotations.OracleArrays;
import org.eclipse.persistence.platform.database.oracle.annotations.OracleObject;
import org.eclipse.persistence.platform.database.oracle.annotations.OracleObjects;
import org.eclipse.persistence.platform.database.oracle.annotations.PLSQLRecord;
import org.eclipse.persistence.platform.database.oracle.annotations.PLSQLRecords;
import org.eclipse.persistence.platform.database.oracle.annotations.PLSQLTable;
import org.eclipse.persistence.platform.database.oracle.annotations.PLSQLTables;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.EL_ACCESS_VIRTUAL;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_FIELD;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_PROPERTY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ASSOCIATION_OVERRIDE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ASSOCIATION_OVERRIDES;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ATTRIBUTE_OVERRIDE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ATTRIBUTE_OVERRIDES;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_BASIC;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ELEMENT_COLLECTION;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_EMBEDDED;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_EMBEDDED_ID;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ID;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MANY_TO_MANY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MANY_TO_ONE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MAPPED_SUPERCLASS;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ONE_TO_MANY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ONE_TO_ONE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_VERSION;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_TRANSIENT;
/**
* INTERNAL:
* A abstract class accessor. Holds common metadata for entities, embeddables
* and mapped superclasses.
*
* Key notes:
* - all metadata mapped from XML to this class must be compared in the
* equals method.
* - all metadata mapped from XML must be initialized in the initXMLObject
* method.
* - all metadata mapped from XML to this class must be handled in the merge
* method. (merging is done at the accessor/mapping level)
* - any metadata mapped from XML to this class must be initialized in the
* initXMLObject method.
* - methods should be preserved in alphabetical order.
*
* @author Guy Pelletier
* @since TopLink EJB 3.0 Reference Implementation
*/
@SuppressWarnings("deprecation")
public abstract class ClassAccessor extends MetadataAccessor {
private boolean m_isPreProcessed = false;
private boolean m_isProcessed = false;
private Boolean m_excludeDefaultMappings;
private Boolean m_metadataComplete;
private ChangeTrackingMetadata m_changeTracking;
// Various copy policies. Represented individually to facilitate XML writing.
private CloneCopyPolicyMetadata m_cloneCopyPolicy;
private CustomCopyPolicyMetadata m_customCopyPolicy;
private InstantiationCopyPolicyMetadata m_instantiationCopyPolicy;
private List<AssociationOverrideMetadata> m_associationOverrides = new ArrayList<AssociationOverrideMetadata>();
private List<AttributeOverrideMetadata> m_attributeOverrides = new ArrayList<AttributeOverrideMetadata>();
private List<MappedSuperclassAccessor> m_mappedSuperclasses = new ArrayList<MappedSuperclassAccessor>();
private List<OracleObjectTypeMetadata> m_oracleObjectTypes = new ArrayList<OracleObjectTypeMetadata>();
private List<OracleArrayTypeMetadata> m_oracleArrayTypes = new ArrayList<OracleArrayTypeMetadata>();
private List<PLSQLRecordMetadata> m_plsqlRecords = new ArrayList<PLSQLRecordMetadata>();
private List<PLSQLTableMetadata> m_plsqlTables = new ArrayList<PLSQLTableMetadata>();
// In the normal case owning descriptors is a single list. Could only be
// multiples when dealing with embeddable accessors.
private List<MetadataDescriptor> m_owningDescriptors = new ArrayList<MetadataDescriptor>();
private MetadataClass m_customizerClass;
private MetadataClass m_parentClass;
private String m_className;
private String m_customizerClassName;
private String m_parentClassName;
private String m_description;
private XMLAttributes m_attributes;
private StructMetadata m_struct;
private NoSqlMetadata m_noSql;
/**
* INTERNAL:
*/
protected ClassAccessor(String xmlElement) {
super(xmlElement);
}
/**
* INTERNAL:
*/
public ClassAccessor(MetadataAnnotation annotation, MetadataClass cls, MetadataProject project) {
super(annotation, cls, new MetadataDescriptor(cls), project);
// Set the class accessor reference on the descriptor.
getDescriptor().setClassAccessor(this);
// Look for an explicit access type specification.
initAccess();
}
/**
* INTERNAL:
* Called from MappedSuperclassAccessor. We want to avoid setting the
* class accessor on the descriptor to be the MappedSuperclassAccessor.
*/
protected ClassAccessor(MetadataAnnotation annotation, MetadataClass cls, MetadataDescriptor descriptor) {
super(annotation, cls, descriptor, descriptor.getProject());
// Look for an explicit access type specification.
initAccess();
}
/**
* INTERNAL:
* Add the accessor to the descriptor
*/
protected void addAccessor(MappingAccessor accessor) {
if (accessor != null) {
// Process any converters on this mapping accessor.
accessor.processConverters();
// Add any embeddedid references to the list of
// (@IdClass and @EmbeddedId reference classes) id 'used' classes.
if (accessor.isEmbeddedId()) {
getProject().addIdClass(accessor.getReferenceClassName());
}
// Add the embeddable accessor to the project. In the case of
// pre-processing, if we are an embeddable accessor the nested
// embeddable will be pre-processed now.
addPotentialEmbeddableAccessor(accessor.getReferenceClass(), accessor.getClassAccessor());
// Tell an embeddable accessor that is a map key to a collection
// to pre-process itself.
if (accessor.isMappedKeyMapAccessor()) {
MappedKeyMapAccessor mapAccessor = (MappedKeyMapAccessor) accessor;
MetadataClass mapKeyClass = mapAccessor.getMapKeyClass();
// If the map key class is not specified, we need to look it
// up from the accessor type.
if (mapKeyClass == null || mapKeyClass.isClass(void.class)) {
// Try to extract the map key class from a generic
// specification. This will throw an exception if it can't.
mapKeyClass = accessor.getMapKeyReferenceClass();
// Set the map key class.
mapAccessor.setMapKeyClass(mapKeyClass);
}
// Add the embeddable accessor to the project. In the case of
// pre-processing, if we are an embeddable accessor the nested
// embeddable will be pre-processed now.
addPotentialEmbeddableAccessor(mapKeyClass, accessor.getClassAccessor());
}
// Add the accessor to the descriptor.
getDescriptor().addMappingAccessor(accessor);
}
}
/**
* INTERNAL:
* Add the accessors from this class accessors java class to the descriptor
* tied to this class accessor. This method is called for every class
* accessor and is also called from parent class accessors to each of its
* subclasses of a TABLE_PER_CLASS inheritance strategy.
*
* Add accessors is called in the preProcess stage and must not be called
* until its owning class accessor has processed its access type.
*/
public void addAccessors() {
if (m_attributes != null) {
for (MappingAccessor accessor : m_attributes.getAccessors()) {
// Load the accessible object from the class.
MetadataAccessibleObject accessibleObject = null;
// We must init all xml mapping accessors with a reference
// of their owning class accessor. The mapping accessors
// require metatata information from them to ensure they
// process themselves correctly.
accessor.initXMLMappingAccessor(this);
// To load the accessible object we must check the access type
// on the individual accessors. If no type is defined we will
// ask the class accessor which can either return an explicit
// type it specified or a default type (pu default or inherited
// from a parent class)
if (accessor.usesVirtualAccess()) {
accessibleObject = getAccessibleVirtualMethod(accessor);
} else if (accessor.usesPropertyAccess()) {
accessibleObject = getAccessibleMethod(accessor);
} else {
accessibleObject = getAccessibleField(accessor);
}
// If we have no accessible object at this point and no
// exception has been thrown then the user decorated an invalid
// attribute. A log warning will have been issued, do not
// further process this accessor.
if (accessibleObject != null) {
// Initialize the accessor with its real accessible object
// now, that is a field or method since it will currently
// hold a reference to its owning class' accessible object.
accessor.initXMLObject(accessibleObject, getEntityMappings());
// It's now safe to init the correct access type for this
// mapping accessor since we now have set the actual
// accessible object for this mapping accessor. Note: the
// initAccess call was originally in initXMLObject, but with
// the current processing setup that isn't valid since
// mapping accessors have their accessible object 'faked'
// out for xml merging purposes during XMLAttributes
// initXMLObject call. Doing the access initialization there
// could cause one of two problems: Firstly, an incorrect
// access type setting and secondly and more importantly, a
// null pointer exception (bug 264596) since our descriptor
// hasn't been set which we use to retrieve the default
// access type.
accessor.initAccess();
// After the accessor has been fully initialized we can ask
// the accessor to validate an attribute type specification
// for a virtual class.
if (accessor.usesVirtualAccess() && ! accessor.hasAttributeType()) {
throw ValidationException.noAttributeTypeSpecification(accessor.getAttributeName(), getJavaClassName(), getLocation());
}
// Add the accessor to the descriptor's list
addAccessor(accessor);
}
}
}
// Process the fields or methods on the class for annotations. Unless
// we are processing a virtual access type which means we should not
// look any further then what is defined in XML.
if (! usesVirtualAccess()) {
if (usesPropertyAccess()) {
addAccessorMethods(false);
} else {
addAccessorFields(false);
}
}
}
/**
* INTERNAL:
* Create mappings from the fields directly. If the mustBeExplicit flag
* is true, then we are processing the inverse of an explicit access
* setting and for a field to be processed it must have a Access(FIELD)
* setting.
*/
protected void addAccessorFields(boolean processingInverse) {
for (MetadataField metadataField : getJavaClass().getFields().values()) {
if (metadataField.isAnnotationPresent(JPA_TRANSIENT, this) || metadataField.shouldBeIgnored()) {
if (! metadataField.areAnnotationsCompatibleWithTransient(this)) {
throw ValidationException.mappingAnnotationsAppliedToTransientAttribute(metadataField);
}
} else {
// The is valid check will throw an exception if needed.
if (metadataField.isValidPersistenceField(processingInverse, this)) {
// If the accessor already exists, it may have come from XML
// or because of an explicit access type setting. E.G.
// Access type is property and we processed the access
// methods for this field, however the field has been tagged
// as access field. We must therefore overwrite the previous
// accessor with this explicit one.
if (! getDescriptor().hasMappingAccessor(metadataField.getAttributeName()) || (getDescriptor().hasMappingAccessor(metadataField.getAttributeName()) && processingInverse)) {
addAccessor(buildAccessor(metadataField));
}
}
}
}
// If we have an explicit access setting we must process the inverse
// for those accessors that have an Access(PROPERTY) setting.
if (hasAccess() && ! processingInverse) {
addAccessorMethods(true);
}
}
/**
* INTERNAL:
* Create mappings via the class properties. If the mustBeExplicit flag
* is true, then we are processing the inverse of an explicit access
* setting and for a field to be processed it must have a Access(PROPERTY)
* setting.
*/
protected void addAccessorMethods(boolean processingInverse) {
for (MetadataMethod metadataMethod : getJavaClass().getMethods().values()) {
if ( metadataMethod.isAnnotationPresent(JPA_TRANSIENT, this)) {
if (!metadataMethod.areAnnotationsCompatibleWithTransient(this)) {
throw ValidationException.mappingAnnotationsAppliedToTransientAttribute(metadataMethod);
}
} else {
// The is valid check will throw an exception if needed.
if (metadataMethod.isValidPersistenceMethod(processingInverse, this)) {
// If the accessor already exists, it may have come from XML
// or because of an explicit access type setting. E.G.
// Access type is field however the user indicated the we
// should use its access methods. We must therefore
// overwrite the previous accessor with this explicit one.
if (! getDescriptor().hasMappingAccessor(metadataMethod.getAttributeName()) || (getDescriptor().hasMappingAccessor(metadataMethod.getAttributeName()) && processingInverse)) {
addAccessor(buildAccessor(metadataMethod));
}
}
}
}
// If we have an explicit access setting we must process the inverse
// for those accessors that have an Access(FIELD)setting.
if (hasAccess() && ! processingInverse) {
addAccessorFields(true);
}
}
/**
* INTERNAL
* Add an embeddable class to the embeddable accessor list if it is
* indeed an embeddable. This method is overridden in EmbeddableAccessor
* and is called during pre-process. At the entity level all we want to do
* is set the owning descriptor whereas for nested embeddables they'll
* need the list of owning descriptors. Any nested embeddables will be
* discovered and pre-processed when pre-processing the known list of root
* embeddables.
* @see MetadataProject processStage1()
*/
protected void addPotentialEmbeddableAccessor(MetadataClass potentialEmbeddableClass, ClassAccessor embeddingAccessor) {
if (potentialEmbeddableClass != null) {
EmbeddableAccessor embeddableAccessor = getProject().getEmbeddableAccessor(potentialEmbeddableClass);
if (embeddableAccessor != null) {
embeddableAccessor.addEmbeddingAccessor(embeddingAccessor);
embeddableAccessor.addOwningDescriptor(getDescriptor());
if (getDescriptor().isMappedSuperclass() || getDescriptor().isEmbeddable()) {
// If the embeddable is from a metamodel mapped superclass
// descriptor or from an embeddable descriptor don't add it
// to root embeddable, just continue to pre-process. We'll
// hit this case when pre-processing a mapped superclass
// that is inherited from an embeddable.
if (!embeddableAccessor.isPreProcessed()) {
embeddableAccessor.preProcess();
}
} else {
// If it is for an entity, add it to the root list.
// Pre-processing will kick off in the later part of stage 1
// when we have collected all our embeddable roots. We must
// process embeddable roots from the root down to handle
// attribute and association overrides correctly.
getProject().addRootEmbeddableAccessor(embeddableAccessor);
}
}
}
}
/**
* INTERNAL:
* Add mapped superclass accessors to inheriting entities.
* Add new descriptors for these mapped superclasses to the core project.
*/
protected void addPotentialMappedSuperclass(MetadataClass metadataClass, boolean addMappedSuperclassAccessors) {
// Get the mappedSuperclass that was stored previously on the project
MappedSuperclassAccessor accessor = getProject().getMappedSuperclassAccessor(metadataClass);
if (accessor == null) {
// If the mapped superclass was not defined in XML then check for a
// MappedSuperclass annotation unless the addMappedSuperclassAccessors
// flag is false, meaning we are pre-processing for the canonical
// model and any and all mapped superclasses should have been
// discovered and we need not investigate this class further.
if (addMappedSuperclassAccessors) {
if (metadataClass.isAnnotationPresent(JPA_MAPPED_SUPERCLASS)) {
m_mappedSuperclasses.add(new MappedSuperclassAccessor(metadataClass.getAnnotation(JPA_MAPPED_SUPERCLASS), metadataClass, getDescriptor()));
// 266912: process and store mappedSuperclass descriptors on
// the project for later use by the Metamodel API.
getProject().addMetamodelMappedSuperclass(new MappedSuperclassAccessor(metadataClass.getAnnotation(JPA_MAPPED_SUPERCLASS), metadataClass, getProject()), getDescriptor());
}
}
} else {
// For the canonical model pre-processing we do not need to do any
// of the reloading (cloning) that we require for the regular
// metadata processing. Therefore, just add the mapped superclass
// directly leaving its current descriptor as is. When a mapped
// superclass accessor is reloaded for a sub entity, its descriptor
// is set to that entity's descriptor.
if (addMappedSuperclassAccessors) {
// Reload the accessor from XML to get our own instance not
// already on the project
m_mappedSuperclasses.add(reloadMappedSuperclass(accessor, getDescriptor()));
// 266912: process and store mappedSuperclass descriptors on the
// project for later use by the Metamodel API Note: we must
// again reload our accessor from XML or we will be sharing
// instances of the descriptor
getProject().addMetamodelMappedSuperclass(reloadMappedSuperclass(accessor, new MetadataDescriptor(metadataClass, getDescriptor().getClassAccessor())), getDescriptor());
} else {
m_mappedSuperclasses.add(accessor);
}
}
}
/**
* INTERNAL:
* Create and return the appropriate accessor based on the accessible
* object given. Order of checking is important, careful when modifying
* or adding, check what the isXyz call does to determine if the accessor
* is of type xyz.
*/
protected MappingAccessor buildAccessor(MetadataAnnotatedElement accessibleObject) {
if (accessibleObject.isBasicCollection(this)) {
return new BasicCollectionAccessor(accessibleObject.getAnnotation(BasicCollection.class), accessibleObject, this);
} else if (accessibleObject.isBasicMap(this)) {
return new BasicMapAccessor(accessibleObject.getAnnotation(BasicMap.class), accessibleObject, this);
} else if (accessibleObject.isArray(this)) {
return new ArrayAccessor(accessibleObject.getAnnotation(Array.class), accessibleObject, this);
} else if (accessibleObject.isElementCollection(this)) {
return new ElementCollectionAccessor(accessibleObject.getAnnotation(JPA_ELEMENT_COLLECTION), accessibleObject, this);
} else if (accessibleObject.isVersion(this)) {
return new VersionAccessor(accessibleObject.getAnnotation(JPA_VERSION), accessibleObject, this);
} else if (accessibleObject.isId(this) && ! accessibleObject.isDerivedId(this)) {
return new IdAccessor(accessibleObject.getAnnotation(JPA_ID), accessibleObject, this);
} else if (accessibleObject.isDerivedIdClass(this)) {
return new DerivedIdClassAccessor(accessibleObject, this);
} else if (accessibleObject.isBasic(this)) {
return new BasicAccessor(accessibleObject.getAnnotation(JPA_BASIC), accessibleObject, this);
} else if (accessibleObject.isStructure(this)) {
return new StructureAccessor(accessibleObject.getAnnotation(Structure.class), accessibleObject, this);
} else if (accessibleObject.isEmbedded(this)) {
return new EmbeddedAccessor(accessibleObject.getAnnotation(JPA_EMBEDDED), accessibleObject, this);
} else if (accessibleObject.isEmbeddedId(this)) {
return new EmbeddedIdAccessor(accessibleObject.getAnnotation(JPA_EMBEDDED_ID), accessibleObject, this);
} else if (accessibleObject.isTransformation(this)) {
return new TransformationAccessor(accessibleObject.getAnnotation(Transformation.class), accessibleObject, this);
} else if (accessibleObject.isManyToMany(this)) {
return new ManyToManyAccessor(accessibleObject.getAnnotation(JPA_MANY_TO_MANY), accessibleObject, this);
} else if (accessibleObject.isManyToOne(this)) {
return new ManyToOneAccessor(accessibleObject.getAnnotation(JPA_MANY_TO_ONE), accessibleObject, this);
} else if (accessibleObject.isOneToMany(this)) {
// A OneToMany can default and doesn't require an annotation to be present.
return new OneToManyAccessor(accessibleObject.getAnnotation(JPA_ONE_TO_MANY), accessibleObject, this);
} else if (accessibleObject.isOneToOne(this)) {
// A OneToOne can default and doesn't require an annotation to be present.
return new OneToOneAccessor(accessibleObject.getAnnotation(JPA_ONE_TO_ONE), accessibleObject, this);
} else if (accessibleObject.isVariableOneToOne(this)) {
// A VariableOneToOne can default and doesn't require an annotation to be present.
return new VariableOneToOneAccessor(accessibleObject.getAnnotation(VariableOneToOne.class), accessibleObject, this);
} else if (excludeDefaultMappings()) {
return null;
} else {
// Default case (everything else falls into a Basic)
return new BasicAccessor(accessibleObject.getAnnotation(JPA_BASIC), accessibleObject, this);
}
}
/**
* INTERNAL:
*/
protected void clearMappedSuperclassesAndInheritanceParents() {
// Re-initialize the mapped superclass list.
m_mappedSuperclasses.clear();
// Null out the inheritance parent and root descriptor before we start
// since they will be recalculated and used to determine when to stop
// looking for mapped superclasses.
getDescriptor().setInheritanceParentDescriptor(null);
getDescriptor().setInheritanceRootDescriptor(null);
}
/**
* INTERNAL:
* In some cases the pre-processing may need to be re-done. Namely, during
* the canonical model generation between compile rounds.
*/
public void clearPreProcessed() {
m_isPreProcessed = false;
// Clear any accessors previously gathered.
getDescriptor().clearMappingAccessors();
}
/**
* INTERNAL:
*/
@Override
public boolean equals(Object objectToCompare) {
if (objectToCompare instanceof ClassAccessor) {
ClassAccessor accessor = (ClassAccessor) objectToCompare;
return valuesMatch(getJavaClassName(), accessor.getJavaClassName());
}
return false;
}
@Override
public int hashCode() {
int result = super.hashCode();
String javaClassName = getJavaClassName();
result = 31 * result + (javaClassName != null ? javaClassName.hashCode() : 0);
return result;
}
/**
* INTERNAL:
* Return true if this class accessor has been set to metadata complete.
*/
public boolean excludeDefaultMappings() {
if (getProject().excludeDefaultMappings()) {
return true;
} else {
if (m_excludeDefaultMappings != null) {
return m_excludeDefaultMappings;
} else {
return isAnnotationPresent(ExcludeDefaultMappings.class);
}
}
}
/**
* INTERNAL:
* Return the accessible field for the given mapping accessor. Validation is
* performed on the existence of the field.
*/
protected MetadataField getAccessibleField(MappingAccessor accessor) {
MetadataField field = getJavaClass().getField(accessor.getName());
if (field == null) {
throw ValidationException.invalidFieldForClass(accessor.getName(), getJavaClass());
} else {
// True will force an exception to be thrown if it is not a valid
// field. However, if it is a transient accessor, don't validate it
// and return.
if (accessor.isTransient() || field.isValidPersistenceField(this, true)) {
return field;
}
return null;
}
}
/**
* INTERNAL:
* Return the accessible method for the given mapping accessor. Validation
* is performed on the existence of the method by property name or by
* the access methods if specified.
*/
protected MetadataMethod getAccessibleMethod(MappingAccessor accessor) {
if (accessor.hasAccessMethods()) {
MetadataMethod getMethod = getJavaClass().getMethod(accessor.getGetMethodName(), new String[]{});
MetadataMethod setMethod = getJavaClass().getMethod(accessor.getSetMethodName(), Arrays.asList(getMethod.getReturnType()));
getMethod.setSetMethod(setMethod);
return getMethod;
} else {
MetadataMethod method = getJavaClass().getMethodForPropertyName(accessor.getName());
if (method == null) {
throw ValidationException.invalidPropertyForClass(accessor.getName(), getJavaClass());
} else {
// True will force an exception to be thrown if it is not a
// valid method. However, if it is a transient accessor, don't
// validate it and return.
if (accessor.isTransient() || method.isValidPersistenceMethod(this, true)) {
return method;
}
return null;
}
}
}
/**
* INTERNAL:
* This method should only be called when using virtual access and
* presumably for dynamic persistence. No method validation is done and
* either the access methods specified or the default get and set methods
* for name access will be used.
*/
protected MetadataMethod getAccessibleVirtualMethod(MappingAccessor accessor) {
// If the mapping accessor does not have access methods specified,
// set the default access methods.
if (! accessor.hasAccessMethods()) {
accessor.setAccessMethods(getDescriptor().getDefaultAccessMethods());
}
MetadataMethod getMethod = new MetadataMethod(getMetadataFactory(), getJavaClass());
MetadataMethod setMethod = new MetadataMethod(getMetadataFactory(), getJavaClass());
// Set the set method on the getMethod and return it.
getMethod.setSetMethod(setMethod);
// Make sure we set the attribute name on the getMethod.
getMethod.setAttributeName(accessor.getName());
// Set the get and set method names.
getMethod.setName(accessor.getGetMethodName());
setMethod.setName(accessor.getSetMethodName());
return getMethod;
}
/**
* INTERNAL:
* Return the access type of this accessor. Assumes all access processing
* has been performed before calling this method.
*/
public String getAccessType() {
if (hasAccess()) {
return getAccess();
} else {
return getDescriptor().getDefaultAccess();
}
}
/**
* INTERNAL:
* Return the annotation if it exists.
*/
@Override
protected MetadataAnnotation getAnnotation(String annotation) {
return getAccessibleObject().getAnnotation(annotation, this);
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<AssociationOverrideMetadata> getAssociationOverrides() {
return m_associationOverrides;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<AttributeOverrideMetadata> getAttributeOverrides() {
return m_attributeOverrides;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public XMLAttributes getAttributes() {
return m_attributes;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public ChangeTrackingMetadata getChangeTracking() {
return m_changeTracking;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getClassName() {
return m_className;
}
/**
* INTERNAL:
*/
public CopyPolicyMetadata getCopyPolicy(){
if (m_cloneCopyPolicy != null){
return m_cloneCopyPolicy;
} else if (m_instantiationCopyPolicy != null){
return m_instantiationCopyPolicy;
} else {
return m_customCopyPolicy;
}
}
/**
* INTERNAL:
* Used for OX mapping
*/
public CloneCopyPolicyMetadata getCloneCopyPolicy(){
return m_cloneCopyPolicy;
}
/**
* INTERNAL:
* Used for OX mapping
*/
public CustomCopyPolicyMetadata getCustomCopyPolicy(){
return m_customCopyPolicy;
}
/**
* INTERNAL:
*/
public MetadataClass getCustomizerClass() {
return m_customizerClass;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getCustomizerClassName() {
return m_customizerClassName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getDescription() {
return m_description;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public Boolean getExcludeDefaultMappings() {
return m_excludeDefaultMappings;
}
/**
* INTERNAL:
* To satisfy the abstract getIdentifier() method from ORMetadata.
*/
@Override
public String getIdentifier() {
return getJavaClassName();
}
/**
* INTERNAL:
* Used for OX mapping
*/
public InstantiationCopyPolicyMetadata getInstantiationCopyPolicy(){
return m_instantiationCopyPolicy;
}
/**
* INTERNAL:
* Return the java class that defines this accessor. It may be an
* entity, embeddable or mapped superclass.
*/
@Override
public MetadataClass getJavaClass() {
return (MetadataClass) getAnnotatedElement();
}
/**
* INTERNAL:
* Return the java class name that defines this accessor. It may be an
* entity, embeddable or mapped superclass.
*/
@Override
public String getJavaClassName() {
return getJavaClass().getName();
}
/**
* INTERNAL:
* Return the mapped superclasses associated with this entity accessor.
* A call to discoverMappedSuperclassesAndInheritanceParents() should be
* made before calling this method.
* @see preProcess()
*/
public List<MappedSuperclassAccessor> getMappedSuperclasses() {
return m_mappedSuperclasses;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public Boolean getMetadataComplete() {
return m_metadataComplete;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public NoSqlMetadata getNoSql() {
return m_noSql;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setNoSql(NoSqlMetadata noSql) {
this.m_noSql = noSql;
}
/**
* INTERNAL:
* In most cases the owning descriptor is the descriptor associated with
* this class accessor. Owning descriptors come into play when dealing
* with embeddable classes and their accessors. Processing certain accessors
* from an embeddable class requires knowledge of owning descriptors that
* require metadata settings from processing the embeddable metadata.
* @see EmbeddableAccessor
*/
public MetadataDescriptor getOwningDescriptor() {
return getDescriptor();
}
/**
* INTERNAL:
* In most cases the owning descriptors is the single descriptor associated
* with this class accessor. Owning descriptors come into play when dealing
* with shared embeddable classes (included nested) and their accessors.
* Processing certain accessors from an embeddable class requires knowledge
* of owning descriptors that require metadata settings from processing the
* embeddable metadata.
* @see EmbeddableAccessor
*/
public List<MetadataDescriptor> getOwningDescriptors() {
if (m_owningDescriptors.isEmpty() && ! isEmbeddableAccessor()) {
m_owningDescriptors.add(getDescriptor());
}
return m_owningDescriptors;
}
/**
* INTERNAL:
*/
protected MetadataClass getParentClass() {
return m_parentClass;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getParentClassName() {
return m_parentClassName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<PLSQLRecordMetadata> getPLSQLRecords() {
return m_plsqlRecords;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<PLSQLTableMetadata> getPLSQLTables() {
return m_plsqlTables;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public StructMetadata getStruct() {
return m_struct;
}
/**
* INTERNAL:
*/
public boolean hasDerivedId() {
return ! getDescriptor().getDerivedIdAccessors().isEmpty();
}
/**
* INTERNAL:
*/
protected boolean hasParentClass() {
return m_parentClass != null && ! m_parentClass.isClass(void.class);
}
/**
* INTERNAL:
* Return whether this ClassAccessor is a MappedSuperclassAccessor
*/
public boolean isMappedSuperclass() {
return false;
}
/**
* INTERNAL:
*/
public boolean isMetadataComplete() {
return m_metadataComplete != null && m_metadataComplete;
}
/**
* INTERNAL:
* Return true if this accessor has been pre-processed.
*/
public boolean isPreProcessed() {
return m_isPreProcessed;
}
/**
* INTERNAL:
* Return true if this accessor has been processed.
*/
@Override
public boolean isProcessed() {
return m_isProcessed;
}
/**
* INTERNAL:
* Return true if this class accessor has been set to metadata complete.
*/
public boolean ignoreAnnotations() {
if (getProject().isXMLMappingMetadataComplete()) {
return true;
} else {
return isMetadataComplete();
}
}
/**
* INTERNAL:
* This method should be subclassed in those methods that need to do
* extra initialization.
*/
public void initXMLClassAccessor(MetadataAccessibleObject accessibleObject, MetadataDescriptor descriptor, MetadataProject project, XMLEntityMappings entityMappings) {
initXMLAccessor(descriptor, project);
initXMLObject(accessibleObject, entityMappings);
// Since the the descriptor, project and accessible object are all
// available at this point, it is now safe to initialize our access
// type.
initAccess();
}
/**
* INTERNAL:
*/
@Override
public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) {
super.initXMLObject(accessibleObject, entityMappings);
// Initialize single objects.
initXMLObject(m_changeTracking, accessibleObject);
initXMLObject(m_cloneCopyPolicy, accessibleObject);
initXMLObject(m_customCopyPolicy, accessibleObject);
initXMLObject(m_instantiationCopyPolicy, accessibleObject);
initXMLObject(m_attributes, accessibleObject);
initXMLObject(m_struct, accessibleObject);
initXMLObject(m_noSql, accessibleObject);
// Initialize lists of objects.
initXMLObjects(m_associationOverrides, accessibleObject);
initXMLObjects(m_attributeOverrides, accessibleObject);
initXMLObjects(m_oracleObjectTypes, accessibleObject);
initXMLObjects(m_oracleArrayTypes, accessibleObject);
initXMLObjects(m_plsqlRecords, accessibleObject);
initXMLObjects(m_plsqlTables, accessibleObject);
// Initialize simple class objects.
m_customizerClass = initXMLClassName(m_customizerClassName);
m_parentClass = initXMLClassName(m_parentClassName);
}
/**
* INTERNAL:
* Indicates whether the specified annotation is present on the annotated
* element for this accessor. Method checks against the metadata complete
* flag.
*/
@Override
public boolean isAnnotationPresent(String annotation) {
return getAccessibleObject().isAnnotationPresent(annotation, this);
}
/**
* INTERNAL:
* Return true if this accessor represents a class.
*/
public boolean isClassAccessor() {
return true;
}
/**
* INTERNAL:
* Return true if this accessor represents an embeddable class.
*/
public boolean isEmbeddableAccessor() {
return false;
}
/**
* INTERNAL:
* Return true if this accessor represents an entity class.
*/
public boolean isEntityAccessor() {
return false;
}
/**
* INTERNAL:
* Generic class level merging details for entities, mapped superclasses
* and embeddables.
*/
@Override
public void merge(ORMetadata metadata) {
super.merge(metadata);
ClassAccessor accessor = (ClassAccessor) metadata;
// Simple object merging.
m_customizerClass = (MetadataClass) mergeSimpleObjects(m_customizerClass, accessor.getCustomizerClass(), accessor, "<customizer>");
m_customizerClassName = (String) mergeSimpleObjects(m_customizerClassName, accessor.getCustomizerClassName(), accessor, "<customizer>");
m_parentClass = (MetadataClass) mergeSimpleObjects(m_parentClass, accessor.getParentClass(), accessor, "<parent-class>");
m_parentClassName = (String) mergeSimpleObjects(m_parentClassName, accessor.getParentClassName(), accessor, "<parent-class>");
m_description = (String) mergeSimpleObjects(m_description, accessor.getDescription(), accessor, "<description>");
m_metadataComplete = (Boolean) mergeSimpleObjects(m_metadataComplete, accessor.getMetadataComplete(), accessor, "@metadata-complete");
m_excludeDefaultMappings = (Boolean) mergeSimpleObjects(m_excludeDefaultMappings, accessor.getExcludeDefaultMappings(), accessor, "@exclude-default-mappings");
// ORMetadata object merging.
m_cloneCopyPolicy = (CloneCopyPolicyMetadata) mergeORObjects(m_cloneCopyPolicy, accessor.getCloneCopyPolicy());
m_customCopyPolicy = (CustomCopyPolicyMetadata) mergeORObjects(m_customCopyPolicy, accessor.getCustomCopyPolicy());
m_instantiationCopyPolicy = (InstantiationCopyPolicyMetadata) mergeORObjects(m_instantiationCopyPolicy, accessor.getInstantiationCopyPolicy());
m_changeTracking = (ChangeTrackingMetadata) mergeORObjects(m_changeTracking, accessor.getChangeTracking());
m_struct = (StructMetadata) mergeORObjects(m_struct, accessor.getStruct());
m_noSql = (NoSqlMetadata) mergeORObjects(m_noSql, accessor.getNoSql());
// ORMetadata list merging.
m_associationOverrides = mergeORObjectLists(m_associationOverrides, accessor.getAssociationOverrides());
m_attributeOverrides = mergeORObjectLists(m_attributeOverrides, accessor.getAttributeOverrides());
m_oracleObjectTypes = mergeORObjectLists(m_oracleObjectTypes, accessor.getOracleObjectTypes());
m_oracleArrayTypes = mergeORObjectLists(m_oracleArrayTypes, accessor.getOracleArrayTypes());
m_plsqlRecords = mergeORObjectLists(m_plsqlRecords, accessor.getPLSQLRecords());
m_plsqlTables = mergeORObjectLists(m_plsqlTables, accessor.getPLSQLTables());
// ORObjects that merge further ...
if (m_attributes == null) {
m_attributes = accessor.getAttributes();
} else {
m_attributes.merge(accessor.getAttributes());
}
}
/**
* INTERNAL:
* The pre-process method is called during regular deployment and metadata
* processing.
*/
public void preProcess() {
// Process the global converters.
processConverters();
// Add the accessors (won't be processed till process).
addAccessors();
// Pre-process our list of mapped superclass accessors now.
for (MappedSuperclassAccessor mappedSuperclass : getMappedSuperclasses()) {
preProcessMappedSuperclassMetadata(mappedSuperclass);
}
// Mark the class accessor as pre-processed.
setIsPreProcessed();
}
/**
* INTERNAL:
* The pre-process for canonical model method is called (and only called)
* during the canonical model generation. The use of this pre-process allows
* us to remove some items from the regular pre-process that do not apply
* to the canonical model generation.
*/
public void preProcessForCanonicalModel() {
// Process the correct access type before any other processing.
processAccessType();
// Add the accessors and converters on this embeddable.
addAccessors();
// Mark the class accessor as pre-processed.
setIsPreProcessed();
}
/**
* INTERNAL:
* Sub classes that support extending mapped superclasses should override
* this method to control what is pre-processed from a mapped superclass.
* By default it does full pre-processing.
*
* @see EmbeddableAccessor
*/
protected void preProcessMappedSuperclassMetadata(MappedSuperclassAccessor mappedSuperclass) {
mappedSuperclass.preProcess();
}
/**
* INTERNAL:
* This method should be overridden by all class accessors to process their
* specific class metadata first then call up to this method to process the
* common metadata.
*/
@Override
public void process() {
// Process the attribute override metadata.
processAttributeOverrides();
// Process the association override metadata.
processAssociationOverrides();
// Process the change tracking metadata.
processChangeTracking();
// Process the customizer metadata.
processCustomizer();
// Process the copy policy metadata.
processCopyPolicy();
// Process the partitioning metadata.
processPartitioning();
// Process the property metadata.
processProperties();
// Process the PLSQL type metadata.
processComplexMetadataTypes();
// Process the MappedSuperclass(es) metadata now after all our. There
// may be several MappedSuperclasses for any given Entity or Embeddable.
for (MappedSuperclassAccessor mappedSuperclass : getMappedSuperclasses()) {
processMappedSuperclassMetadata(mappedSuperclass);
}
// Mark the class accessor as processed.
setIsProcessed();
}
/**
* INTERNAL:
*/
protected abstract void processAccessType();
/**
* INTERNAL:
* Process the association override metadata specified on an entity or
* mapped superclass. For any given class, XML association overrides are
* always added first (see processAssociationOverrides()).
*/
protected void processAssociationOverride(AssociationOverrideMetadata associationOverride) {
// If an association override already exists, need to make some checks
// to determine if we should throw an exception or log an ignore
// message.
if (associationOverride.shouldOverride(getDescriptor().getAssociationOverrideFor(associationOverride.getName()), getLogger(), getDescriptor().getJavaClassName())) {
getDescriptor().addAssociationOverride(associationOverride);
}
}
/**
* INTERNAL:
* Process the association override metadata specified on an entity or
* mapped superclass. Once the association overrides are processed from
* XML process the association overrides from annotations. This order of
* processing must be maintained.
*/
protected void processAssociationOverrides() {
// Process the XML association override elements first.
for (AssociationOverrideMetadata associationOverride : m_associationOverrides) {
// Process the association override.
processAssociationOverride(associationOverride);
}
// Process the association override annotations.
// Look for an @AssociationOverrides.
MetadataAnnotation associationOverrides = getAnnotation(JPA_ASSOCIATION_OVERRIDES);
if (associationOverrides != null) {
for (Object associationOverride : associationOverrides.getAttributeArray("value")) {
processAssociationOverride(new AssociationOverrideMetadata((MetadataAnnotation) associationOverride, this));
}
}
// Look for an @AssociationOverride.
MetadataAnnotation associationOverride = getAnnotation(JPA_ASSOCIATION_OVERRIDE);
if (associationOverride != null) {
processAssociationOverride(new AssociationOverrideMetadata(associationOverride, this));
}
}
/**
* INTERNAL:
* Process the attribute override metadata specified on an entity or
* mapped superclass. For any given class, XML attribute overrides are
* always added first (see processAttributeOverrides()).
*/
protected void processAttributeOverride(AttributeOverrideMetadata attributeOverride) {
// If an attribute override already exists, need to make some checks
// to determine if we should throw an exception or log an ignore
// message.
if (attributeOverride.shouldOverride(getDescriptor().getAttributeOverrideFor(attributeOverride.getName()), getLogger(), getDescriptor().getJavaClassName())) {
getDescriptor().addAttributeOverride(attributeOverride);
}
}
/**
* INTERNAL:
* Process the attribute override metadata specified on an entity or
* mapped superclass. Once the attribute overrides are processed from
* XML process the attribute overrides from annotations. This order of
* processing must be maintained.
*/
protected void processAttributeOverrides() {
// Process the XML attribute overrides first.
for (AttributeOverrideMetadata attributeOverride : m_attributeOverrides) {
// Process the attribute override.
processAttributeOverride(attributeOverride);
}
// Process the attribute override annotations.
// Look for an @AttributeOverrides.
MetadataAnnotation attributeOverrides = getAnnotation(JPA_ATTRIBUTE_OVERRIDES);
if (attributeOverrides != null) {
for (Object attributeOverride : attributeOverrides.getAttributeArray("value")) {
processAttributeOverride(new AttributeOverrideMetadata((MetadataAnnotation) attributeOverride, this));
}
}
// Look for an @AttributeOverride.
MetadataAnnotation attributeOverride = getAnnotation(JPA_ATTRIBUTE_OVERRIDE);
if (attributeOverride != null) {
processAttributeOverride(new AttributeOverrideMetadata(attributeOverride, this));
}
}
/**
* INTERNAL:
* Process the change tracking setting for this accessor.
*/
protected void processChangeTracking() {
MetadataAnnotation changeTracking = getAnnotation(ChangeTracking.class);
if (m_changeTracking != null || changeTracking != null) {
if (getDescriptor().hasChangeTracking()) {
// We must be processing a mapped superclass setting for an
// entity that has its own change tracking setting. Ignore it
// and log a warning.
getLogger().logConfigMessage(MetadataLogger.IGNORE_MAPPED_SUPERCLASS_CHANGE_TRACKING, getDescriptor().getJavaClass(), getJavaClass());
} else {
if (m_changeTracking == null) {
new ChangeTrackingMetadata(changeTracking, this).process(getDescriptor());
} else {
if (changeTracking != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, changeTracking, getJavaClassName(), getLocation());
}
m_changeTracking.process(getDescriptor());
}
}
}
}
/**
* Process PL/SQL record and table types, Oracle object array and XMLType types.
*/
public void processComplexMetadataTypes() {
// PLSQL types.
// Process the XML first.
for (PLSQLRecordMetadata record : m_plsqlRecords) {
getProject().addComplexMetadataType(record);
}
// Process the annotations.
MetadataAnnotation records = getAnnotation(PLSQLRecords.class);
if (records != null) {
for (Object record : records.getAttributeArray("value")) {
getProject().addComplexMetadataType(new PLSQLRecordMetadata((MetadataAnnotation) record, this));
}
}
MetadataAnnotation record = getAnnotation(PLSQLRecord.class);
if (record != null) {
getProject().addComplexMetadataType(new PLSQLRecordMetadata(record, this));
}
// Process the XML first.
for (PLSQLTableMetadata table : m_plsqlTables) {
getProject().addComplexMetadataType(table);
}
// Process the annotations.
MetadataAnnotation tables = getAnnotation(PLSQLTables.class);
if (tables != null) {
for (Object table : tables.getAttributeArray("value")) {
getProject().addComplexMetadataType(new PLSQLTableMetadata((MetadataAnnotation) table, this));
}
}
MetadataAnnotation table = getAnnotation(PLSQLTable.class);
if (table != null) {
getProject().addComplexMetadataType(new PLSQLTableMetadata(table, this));
}
// Oracle advanced JDBC types.
// Process XML.
for (OracleObjectTypeMetadata objectType : m_oracleObjectTypes) {
getProject().addComplexMetadataType(objectType);
}
// Process the annotations.
MetadataAnnotation objectTypes = getAnnotation(OracleObjects.class);
if (objectTypes != null) {
for (Object objectType : objectTypes.getAttributeArray("value")) {
getProject().addComplexMetadataType(new OracleObjectTypeMetadata((MetadataAnnotation) objectType, this));
}
}
MetadataAnnotation objectType = getAnnotation(OracleObject.class);
if (objectType != null) {
getProject().addComplexMetadataType(new OracleObjectTypeMetadata(objectType, this));
}
// Process XML.
for (OracleArrayTypeMetadata arrayType : m_oracleArrayTypes) {
getProject().addComplexMetadataType(arrayType);
}
// Process the annotations.
MetadataAnnotation arrayTypes = getAnnotation(OracleArrays.class);
if (arrayTypes != null) {
for (Object arrayType : arrayTypes.getAttributeArray("value")) {
getProject().addComplexMetadataType(new OracleArrayTypeMetadata((MetadataAnnotation) arrayType, this));
}
}
MetadataAnnotation arrayType = getAnnotation(OracleArray.class);
if (arrayType != null) {
getProject().addComplexMetadataType(new OracleArrayTypeMetadata(arrayType, this));
}
}
/**
* INTERNAL:
*/
protected void processCopyPolicy(){
MetadataAnnotation copyPolicy = getAnnotation(CopyPolicy.class);
MetadataAnnotation instantiationCopyPolicy = getAnnotation(InstantiationCopyPolicy.class);
MetadataAnnotation cloneCopyPolicy = getAnnotation(CloneCopyPolicy.class);
if (getCopyPolicy() != null || copyPolicy != null || instantiationCopyPolicy != null || cloneCopyPolicy != null) {
if (getDescriptor().hasCopyPolicy()){
// We must be processing a mapped superclass ...
getLogger().logConfigMessage(MetadataLogger.IGNORE_MAPPED_SUPERCLASS_COPY_POLICY, getDescriptor().getJavaClass(), getJavaClass());
}
if (getCopyPolicy() == null) {
// Look at the annotations.
if (copyPolicy != null) {
if (instantiationCopyPolicy != null || cloneCopyPolicy != null) {
throw ValidationException.multipleCopyPolicyAnnotationsOnSameClass(getJavaClassName());
}
new CustomCopyPolicyMetadata(copyPolicy, this).process(getDescriptor());
}
if (instantiationCopyPolicy != null){
if (cloneCopyPolicy != null) {
throw ValidationException.multipleCopyPolicyAnnotationsOnSameClass(getJavaClassName());
}
new InstantiationCopyPolicyMetadata(instantiationCopyPolicy, this).process(getDescriptor());
}
if (cloneCopyPolicy != null){
new CloneCopyPolicyMetadata(cloneCopyPolicy, this).process(getDescriptor());
}
} else {
// We have a copy policy specified in XML.
if (copyPolicy != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, copyPolicy, getJavaClassName(), getLocation());
}
if (instantiationCopyPolicy != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, instantiationCopyPolicy, getJavaClassName(), getLocation());
}
if (cloneCopyPolicy != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, cloneCopyPolicy, getJavaClassName(), getLocation());
}
getCopyPolicy().process(getDescriptor());
}
}
}
/**
* INTERNAL:
*/
protected void processCustomizer() {
MetadataAnnotation customizer = getAnnotation(Customizer.class);
if ((m_customizerClass != null && ! m_customizerClass.isClass(void.class)) || customizer != null) {
if (getDescriptor().hasCustomizer()) {
// We must be processing a mapped superclass and its subclass
// override the customizer class, that is, defined its own. Log
// a warning that we are ignoring the Customizer metadata on the
// mapped superclass for the descriptor's java class.
getLogger().logConfigMessage(MetadataLogger.IGNORE_MAPPED_SUPERCLASS_CUSTOMIZER, getDescriptor().getJavaClass(), getJavaClass());
} else {
if (m_customizerClass == null || m_customizerClass.isClass(void.class)) {
// Use the annotation value.
m_customizerClass = getMetadataClass(customizer.getAttributeString("value"));
} else {
// Use the xml value and log a message if necessary.
if (customizer != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, customizer, getJavaClassName(), getLocation());
}
}
getDescriptor().getClassDescriptor().setDescriptorCustomizerClassName(m_customizerClass.getName());
getDescriptor().setHasCustomizer();
getProject().addAccessorWithCustomizer(this);
}
}
}
/**
* INTERNAL:
* Allows for processing derived ids, either from an Id or MapsId
* specification. All referenced accessors are processed first to ensure
* the necessary fields are set before the derived id is processed and
* circular references are checked.
*/
public void processDerivedId(HashSet<ClassAccessor> processing, HashSet<ClassAccessor> processed) {
// Process only if we haven't already done so (inheritance case)
if (! processed.contains(this)) {
// If we appear in the processing list, through the chain of derived
// id we have come back to ourself. We have a circular reference,
// throw an exception.
if (processing.contains(this)) {
throw ValidationException.idRelationshipCircularReference(processing);
}
processing.add(this);
for (ObjectAccessor accessor : getDescriptor().getDerivedIdAccessors()) {
// Check the reference accessor for a derived id and fast
// track its processing if need be.
MetadataDescriptor referenceDescriptor = accessor.getReferenceDescriptor();
ClassAccessor referenceAccessor = referenceDescriptor.getClassAccessor();
if (referenceAccessor.hasDerivedId()) {
referenceAccessor.processDerivedId(processing, processed);
}
// Now process the relationship, and the derived id.
if (! accessor.isProcessed()) {
accessor.process();
}
}
// Once we're done, we'll move ourselves from the processing to
// processed step.
processing.remove(this);
processed.add(this);
}
}
/**
* INTERNAL
* Sub classes that support extending mapped superclasses should override
* this method to control what is processed from a mapped superclass. By
* default it does full processing.
*
* @see EmbeddableAccessor
*/
protected void processMappedSuperclassMetadata(MappedSuperclassAccessor mappedSuperclass) {
mappedSuperclass.process();
}
/**
* INTERNAL:
* Process the accessors for the given class.
*/
public void processMappingAccessors() {
// Now tell the descriptor to process its accessors.
getDescriptor().processMappingAccessors();
}
/**
* INTERNAL:
* Check for and process a NoSql annotation and configure the correct
* descriptor type.
*
* NOTE: NoSql metadata is supported only on Entity and Embeddable.
*/
protected void processNoSql() {
MetadataAnnotation noSql = getAnnotation("org.eclipse.persistence.nosql.annotations.NoSql");
if (m_noSql != null || noSql != null) {
if (m_noSql == null) {
new NoSqlMetadata(noSql, this).process(getDescriptor());
} else {
if (noSql != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, noSql, getJavaClassName(), getLocation());
}
m_noSql.process(getDescriptor());
}
}
}
/**
* INTERNAL:
* If the user specified a parent class set it on the metadata class
* for this accessor. The parent class is only ever required in a VIRTUAL
* case when no java class file is available (otherwise we look at the
* class for the parent).
*/
public void processParentClass() {
if (hasParentClass()) {
// Set the class the user specified.
getJavaClass().setSuperclass(getParentClass());
} else if (getJavaClass().getSuperclass() == null) {
// Default the superclass to Object.class if no superclass exists.
getJavaClass().setSuperclass(getMetadataClass(Object.class));
}
}
/**
* INTERNAL:
* Adds properties to the descriptor.
*/
protected void processProperties() {
// Add the XML properties first.
for (PropertyMetadata property : getProperties()) {
getDescriptor().addProperty(property);
}
// Now add the properties defined in annotations.
if (isAnnotationPresent(Properties.class)) {
for (Object property : getAnnotation(Properties.class).getAttributeArray("value")) {
getDescriptor().addProperty(new PropertyMetadata((MetadataAnnotation) property, this));
}
}
if (isAnnotationPresent(Property.class)) {
getDescriptor().addProperty(new PropertyMetadata(getAnnotation(Property.class), this));
}
}
/**
* Check for and process a Struct annotation and configure the correct
* descriptor type.
*
* NOTE: Struct metadata is supported only on Entity and Embeddable.
*/
protected void processStruct() {
MetadataAnnotation struct = getAnnotation(Struct.class);
if (m_struct != null || struct != null) {
if (m_struct == null) {
new StructMetadata(struct, this).process(getDescriptor());
} else {
if (struct != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, struct, getJavaClassName(), getLocation());
}
m_struct.process(getDescriptor());
}
}
}
/**
* INTERNAL:
* If this class accessor uses VIRTUAL access and is not accessible, add it
* to our list of virtual classes that will be dynamically created.
*/
protected void processVirtualClass() {
if (usesVirtualAccess() && ! getJavaClass().isAccessible()) {
getProject().addVirtualClass(this);
// In a dynamic configuration, descriptors are dealt/referenced
// using an alias. JPA does not provide a way of specifying an alias
// for embeddable descriptors and defaulting them in a JPA
// configuration could lead to clashes for similarly named
// embeddables across packages. So for dynamic use cases, this is
// currently a limitation, that is, embedabbles must be uniquely
// named across the persistence unit.
if (isEmbeddableAccessor()) {
// Add an alias for this embeddable descriptor.
getProject().addAlias(Helper.getShortClassName(getJavaClassName()), getDescriptor());
}
}
}
/**
* INTERNAL:
* This method resolves generic types. Resolving generic types will be the
* responsibility of the metadata factory since each factory could have its
* own means to do so and not respect a generic format on the metadata
* objects.
*/
protected void resolveGenericTypes(List<String> genericTypes, MetadataClass parent) {
getMetadataFactory().resolveGenericTypes(getJavaClass(), genericTypes, parent, getDescriptor());
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setAssociationOverrides(List<AssociationOverrideMetadata> associationOverrides) {
m_associationOverrides = associationOverrides;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setAttributeOverrides(List<AttributeOverrideMetadata> attributeOverrides) {
m_attributeOverrides = attributeOverrides;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setAttributes(XMLAttributes attributes) {
m_attributes = attributes;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setChangeTracking(ChangeTrackingMetadata changeTracking) {
m_changeTracking = changeTracking;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setClassName(String className) {
m_className = className;
}
/**
* INTERNAL:
* set the copy policy metadata
*/
public void setCloneCopyPolicy(CloneCopyPolicyMetadata copyPolicy){
m_cloneCopyPolicy = copyPolicy;
}
/**
* INTERNAL:
* set the copy policy metadata
*/
public void setCustomCopyPolicy(CustomCopyPolicyMetadata copyPolicy){
m_customCopyPolicy = copyPolicy;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setCustomizerClassName(String customizerClassName) {
m_customizerClassName = customizerClassName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setDescription(String description) {
m_description = description;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setExcludeDefaultMappings(Boolean excludeDefaultMappings) {
m_excludeDefaultMappings = excludeDefaultMappings;
}
/**
* INTERNAL:
* set the copy policy metadata
*/
public void setInstantiationCopyPolicy(InstantiationCopyPolicyMetadata copyPolicy){
m_instantiationCopyPolicy = copyPolicy;
}
/**
* INTERNAL:
*/
protected void setIsPreProcessed() {
m_isPreProcessed = true;
}
/**
* INTERNAL:
*/
protected void setIsProcessed() {
m_isProcessed = true;
}
/**
* INTERNAL:
* Set the java class for this accessor. This is currently called after
* the class loader has changed and we are adding entity listeners.
*/
public void setJavaClass(MetadataClass cls) {
setAccessibleObject(cls);
getDescriptor().setJavaClass(cls);
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setMetadataComplete(Boolean metadataComplete) {
m_metadataComplete = metadataComplete;
}
/**
* INTERNAL:
*/
protected void setParentClass(MetadataClass parentClass) {
m_parentClass = parentClass;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setParentClassName(String parentClassName) {
m_parentClassName = parentClassName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setPLSQLRecords(List<PLSQLRecordMetadata> records) {
m_plsqlRecords = records;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setPLSQLTables(List<PLSQLTableMetadata> tables) {
m_plsqlTables = tables;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setStruct(StructMetadata struct) {
m_struct = struct;
}
/**
* INTERNAL:
*/
@Override
public String toString() {
return getJavaClassName();
}
/**
* INTERNAL:
* Returns true if this class uses field access. It will first check for
* an explicit access type specification, otherwise will use the default
* access as specified on the descriptor for this accessor since we may be
* processing a mapped superclass.
*/
public boolean usesFieldAccess() {
return getAccessType().equals(JPA_ACCESS_FIELD);
}
/**
* INTERNAL:
* Returns true if this class uses property access. It will first check for
* an explicit access type specification, otherwise will use the default
* access as specified on the descriptor for this accessor since we may be
* processing a mapped superclass.
*/
public boolean usesPropertyAccess() {
return getAccessType().equals(JPA_ACCESS_PROPERTY);
}
/**
* INTERNAL:
* Returns true if this class uses virtual access. It will first check for
* an explicit access type specification, otherwise will use the default
* access as specified on the descriptor for this accessor since we may be
* processing a mapped superclass.
*/
public boolean usesVirtualAccess() {
return getAccessType().equals(EL_ACCESS_VIRTUAL);
}
/**
* Returns the list of OracleObjectType instances.
*/
public List<OracleObjectTypeMetadata> getOracleObjectTypes() {
return m_oracleObjectTypes;
}
/**
* Sets the list of OracleObjectType instances.
*/
public void setOracleObjectTypes(List<OracleObjectTypeMetadata> oracleObjectTypes) {
m_oracleObjectTypes = oracleObjectTypes;
}
/**
* Returns the list of OracleArrayType instances.
*/
public List<OracleArrayTypeMetadata> getOracleArrayTypes() {
return m_oracleArrayTypes;
}
/**
* Sets the list of OracleArrayType instances.
*/
public void setOracleArrayTypes(List<OracleArrayTypeMetadata> oracleArrayTypes) {
m_oracleArrayTypes = oracleArrayTypes;
}
}