/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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.equals(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(new String[]{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.equals(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. */ 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.equals(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.equals(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; } }