| /* |
| * 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()); |
| } |
| } |
| } |