blob: 2453cfe2d99390a8552747577bc8ca3caaadcd06 [file] [log] [blame]
/*
* Copyright (c) 2011, 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:
// 03/19/2009-2.0 dclarke - initial API start
// 06/30/2009-2.0 mobrien - finish JPA Metadata API modifications in support
// of the Metamodel implementation for EclipseLink 2.0 release involving
// Map, ElementCollection and Embeddable types on MappedSuperclass descriptors
// - 266912: JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
// 07/06/2009-2.0 mobrien - 266912: Introduce IdentifiableTypeImpl between ManagedTypeImpl
// - EntityTypeImpl now inherits from IdentifiableTypeImpl instead of ManagedTypeImpl
// - MappedSuperclassTypeImpl now inherits from IdentifiableTypeImpl instead
// of implementing IdentifiableType indirectly
// - implement Set<SingularAttribute<? super X, ?>> getSingularAttributes()
// 07/09/2009-2.0 mobrien - 266912: implement get//Attribute() functionality
// - functions throw 2 types of IllegalArgumentExceptions depending on whether
// the member is missing or is the wrong type - see design issue #41
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_41:_When_to_throw_IAE_for_missing_member_or_wrong_type_on_get.28.29_call
// 07/14/2009-2.0 mobrien - 266912: implement getDeclared//() functionality
// - Implement 14 functions for ManagedType - see design issue #43
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_43:_20090710:_Implement_getDeclaredX.28.29_methods
// 07/28/2009-2.0 mobrien - 284877: implement recursive functionality for hasDeclaredAttribute()
// - see design issue #52
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_52:_20090728:_JPA_2:_Implement_recursive_ManagedType.getDeclared.2A_algorithm_to_differentiate_by_IdentifiableType
// 08/08/2009-2.0 mobrien - 266912: implement Collection and List separation during attribute initialization
// - see design issue #58
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_58:_20090807:_ManagedType_Attribute_Initialization_must_differentiate_between_Collection_and_List
// 08/17/2009-2.0 mobrien - 284877: The base case for the recursive function
// managedTypeImpl.hasDeclaredAttribute() does not handle use case 1.4 (root-level managedType)
// when the caller of the function does not do it's own inheritedType check.
// - see design issue #52
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI:52_Refactor:_20090817
// 08/19/2009-2.0 mobrien - 266912: Handle MappedSuperclass in ManagedTypeImpl.create()
// - see design issue #39 (partial)
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_39:_20090708:_Handle_MappedSuperclass_in_ManagedTypeImpl.create.28.29
// 08/19/2009-2.0 mobrien - 266912: ManagedType.getDeclaredX() leaks members into entity-entity hierarchy
// - see design issue #61
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_61:_20090820:_ManagedType.getDeclaredX.28.29_leaks_members_into_entity-entity_hierarchy
// 06/01/2010-2.1 mobrien - 315287: Handle BasicType as inheritance root for ManagedTypes
// - see design issue #103
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_103:_20100601:_315287:_Handle_BasicType_as_inheritance_root_for_ManagedTypes
// 09/09/2010-2.2 mobrien - 322166: If attribute is defined on this current ManagedType (and not on a superclass)
// - do not attempt a reflective call on a superclass
// - see design issue #25
// http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_25:_20090616:_Inherited_parameterized_generics_for_Element_Collections_.28Basic.29
//
package org.eclipse.persistence.internal.jpa.metamodel;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.CollectionAttribute;
import jakarta.persistence.metamodel.ListAttribute;
import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.persistence.metamodel.PluralAttribute;
import jakarta.persistence.metamodel.PluralAttribute.CollectionType;
import jakarta.persistence.metamodel.SetAttribute;
import jakarta.persistence.metamodel.SingularAttribute;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import org.eclipse.persistence.internal.dynamic.ValuesAccessor;
import org.eclipse.persistence.internal.helper.BasicTypeHelperImpl;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredField;
import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredMethod;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.CollectionMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
/**
* <p>
* <b>Purpose</b>: Provides the implementation for the ManagedType interface
* of the JPA 2.0 Metamodel API (part of the JSR-317 EJB 3.1 Criteria API)
* <p>
* <b>Description</b>:
* Instances of the type ManagedType represent entities, mapped superclasses
* and embeddable types.
*
* @see jakarta.persistence.metamodel.ManagedType
*
* @since EclipseLink 1.2 - JPA 2.0
* @param <X> The represented type.
*/
public abstract class ManagedTypeImpl<X> extends TypeImpl<X> implements ManagedType<X> {
/** Native RelationalDescriptor that contains all the mappings of this type **/
protected ClassDescriptor descriptor;
/** The map of attributes keyed on attribute string name **/
private Map<String, Attribute<X,?>> members;
/** Reference to the metamodel that this managed type belongs to **/
protected MetamodelImpl metamodel;
/**
* INTERNAL:
* This constructor will create a ManagedType but will not initialize its member mappings.
* This is accomplished by delayed initialization in MetamodelImpl.initialize()
* in order that we have access to all types when resolving relationships in mappings.
* @param metamodel - the metamodel that this managedType is associated with
* @param descriptor - the RelationalDescriptor that defines this managedType
*/
protected ManagedTypeImpl(MetamodelImpl metamodel, ClassDescriptor descriptor) {
// A valid descriptor will always have a javaClass set except in bug# 303063
super(descriptor.getJavaClass(), descriptor.getJavaClassName());
this.descriptor = descriptor;
// the metamodel field must be instantiated prior to any *AttributeImpl instantiation which will use the metamodel
this.metamodel = metamodel;
// Cache the ManagedType on the descriptor - avoid doing this, use metamodel map instead.
// descriptor.setProperty(getClass().getName(), this);
metamodel.getManagedTypesMap().put(descriptor.getJavaClassName(), this);
// Note: Full initialization of the ManagedType occurs during MetamodelImpl.initialize() after all types are instantiated
}
/**
* Return the attribute of the managed
* type that corresponds to the specified name.
* @param name the name of the represented attribute
* @return attribute with given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public Attribute<X, ?> getAttribute(String name) {
if(!members.containsKey(name)) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return members.get(name);
}
/**
* Return the attributes of the managed type.
*/
@Override
public Set<Attribute<? super X, ?>> getAttributes() {
// We return a new Set instead of directly returning the Collection of values from the members HashMap
return new LinkedHashSet<Attribute<? super X, ?>>(this.members.values());
}
/**
* Return the Collection-valued attribute of the managed type
* that corresponds to the specified name.
* @param name the name of the represented attribute
* @return CollectionAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public CollectionAttribute<? super X, ?> getCollection(String name) {
// Get the named collection from the set directly
/*
* Note: We do not perform type checking on the get(name)
* If the type is not of the correct Attribute implementation class then
* a possible CCE will be allowed to propagate to the client.
* For example if a getCollection() is performed on a ListAttribute a CCE will occur
*/
CollectionAttribute<? super X, ?> anAttribute = (CollectionAttribute<? super X, ?>)this.members.get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return anAttribute;
}
/**
* Return the Collection-valued attribute of the managed type
* that corresponds to the specified name and Java element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return CollectionAttribute of the given name and element
* type
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <E> CollectionAttribute<? super X, E> getCollection(String name, Class<E> elementType) {
// We do not use getCollection(name) so that we can catch a possible CCE on the wrong attribute type
Attribute<? super X, E> anAttribute = (Attribute<? super X, E>)this.members.get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
} else {
// Throw appropriate IAException if required
verifyAttributeTypeAndReturnType(anAttribute, elementType, CollectionType.COLLECTION);
}
return (CollectionAttribute<? super X, E>)anAttribute;
}
/**
* Return all collection-valued attributes of the managed type.
* @return collection valued attributes
*/
@Override
public Set<PluralAttribute<? super X, ?, ?>> getPluralAttributes() {
// Get all attributes and filter only for PluralAttributes
Set<Attribute<? super X, ?>> allAttributes = this.getAttributes();
// Is it better to add to a new Set or remove from an existing Set without a concurrentModificationException
Set<PluralAttribute<? super X, ?, ?>> pluralAttributes = new LinkedHashSet<PluralAttribute<? super X, ?, ?>>();
for(Attribute<? super X, ?> anAttribute : allAttributes) {
// Add only CollectionType attributes
if(anAttribute.isCollection()) {
pluralAttributes.add((PluralAttribute<? super X, ?, ?>)anAttribute);
}
}
return pluralAttributes;
}
/**
* INTERNAL:
* Return the declared attribute of the managed
* type that corresponds to the specified name.
* @param name the name of the represented attribute
* @param attributeKnownToExistOnLeafTarget true if we already know the attribute exists on the leaf
* @return attribute with given name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
protected Attribute<X, ?> getDeclaredAttribute(String name, boolean attributeKnownToExistOnLeafTarget){
// get the attribute parameterized by <Owning type, return Type> - throw an IAE if not found (no need to check hierarchy)
// Handles UC1 and UC2
Attribute<X, ?> anAttribute = getAttribute(name);
// If an Attribute is found then check the hierarchy for a declaration in the superclass(s)
// Keep moving up only when the attribute is not found
ManagedTypeImpl aManagedSuperType = getManagedSuperType();
if(null == aManagedSuperType) {
return anAttribute;
} else {
boolean isDeclaredAboveLeaf = false;
// keep checking the hierarchy but skip this level and go directly to the superType
if(attributeKnownToExistOnLeafTarget) {
isDeclaredAboveLeaf = aManagedSuperType.isAttributeDeclaredOnlyInLeafType(name, anAttribute);
} else {
isDeclaredAboveLeaf = aManagedSuperType.isAttributeDeclaredOnlyInLeafType(name);
}
// Cases 10 and 01 throw an IAE, cases 00 and 11 are normal - an Exclusive OR (EOR)
if((attributeKnownToExistOnLeafTarget && !isDeclaredAboveLeaf) || (!attributeKnownToExistOnLeafTarget && isDeclaredAboveLeaf)) {
// Handles UC4 and UC5 - throw an IAE if the class is declared above
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_declared_attribute_not_present_but_is_on_superclass",
new Object[] { name, this }));
} else {
// Handles UC3 (normal case - attribute is not declared on a superclass)
return anAttribute;
}
}
}
/**
* Return the declared attribute of the managed
* type that corresponds to the specified name.
* @param name the name of the represented attribute
* @return attribute with given name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
@Override
public Attribute<X, ?> getDeclaredAttribute(String name){
return getDeclaredAttribute(name, false);
}
/**
* All getDeclared*(name, *) function calls require navigation up the superclass tree
* in order to determine if the member name is declared on the current managedType.<p>
* If the attribute is found anywhere above on the superclass tree - then throw an IAE.
*
Use Case Partitioning:
- attribute positioning(none, current, 1st parent, Nth parent)
- attribute type (right, wrong type)
- attribute classification for current and parents (Entity, MappedSuperclass, embeddable?, Basic?)
UC1) Attribute is not found on current attribute (regardless of what is on its' superclasses)
- throw IAException
UC2) Attribute is found on current attribute but is of the wrong type
- throw IAException
UC3) Attribute is found on on current managedType Entity/MappedSuperclass
(but not found anywhere on the supertype hierarchy - declared above)
In this case we do the reverse - keep checking only when attribute is null
- return attribute
UC4) Attribute is declared on immediate superclass
- throw IAException
UC5) Attribute is declared on Nth superclass
- throw IAException
We use two functions, one public, one a private recursive function.
If the attribute is not found at the current level or above, or is of the wrong type - throw an IAException
If the attribute is found then we still need to search to the
top of the hierarchy tree to verify it is not declared above
- if it is also not found above - return the attribute in this case only
*/
/**
* Return the attributes declared by the managed type.
*/
@Override
public Set<Attribute<X, ?>> getDeclaredAttributes() {
// return only the set of attributes declared on this class - not via inheritance
// Get all attributes and filter only for declared attributes
Set<Attribute<X, ?>> allAttributes = new LinkedHashSet<Attribute<X, ?>>(this.members.values());;
// Is it better to add to a new Set or remove from an existing Set without a concurrentModificationException
Set<Attribute<X, ?>> declaredAttributes = new LinkedHashSet<Attribute<X, ?>>();
for(Attribute<X, ?> anAttribute : allAttributes) {
// Check the inheritance hierarchy for higher declarations
if(this.isAttributeDeclaredOnlyInLeafType(anAttribute.getName())) {
declaredAttributes.add(anAttribute);
}
}
return declaredAttributes;
}
/**
* Return the Collection-valued attribute declared by the
* managed type that corresponds to the specified name.
* @param name the name of the represented attribute
* @return declared CollectionAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
@Override
public CollectionAttribute<X, ?> getDeclaredCollection(String name) {
// return only a collection declared on this class - not via inheritance
// Handles UC1 and UC2
CollectionAttribute<X, ?> anAttribute = (CollectionAttribute<X, ?>) getCollection(name);
// The following verification step will throw an appropriate IAException if required (we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the Collection-valued attribute declared by the
* managed type that corresponds to the specified name and Java
* element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return declared CollectionAttribute of the given name and
* element type
* @throws IllegalArgumentException if attribute of the given
* name and type is not declared in the managed type
*/
@Override
public <E> CollectionAttribute<X, E> getDeclaredCollection(String name, Class<E> elementType) {
// return only a collection declared on this class - not via inheritance
// Handles UC1 and UC2
CollectionAttribute<X, E> anAttribute = (CollectionAttribute<X, E>) getCollection(name, elementType);
// The following verification step will throw an appropriate IAException if required (type checking has been done, and we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get, (optionally a type check) and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return all collection-valued attributes declared by the
* managed type.
* @return declared collection valued attributes
*/
@Override
public Set<PluralAttribute<X, ?, ?>> getDeclaredPluralAttributes() {
// It is evident from the fact that we have only getAttributes(), getPluralAttributes() and getSingularAttributes() that a Collection is a superset of all Set, List and even Map
// return only a set of collections declared on this class - not via inheritance
// Get all collection attribute and filter only on declared ones
Set<PluralAttribute<? super X, ?, ?>> pluralAttributes = this.getPluralAttributes();
// Is it better to add to a new Set or remove from an existing Set without a concurrentModificationException
Set<PluralAttribute<X, ?, ?>> declaredAttributes = new LinkedHashSet<PluralAttribute<X, ?, ?>>();
// The set is a copy of the underlying metamodel attribute set - we will remove all SingularAttribute(s)
for(PluralAttribute<? super X, ?, ?> anAttribute :pluralAttributes) {
// check for declarations in the hierarchy and don't add if declared above
// add attributes that don't have superclasses automatically
ManagedTypeImpl potentialSuperType = getManagedSuperType();
if(null == potentialSuperType) {
declaredAttributes.add((PluralAttribute<X, ?, ?>)anAttribute);
} else {
// add only if we reach the root without finding another declaration
if(!potentialSuperType.isAttributeDeclaredOnlyInLeafType(anAttribute.getName())) {
declaredAttributes.add((PluralAttribute<X, ?, ?>)anAttribute);
}
}
}
return declaredAttributes;
}
/**
* INTERNAL:
* Return an instance of a ManagedType based on the RelationalDescriptor parameter
*/
protected static ManagedTypeImpl<?> create(MetamodelImpl metamodel, ClassDescriptor descriptor) {
// Get the ManagedType property on the descriptor if it exists
ManagedTypeImpl<?> managedType = metamodel.getManagedTypesMap().get(descriptor.getJavaClassName());
// Create an Entity, Embeddable or MappedSuperclass
if (null == managedType) {
// The descriptor can be one of NORMAL:0, INTERFACE:1 (not supported), AGGREGATE:2 or AGGREGATE_COLLECTION:3
if(descriptor.isDescriptorForInterface()) {
// INTERFACE:1 (not supported)
/* throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_interface_inheritance_not_supported",
new Object[] { descriptor, "Interface"}));
*/
// Default to Entity
managedType = new EntityTypeImpl(metamodel, descriptor);
} else if (descriptor.isDescriptorTypeAggregate()) {
// AGGREGATE:2 or AGGREGATE_COLLECTION:3
managedType = new EmbeddableTypeImpl(metamodel, descriptor);
} else if(descriptor.isDescriptorTypeNormal()) {
// NORMAL:0 = ENTITY | MAPPEDSUPERCLASS
// DI 39: Determine if the descriptor is a mappedSuperclass
if(metamodel.hasMappedSuperclass(descriptor.getJavaClassName())) {
// MAPPEDSUPERCLASS - defer to subclass
managedType = MappedSuperclassTypeImpl.create(metamodel, descriptor);
} else {
// ENTITY
managedType = new EntityTypeImpl(metamodel, descriptor);
}
} else {
// unknown descriptor type (or > 3)
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_interface_inheritance_not_supported",
new Object[] { descriptor, "Unknown"}));
}
}
return managedType;
}
/**
* Return the List-valued attribute declared by the managed
* type that corresponds to the specified name and Java
* element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return declared ListAttribute of the given name and
* element type
* @throws IllegalArgumentException if attribute of the given
* name and type is not declared in the managed type
*/
@Override
public <E> ListAttribute<X, E> getDeclaredList(String name, Class<E> elementType) {
// get the attribute parameterized by <Owning type, return Type> - throw an IAE if not found (no need to check hierarchy)
// Handles UC1 and UC2
ListAttribute<X, E> anAttribute = (ListAttribute<X, E>) getList(name, elementType);
// The following verification step will throw an appropriate IAException if required (type checking has been done, and we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get, (optionally a type check) and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the List-valued attribute declared by the managed
* type that corresponds to the specified name.
* @param name the name of the represented attribute
* @return declared ListAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
@Override
public ListAttribute<X, ?> getDeclaredList(String name) {
// return only a List declared on this class - not via inheritance
// Handles UC1 and UC2
ListAttribute<X, ?> anAttribute = (ListAttribute<X, ?>) getList(name);
// The following verification step will throw an appropriate IAException if required (we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the Map-valued attribute of the managed type that
* corresponds to the specified name.
* @param name the name of the represented attribute
* @return MapAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public MapAttribute<X, ?, ?> getDeclaredMap(String name) {
// return only a map declared on this class - not via inheritance
// Handles UC1 and UC2
MapAttribute<X, ?, ?> anAttribute = (MapAttribute<X, ?, ?>) getMap(name);
// The following verification step will throw an appropriate IAException if required (we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the Map-valued attribute of the managed type that
* corresponds to the specified name and Java key and value
* types.
* @param name the name of the represented attribute
* @param keyType the key type of the represented attribute
* @param valueType the value type of the represented attribute
* @return MapAttribute of the given name and key and value
* types
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <K, V> MapAttribute<X, K, V> getDeclaredMap(String name, Class<K> keyType, Class<V> valueType) {
// return only a map declared on this class - not via inheritance
// Handles UC1 and UC2
MapAttribute<X, K, V> anAttribute = (MapAttribute<X, K, V>) getMap(name, keyType, valueType);
// The following verification step will throw an appropriate IAException if required (type checking has been done, and we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get, (optionally a type check) and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the Set-valued attribute declared by the managed type
* that corresponds to the specified name.
* @param name the name of the represented attribute
* @return declared SetAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
@Override
public SetAttribute<X, ?> getDeclaredSet(String name) {
// return only a set declared on this class - not via inheritance
// Handles UC1 and UC2
SetAttribute<X, ?> anAttribute = (SetAttribute<X, ?>) getSet(name);
// The following verification step will throw an appropriate IAException if required (we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the Set-valued attribute declared by the managed type
* that corresponds to the specified name and Java element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return declared SetAttribute of the given name and
* element type
* @throws IllegalArgumentException if attribute of the given
* name and type is not declared in the managed type
*/
@Override
public <E> SetAttribute<X, E> getDeclaredSet(String name, Class<E> elementType) {
// return only a set declared on this class - not via inheritance
// Handles UC1 and UC2
SetAttribute<X, E> anAttribute = (SetAttribute<X, E>) getSet(name, elementType);
// The following verification step will throw an appropriate IAException if required (type checking has been done, and we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get, (optionally a type check) and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the declared single-valued attribute of the managed
* type that corresponds to the specified name in the
* represented type.
* @param name the name of the represented attribute
* @return declared single-valued attribute of the given
* name
* @throws IllegalArgumentException if attribute of the given
* name is not declared in the managed type
*/
@Override
public SingularAttribute<X, ?> getDeclaredSingularAttribute(String name) {
// return only a SingularAttribute declared on this class - not via inheritance
// Handles UC1 and UC2
SingularAttribute<X, ?> anAttribute = (SingularAttribute<X, ?>) getSingularAttribute(name);
// The following verification step will throw an appropriate IAException if required (we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the declared single-valued attribute of the
* managed type that corresponds to the specified name and Java
* type in the represented type.
* @param name the name of the represented attribute
* @param type the type of the represented attribute
* @return declared single-valued attribute of the given
* name and type
* @throws IllegalArgumentException if attribute of the given
* name and type is not declared in the managed type
*/
@Override
public <Y> SingularAttribute<X, Y> getDeclaredSingularAttribute(String name, Class<Y> type) {
// return only a SingularAttribute declared on this class - not via inheritance
// Handles UC1 and UC2
SingularAttribute<X, Y> anAttribute = (SingularAttribute<X, Y>) getSingularAttribute(name, type);
// The following verification step will throw an appropriate IAException if required (type checking has been done, and we can discard the return attribute here)
getDeclaredAttribute(name);
// We return an attribute that has passed through both a get, (optionally a type check) and a declared inheritance check
// all of which would throw an IAException before the return below.
return anAttribute;
}
/**
* Return the single-valued attributes declared by the managed
* type.
* @return declared single-valued attributes
*/
@Override
public Set<SingularAttribute<X, ?>> getDeclaredSingularAttributes() {
// return the set of SingularAttributes declared on this class - not via inheritance
// Get all attributes and filter only for declared attributes
Set<Attribute<X, ?>> allAttributes = new LinkedHashSet<Attribute<X, ?>>(this.members.values());;
// Is it better to add to a new Set or remove from an existing Set without a concurrentModificationException
Set<SingularAttribute<X, ?>> declaredAttributes = new LinkedHashSet<SingularAttribute<X, ?>>();
for(Attribute<X, ?> anAttribute : allAttributes) {
if(!anAttribute.isCollection()) {
declaredAttributes.add((SingularAttribute<X, ?>)anAttribute);
}
}
return declaredAttributes;
}
/**
* INTERNAL:
* Return the RelationalDescriptor associated with this ManagedType
*/
public ClassDescriptor getDescriptor() {
return this.descriptor;
}
/**
* Return the List-valued attribute of the managed type that
* corresponds to the specified name.
* @param name the name of the represented attribute
* @return ListAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public ListAttribute<? super X, ?> getList(String name) {
return getList(name, true);
}
/**
* INTERNAL:
* Return the List-valued attribute of the managed type that
* corresponds to the specified name.
* @param name the name of the represented attribute
* @param performNullCheck - flag on whether we should be doing an IAException check
* @return ListAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
private ListAttribute<? super X, ?> getList(String name, boolean performNullCheck) {
/*
* Note: We do not perform type checking on the get(name)
* If the type is not of the correct Attribute implementation class then
* a possible CCE will be allowed to propagate to the client.
*/
ListAttribute<? super X, ?> anAttribute = (ListAttribute<? super X, ?>)this.members.get(name);
if(performNullCheck && null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return anAttribute;
}
/**
* INTERNAL:
* Perform type checking on the attribute and return types of the named attribute.
* This function will cause an IllegalArgumentException if any of the passed in types are incorrect.
* @param anAttribute - the Attribute we are verifying
* @param attributeElementType - the java element or basic element type
* @param aReturnCollectionType - the plural return type
* @throws IllegalArgumentException if either type is wrong
*/
private void verifyAttributeTypeAndReturnType(Attribute anAttribute, Class attributeElementType, CollectionType aReturnCollectionType) {
// Check for plural or singular attribute
if(anAttribute.isCollection()) {
// check for CollectionAttribute
if(((PluralAttribute)anAttribute).getCollectionType().equals(aReturnCollectionType)) {
// check that the java class is correct (use BindableJavaType not elementType.getJavaType()
Class aBindableJavaClass = ((PluralAttribute)anAttribute).getBindableJavaType();
if(attributeElementType != aBindableJavaClass) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_type_incorrect",
new Object[] { anAttribute.getName(), this, attributeElementType, aBindableJavaClass }));
}
} else {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_return_type_incorrect",
new Object[] { anAttribute.getName(), this, aReturnCollectionType,
((PluralAttribute)anAttribute).getCollectionType()}));
}
}
}
/**
* Return the List-valued attribute of the managed type that
* corresponds to the specified name and Java element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return ListAttribute of the given name and element type
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <E> ListAttribute<? super X, E> getList(String name, Class<E> elementType) {
// We do not use getList(name) so that we can catch a possible CCE on the wrong attribute type
ListAttribute<? super X, E> anAttribute = (ListAttribute<? super X, E>)this.members.get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
} else {
// Throw appropriate IAException if required
verifyAttributeTypeAndReturnType(anAttribute, elementType, CollectionType.LIST);
}
return anAttribute;
}
/**
* INTERNAL:
* Return the ManagedType that represents the superType (superclass) of
* the current ManagedType.
* If the superType is a BasicType - return null
*
* @return ManagedType supertype or null if no superclass
*/
private ManagedTypeImpl getManagedSuperType() {
// Note this method provides the same functionality of the more specific IdentifiableType.superType but is general to ManagedTypeImpl
ManagedTypeImpl<?> aSuperType = null;
// Get the superType if it exists (without using IdentifiableType.superType)
Class<? super X> aSuperClass = this.getJavaType().getSuperclass();
// The superclass for top-level types will be Object - which we will leave as a null supertype on the type
if(null != aSuperClass && aSuperClass != ClassConstants.OBJECT &&
this.getMetamodel().getType(aSuperClass).isManagedType()) { // 315287: return null for BasicType
aSuperType = (ManagedTypeImpl<?>)this.getMetamodel().managedType(aSuperClass);
}
return aSuperType;
}
/**
* Return the Map-valued attribute of the managed type that
* corresponds to the specified name.
* @param name the name of the represented attribute
* @return MapAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public MapAttribute<? super X, ?, ?> getMap(String name) {
/*
* Note: We do not perform type checking on the get(name)
* If the type is not of the correct Attribute implementation class then
* a possible CCE will be allowed to propagate to the client.
*/
MapAttribute<? super X, ?, ?> anAttribute = (MapAttribute<? super X, ?, ?>)this.members.get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return anAttribute;
}
/**
* Return the Map-valued attribute of the managed type that
* corresponds to the specified name and Java key and value
* types.
* @param name the name of the represented attribute
* @param keyType the key type of the represented attribute
* @param valueType the value type of the represented attribute
* @return MapAttribute of the given name and key and value
* types
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <K, V> MapAttribute<? super X, K, V> getMap(String name, Class<K> keyType, Class<V> valueType) {
MapAttribute<? super X, K, V> anAttribute = (MapAttribute<? super X, K, V>)this.getMap(name);
Class<V> aClass = anAttribute.getElementType().getJavaType();
if(valueType != aClass) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_type_incorrect",
new Object[] { name, this, valueType, aClass }));
}
return anAttribute;
}
/**
* INTERNAL:
* Return the Map of AttributeImpl members keyed by String.
*/
protected java.util.Map<String, Attribute<X, ?>> getMembers() {
return this.members;
}
/**
* INTERNAL:
* Return the Metamodel that this ManagedType is associated with.
*/
protected MetamodelImpl getMetamodel() {
return this.metamodel;
}
/**
* Return the Set-valued attribute of the managed type that
* corresponds to the specified name.
* @param name the name of the represented attribute
* @return SetAttribute of the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public SetAttribute<? super X, ?> getSet(String name) {
/*
* Note: We do not perform type checking on the get(name)
* If the type is not of the correct Attribute implementation class then
* a possible CCE will be allowed to propagate to the client.
*/
SetAttribute<? super X, ?> anAttribute = (SetAttribute<? super X, ?>)this.members.get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return anAttribute;
}
/**
* Return the Set-valued attribute of the managed type that
* corresponds to the specified name and Java element type.
* @param name the name of the represented attribute
* @param elementType the element type of the represented
* attribute
* @return SetAttribute of the given name and element type
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <E> SetAttribute<? super X, E> getSet(String name, Class<E> elementType) {
SetAttribute<? super X, E> anAttribute = (SetAttribute<? super X, E>)getSet(name);
Class<E> aClass = anAttribute.getElementType().getJavaType();
if(elementType != aClass) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_type_incorrect",
new Object[] { name, this, elementType, aClass.getName() }));
}
return anAttribute;
}
/**
* Return the single-valued attribute of the managed type that
* corresponds to the specified name in the represented type.
* @param name the name of the represented attribute
* @return single-valued attribute with the given name
* @throws IllegalArgumentException if attribute of the given
* name is not present in the managed type
*/
@Override
public SingularAttribute<? super X, ?> getSingularAttribute(String name) {
Attribute<X, ?> anAttribute = getMembers().get(name);
if(null == anAttribute) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_not_present",
new Object[] { name, this }));
}
return (SingularAttribute<? super X, ?>)anAttribute;
}
/**
* INTERNAL:
* This function returns whether the Object class passed in can be autoboxed
* (a primitive wrapped in its' object type) or the reverse - an autoboxed object
* that wraps a primitive type).
* It answers the question of whether the two classes can be considered to be essentially the same<br>
* This function is used by the metamodel to determine whether the
* IAE (IllegalArgumentException) type checking should be relaxed for SingleAttributes.
*
* @param targetPrimitiveOrWrapperClass (the type we are verifying against)
* @param actualPrimitiveOrWrapperClass (the type that may be the autoboxed or primitive equal
*/
private boolean isAutoboxedType(Class targetPrimitiveOrWrapperClass, Class actualPrimitiveOrWrapperClass) {
BasicTypeHelperImpl typeHelper = BasicTypeHelperImpl.getInstance();
if ((targetPrimitiveOrWrapperClass == null) || (actualPrimitiveOrWrapperClass == null)) {
return false;
}
// Check for the same class in the same classloader or different classloaders
if (targetPrimitiveOrWrapperClass == actualPrimitiveOrWrapperClass ||
targetPrimitiveOrWrapperClass.getCanonicalName().equals(actualPrimitiveOrWrapperClass.getCanonicalName())) {
return false;
}
/**
* We return true for any of the following combinations.
* boolean:Boolean byte:Byte short:Short char:Character int:Integer long:Long float:Float double:Double
*/
// Are we dealing with autoboxed wrappers Boolean, Byte, Short, Character, Integer, Long, Float, Double
// Or are we dealing with the primitives boolean, byte, short, char, int, long, float, double
// Note BigDecimal, BigInteger, Calendar, Timestamp, Time and Date are not wrappers for pimitives
if(typeHelper.isWrapperClass(targetPrimitiveOrWrapperClass) ||
targetPrimitiveOrWrapperClass.isPrimitive()) {
// Check each type (primitive or Class) against each Class type - the target and actual can both be primitives or Objects
if(typeHelper.isBooleanType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isBooleanType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isByteType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isByteType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isShortType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isShortType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isCharacterType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isCharacterType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isIntType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isIntType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isLongType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isLongType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isFloatType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isFloatType(actualPrimitiveOrWrapperClass);
}
if(typeHelper.isDoubleType(targetPrimitiveOrWrapperClass)) {
return typeHelper.isDoubleType(actualPrimitiveOrWrapperClass);
}
}
return false;
}
/**
* Return the single-valued attribute of the managed
* type that corresponds to the specified name and Java type
* in the represented type.
* @param name the name of the represented attribute
* @param type the type of the represented attribute
* @return single-valued attribute with given name and type
* @throws IllegalArgumentException if attribute of the given
* name and type is not present in the managed type
*/
@Override
public <Y> SingularAttribute<? super X, Y> getSingularAttribute(String name, Class<Y> type) {
SingularAttribute<? super X, Y> anAttribute = (SingularAttribute<? super X, Y>)getSingularAttribute(name);
Class<Y> aClass = anAttribute.getType().getJavaType();
// Determine whether to throw an IAE for the wrong type - Note: we relax the rules for autoboxed types
if(type != aClass && !isAutoboxedType(type, aClass)) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage(
"metamodel_managed_type_attribute_type_incorrect",
new Object[] { name, this, type, aClass }));
}
return anAttribute;
}
/**
* Return the single-valued attributes of the managed type.
* @return single-valued attributes
*/
@Override
public Set<SingularAttribute<? super X, ?>> getSingularAttributes() {
// Iterate the members set for attributes of type SingularAttribute
Set singularAttributeSet = new LinkedHashSet<SingularAttribute<? super X, ?>>();
for(Attribute<X, ?> anAttribute : this.members.values()) {
if(!((AttributeImpl<? super X, ?>)anAttribute).isPlural()) {
singularAttributeSet.add(anAttribute);
}
}
return singularAttributeSet;
}
/**
* INTERNAL:
* Recursively search the superclass tree of the current managedType
* for the named attribute.<p>
* This internal function is used exclusively by the getDeclared*() calls on ManagedType objects.<p>
* This function is type agnostic (Set, List, Map and Collection are treated as attributes)
* @param attributeName - String name of possible declared attribute search
* @return true if the attribute is declared at this first level,
* false if no attribute is found in the superTree, or
* false if the attribute is found declared higher up in the inheritance superTree
*/
private boolean isAttributeDeclaredOnlyInLeafType(String attributeName) {
return isAttributeDeclaredOnlyInLeafType(attributeName, this.getMembers().get(attributeName));
}
/**
* INTERNAL:
* Recursively search the superclass tree of the current managedType
* for the named attribute.<p>
* This internal function is used exclusively by the getDeclared*() calls on ManagedType objects.<p>
* This function is type agnostic (Set, List, Map and Collection are treated as attributes)
* @param attributeName - String name of possible declared attribute search
* @return true if the attribute is declared at this first level,
* false if no attribute is found in the superTree, or
* false if the attribute is found declared higher up in the inheritance superTree
*/
private boolean isAttributeDeclaredOnlyInLeafType(String attributeName, Attribute firstLevelAttribute) {
/*
* Issues: We need to take into account whether the superType is an Entity or MappedSuperclass
* - If superType is entity then inheriting entities will not have copies of the inherited mappings
* - however, if superType is mappedSuperclass then all inheriting mappedSuperclasses and the first
* entity will have copies of the inherited mappings
* - Note: a sub-entity can override a mapping above it
* Use Cases:
* UC1 Superclass declares attribute
* UC1.1: Entity (searched) --> Entity --> Entity (declares attribute)
* UC1.2: Entity (searched) --> Entity (copy of attribute) --> MappedSuperclass (declares attribute)
* UC1.3: Entity (searched) --> MappedSuperclass --> Entity (declares attribute)
* UC1.4: Entity (copy of attribute) (searched) --> MappedSuperclass (no copy of attribute) (searched) --> MappedSuperclass (declares attribute) (searched)
* UC1.5: Entity (copy of attribute) (searched) --> MappedSuperclass (declares attribute) (searched) --> MappedSuperclass
* UC2 Nobody declares attribute
* UC2.1: Entity (searched) --> Entity --> MappedSuperclass (declares attribute)
* UC2.2: Entity (searched) --> Entity --> Entity (declares attribute)
* UC2.3: Entity (searched) --> MappedSuperclass (searched) --> MappedSuperclass (declares attribute)
* UC2.4: Entity (searched) --> MappedSuperclass (searched) --> Entity (declares attribute)
* UC3 Superclass declares attribute but child overrides it
* UC3.1: Entity (searched) --> Entity --> MappedSuperclass (declares attribute)
* UC3.2: Entity (searched) --> Entity --> Entity (declares attribute)
* UC3.3: Entity (searched) --> MappedSuperclass (override attribute) (searched) --> MappedSuperclass (declares attribute)
* UC3.4: Entity (searched) --> MappedSuperclass (override attribute) (searched) --> Entity (declares attribute) (searched)
* UC3.5: Entity (override attribute) (searched) --> MappedSuperclass (searched) --> MappedSuperclass (declares attribute) (searched)
* UC3.6: Entity (override attribute) (searched) --> MappedSuperclass (searched) --> Entity (declares attribute)
* Solution:
* Results Expected for hasDeclaredAttribute()
* True = attribute declared only on current type
* False = attribute not found in superType tree or attribute found in more than one(1) level of the superType tree
* Base Case
* attribute found && no superType exists = true
* attribute not found && no superType exists = false
* Recursive Case
* Exit(false) as soon as attribute is found in a superType - without continuing to the root
* continue as long as we find an attribute in the superType (essentially only MappedSuperclass parents)
**/
Attribute<X, ?> anAttribute = this.getMembers().get(attributeName);
ManagedTypeImpl<?> aSuperType = getManagedSuperType();
// Base Case: If we are at the root, check for the attribute and return results immediately
if(null == aSuperType) { // 315287: the superType will be null if the root is a BasicType (non-Entity|non-MappedSuperclass)
if(null == anAttribute && null != firstLevelAttribute) {
return true;
} else {
// UC 1.3 (part of the else condition (anAttribute != null)) is handled by the return false in null != aSuperTypeAttribute
// UC 1.4 (when caller is firstLevel) superType does not contain the attribute - check that the current attribute and the first differ
if(null != anAttribute && anAttribute == firstLevelAttribute) {
return true;
} else {
return false;
}
}
} else {
// Recursive Case: check hierarchy both if the immediate superclass is a MappedSuperclassType or EntityType
Attribute<?, ?> aSuperTypeAttribute = aSuperType.getMembers().get(attributeName);
// UC1.3 The immediate mappedSuperclass may have the attribute - we check it in the base case of the next recursive call
if(null != aSuperTypeAttribute) {
// return false immediately if a superType exists above the first level
return false;
} else {
// UC1.4 (when caller is firstLevel.supertype) - the immediate mappedSuperclass may not have the attribute if another one up the chain of rmappedSuperclasses declares it
// UC 1.5: keep searching a possible chain of mappedSuperclasses or entities
return aSuperType.isAttributeDeclaredOnlyInLeafType(attributeName, firstLevelAttribute);
}
}
}
/**
* INTERNAL:
* Handle the case where we were unable to determine the element type of the plural attribute.
* Normally this function is never required and should have a code coverage of 0%.
*/
private AttributeImpl initializePluralAttributeTypeNotFound(ManagedTypeImpl managedType, CollectionMapping collectionMapping, boolean validation) {
// default to List
// TODO: System.out.println("_Warning: type is null on " + colMapping);
AttributeImpl<X, ?> member = new ListAttributeImpl(managedType, collectionMapping, validation);
return member;
}
/**
* INTERNAL:
* Initialize the members of this ManagedType based on the mappings defined on the descriptor.
* We process the appropriate Map, List, Set, Collection or Object/primitive types.<p>
* Initialization should occur after all types in the metamodel have been created already.
*
*/
protected void initialize() { // Future: Check all is*Policy() calls
/*
* Design Issue 37 and 58:
* http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_37:_20090708:_CollectionAttribute_acts_as_a_peer_of_Map.2C_Set.2C_List_but_should_be_a_super_interface
* http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_58:_20090807:_ManagedType_Attribute_Initialization_must_differentiate_between_Collection_and_List
*
* The hierarchy of the Metamodel API has Collection alongside List, Set and Map.
* However, in a normal Java collections framework Collection is an
* abstract superclass of List, Set and Map (with Map not really a Collection).
* We therefore need to treat Collection here as a peer of the other "collections" while also treating it as a non-instantiated superclass.
*/
// this could have been initialized earlier if it is an inheriting subclass of a MappedSuperclassType
// See MappedSuperclassType.getMemberFromInheritingType()
if (null != this.members) {
//this is already initialized
return;
}
this.members = new HashMap<String, Attribute<X, ?>>();
// Get and process all mappings on the relationalDescriptor
for (DatabaseMapping mapping : getDescriptor().getMappings()) {
AttributeImpl<X, ?> member = null;
/**
* The following section will determine the plural attribute type for each mapping on the managedType.
* Special handling is required for differentiation of List and Collection
* beyond their shared IndirectList ContainerPolicy,
* as even though a List is an implementation of Collection in Java,
* In the Metamodel we treat the Collection as a peer of a List.
*
* Collection.class --> via IndirectList --> CollectionAttributeImpl
* List.class --> via IndirectList --> ListAttributeImpl
* Set.class --> SetAttributeImpl
* Map.class --> MapAttributeImpl
*
* If the type is Embeddable then special handling will be required to get the plural type.
* The embeddable's MethodAttributeAccessor will not have the getMethod set.
* We can however use reflection and get the returnType directly from the class
* using the getMethodName on the accessor.
*/
// Tie into the collection hierarchy at a lower level
if (mapping instanceof CollectionMapping) {
// Handle 1:m, n:m collection mappings
CollectionMapping colMapping = (CollectionMapping) mapping;
ContainerPolicy collectionContainerPolicy = colMapping.getContainerPolicy();
if (collectionContainerPolicy.isMapPolicy()) {
// Handle the 3 Map type mappings (policy.isMappedKeyMapPolicy()) is handled by isMapPolicy())
member = new MapAttributeImpl(this, colMapping, true);
// check mapping.attributeAcessor.attributeField.type=Collection
} else if (collectionContainerPolicy.isListPolicy()) {
// This seems very over complex...
/**
* Handle lazy Collections and Lists and the fact that both return an IndirectList policy.
* We check the type on the attributeField of the attributeAccessor on the mapping
*/
Class aType = null;
// 325699: AttributeAccessor is subclassed by both IntanceVariableAttributeAccessor (JPA) and ValuesAccessor (Dynamic JPA)
if(colMapping.getAttributeAccessor() instanceof ValuesAccessor) {
member = new ListAttributeImpl(this, colMapping);
} else if(colMapping.getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) {
Field aField = ((InstanceVariableAttributeAccessor)colMapping.getAttributeAccessor()).getAttributeField();
// MappedSuperclasses need special handling to get their type from an inheriting subclass
if(null == aField) { // MappedSuperclass field will not be set
if(this.isMappedSuperclass()) {
// get inheriting subtype member (without handling @override annotations)
MappedSuperclassTypeImpl aMappedSuperclass = ((MappedSuperclassTypeImpl)this);
AttributeImpl inheritingTypeMember = aMappedSuperclass.getMemberFromInheritingType(colMapping.getAttributeName());
// 322166: If attribute is defined on this current ManagedType (and not on a superclass) - do not attempt a reflective call on a superclass
if(null != inheritingTypeMember) {
// Verify we have an attributeAccessor
aField = ((InstanceVariableAttributeAccessor)inheritingTypeMember.getMapping().getAttributeAccessor()).getAttributeField();
}
}
}
// 322166: The attribute may be defined on the current ManagedType - not inherited
if(null == aField) {
// Check attributeName when the field is null
aType = this.getTypeClassFromAttributeOrMethodLevelAccessor(mapping);
} else {
aType = aField.getType();
}
// This attribute is declared as List
if((aType != null) && List.class.isAssignableFrom(aType)) {
member = new ListAttributeImpl(this, colMapping, true);
} else if((aType != null) && Collection.class.isAssignableFrom(aType)) {
// This attribute is therefore declared as Collection
member = new CollectionAttributeImpl(this, colMapping, true);
} else {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
} else {
// handle variations of missing get/set methods - only for Collection vs List
if(colMapping.getAttributeAccessor() instanceof MethodAttributeAccessor) {
/**
* The following call will perform a getMethod call for us.
* If no getMethod exists, we will secondarily check the getMethodName below.
*/
aType = colMapping.getAttributeAccessor().getAttributeClass();
if((aType != null) && List.class.isAssignableFrom(aType)) {
member = new ListAttributeImpl(this, colMapping, true);
} else if((aType != null) && Collection.class.isAssignableFrom(aType)) {
member = new CollectionAttributeImpl(this, colMapping, true);
} else {
/**
* In this block we have the following scenario:
* 1) The access type is "field"
* 2) The get method is not set on the entity
* 3) The get method is named differently than the attribute
*/
// Type may be null when no getMethod exists for the class for a ManyToMany mapping
// Here we check the returnType on the declared method on the class directly
String getMethodName = ((MethodAttributeAccessor)colMapping.getAttributeAccessor()).getGetMethodName();
if(null == getMethodName) {
// Check declaredFields in the case where we have no getMethod or getMethodName
try {
Field field = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
field = AccessController.doPrivileged(new PrivilegedGetDeclaredField(
this.getJavaType(), colMapping.getAttributeName(), false));
} catch (PrivilegedActionException exception) {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
} else {
field = PrivilegedAccessHelper.getDeclaredField(
this.getJavaType(), colMapping.getAttributeName(), false);
}
if(null == field) {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
} else {
aType = field.getType();
if((aType != null) && List.class.isAssignableFrom(aType)) {
member = new ListAttributeImpl(this, colMapping, true);
} else if((aType != null) && Collection.class.isAssignableFrom(aType)) {
member = new CollectionAttributeImpl(this, colMapping, true);
} else {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
}
} catch (Exception e) {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
} else {
/**
* Field access Handling:
* If a get method name exists, we check the return type on the method directly
* using reflection.
* In all failure cases we default to the List type.
*/
try {
Method aMethod = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod(
this.getJavaType(), getMethodName, null));
} else {
aMethod = PrivilegedAccessHelper.getDeclaredMethod(
this.getJavaType(), getMethodName, null);
}
if(null == aMethod) {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
} else {
aType = aMethod.getReturnType();
if((aType != null) && List.class.isAssignableFrom(aType)) {
member = new ListAttributeImpl(this, colMapping, true);
} else if((aType != null) && Collection.class.isAssignableFrom(aType)) {
member = new CollectionAttributeImpl(this, colMapping, true);
} else {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
}
} catch (Exception e) {
member = initializePluralAttributeTypeNotFound(this, colMapping, true);
}
}
}
}
}
} else {
// Handle non-lazy Collection or Set type mappings (IndirectSet.isAssignableFrom(Set.class) == false)
if ((collectionContainerPolicy.getContainerClass() != null) && Set.class.isAssignableFrom(collectionContainerPolicy.getContainerClass())) {
member = new SetAttributeImpl(this, colMapping, true);
} else {
// Check for non-lazy Collection policy possibly instantiated to a Set or List (both of which is ignored)
if(collectionContainerPolicy.isCollectionPolicy()) {
member = new CollectionAttributeImpl(this, colMapping, true);
} else {
// Handle Collection type mappings as a default (we should never get here)
// TODO: System.out.println("_Warning: defaulting to non-Set specific Collection type on " + colMapping);
member = new CollectionAttributeImpl(this, colMapping);
}
}
}
} else {
// Handle 1:1 single object and direct mappings including EnumSet
member = new SingularAttributeImpl(this, mapping, true);
}
// 303063: secondary check for a null value put - should never happen but this will show on code coverage
if(null == member) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, "metamodel_attribute_getmember_is_null",
mapping.getAttributeName(), this, descriptor);
}
this.members.put(mapping.getAttributeName(), member);
}
}
/**
* INTERNAL:
* Get the elementType directly from the class using a reflective method call
* directly on the containing java class associated with this managedType.
*/
protected Class getTypeClassFromAttributeOrMethodLevelAccessor(DatabaseMapping mapping) {
/**
* In this block we have the following scenario:
* 1) The access type is "method" or "field"
* 1a) The get method is set on the entity (method access)
* 1b) The get method is not set on the entity (field access)
* 1c) The get method is named differently than the attribute
*/
// Type may be null when no getMethod exists for the class for a ManyToMany mapping
// Here we check the returnType on the declared method on the class directly
Class aType = null;
Field aField = null;
String getMethodName = null;
//boolean isFieldLevelAccess = false;
// 1) Check access Type
if(mapping.getAttributeAccessor() instanceof MethodAttributeAccessor) {
//isFieldLevelAccess = false;
getMethodName = ((MethodAttributeAccessor)mapping.getAttributeAccessor()).getGetMethodName();
} else if(mapping.getAttributeAccessor() instanceof InstanceVariableAttributeAccessor) {
//isFieldLevelAccess = true;
aField = ((InstanceVariableAttributeAccessor)mapping.getAttributeAccessor()).getAttributeField();
}
// 2) based on access type get the element type
// 3) If field level access - perform a getDeclaredField call
if(null == aField && this.getJavaType() != null) {
// Field level access
// Check declaredFields in the case where we have no getMethod or getMethodName
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
aField = AccessController.doPrivileged(new PrivilegedGetDeclaredField(
this.getJavaType(), mapping.getAttributeName(), false));
} else {
aField = PrivilegedAccessHelper.getDeclaredField(
this.getJavaType(), mapping.getAttributeName(), false);
}
} catch (PrivilegedActionException pae) {
} catch (NoSuchFieldException nsfe) {
}
}
// 4) If method level access - perform a getDeclaredMethod call
/**
* Field access Handling:
* If a get method name exists, we check the return type on the method directly
* using reflection.
* In all failure cases we default to the List type.
*/
if(null == aField && this.getJavaType() != null && getMethodName !=null) {
Method aMethod = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod(
this.getJavaType(), getMethodName, null));
} else {
aMethod = PrivilegedAccessHelper.getDeclaredMethod(
this.getJavaType(), getMethodName, null);
}
} catch (PrivilegedActionException pae) {
} catch (NoSuchMethodException nsfe) {
} catch (NullPointerException npe) {
// case: null name arg to Class.searchMethods from getDeclaredMethod if getMethodName is null
// because we do not know the javaType on the Type (descriptor.javaClass was null)
// See bug# 303063
npe.printStackTrace();
}
if(null != aMethod) {
aType = aMethod.getReturnType();
}
}
// 5) Special processing for MappedSuperclass hierarchies
// MappedSuperclasses need special handling to get their type from an inheriting subclass
if(null == aField && null == aType && this.isMappedSuperclass()) {
// get inheriting subtype member (without handling @override annotations)
MappedSuperclassTypeImpl aMappedSuperclass = ((MappedSuperclassTypeImpl)this);
AttributeImpl inheritingTypeMember = aMappedSuperclass.getMemberFromInheritingType(mapping.getAttributeName());
aField = ((InstanceVariableAttributeAccessor)inheritingTypeMember.getMapping().getAttributeAccessor()).getAttributeField();
}
// 6) get the type from the resulting field (method level access was handled)
if(null != aField) {
// field access
aType = aField.getType();
}
// 7) catch unsupported element type
if(null == aType) {
aType = MetamodelImpl.DEFAULT_ELEMENT_TYPE_FOR_UNSUPPORTED_MAPPINGS;
}
// 303063: secondary check for case where descriptor has no java class set - should never happen but this will show on code coverage
if(null == this.getJavaType()) {
AbstractSessionLog.getLog().log(SessionLog.FINEST, SessionLog.METAMODEL, "metamodel_relationaldescriptor_javaclass_null_on_managedType", descriptor, this);
}
return aType;
}
/**
* INTERNAL:
* Return whether this type is identifiable.
* This would be EntityType and MappedSuperclassType
*/
@Override
protected boolean isIdentifiableType() {
return false;
}
/**
* INTERNAL:
* Return whether this type is identifiable.
* This would be EmbeddableType as well as EntityType and MappedSuperclassType
*/
@Override
protected boolean isManagedType() {
return true;
}
/**
* INTERNAL:
* Append the partial string representation of the receiver to the StringBuffer.
*/
@Override
protected void toStringHelper(StringBuffer aBuffer) {
aBuffer.append(" descriptor: ");
aBuffer.append(this.getDescriptor());
if(null != this.getDescriptor()) {
aBuffer.append(", mappings: ");
aBuffer.append(this.getDescriptor().getMappings().size());
}
}
}