/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
 * third-party contributors as indicated by either @author tags or express
 * copyright attribution statements applied by the authors.  All
 * third-party contributions are distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.jpa.internal.metamodel;

import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.util.Iterator;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.Type;

import org.hibernate.annotations.common.AssertionFailure;
import org.hibernate.jpa.internal.EntityManagerMessageLogger;
import org.hibernate.jpa.internal.HEMLogging;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.property.Getter;
import org.hibernate.property.MapAccessor;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.EntityType;

A factory for building Attribute instances. Exposes 3 main services for building
  1. normal attributes
  2. id attributes
  3. version attributes
Author:Steve Ebersole, Emmanuel Bernard
/** * A factory for building {@link Attribute} instances. Exposes 3 main services for building<ol> * <li>{@link #buildAttribute normal attributes}</li> * <li>{@link #buildIdAttribute id attributes}</li> * <li>{@link #buildVersionAttribute version attributes}</li> * <ol> * * @author Steve Ebersole * @author Emmanuel Bernard */
public class AttributeFactory { private static final EntityManagerMessageLogger LOG = HEMLogging.messageLogger( AttributeFactory.class ); private final MetadataContext context; public AttributeFactory(MetadataContext context) { this.context = context; }
Build a normal attribute.
Params:
  • ownerType – The descriptor of the attribute owner (aka declarer).
  • property – The Hibernate property descriptor for the attribute
Type parameters:
  • <X> – The type of the owner
  • <Y> – The attribute type
Returns:The built attribute descriptor or null if the attribute is not part of the JPA 2 model (eg backrefs)
/** * Build a normal attribute. * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the attribute * @param <X> The type of the owner * @param <Y> The attribute type * * @return The built attribute descriptor or null if the attribute is not part of the JPA 2 model (eg backrefs) */
@SuppressWarnings({"unchecked"}) public <X, Y> AttributeImplementor<X, Y> buildAttribute(AbstractManagedType<X> ownerType, Property property) { if ( property.isSynthetic() ) { // hide synthetic/virtual properties (fabricated by Hibernate) from the JPA metamodel. LOG.tracef( "Skipping synthetic property %s(%s)", ownerType.getTypeName(), property.getName() ); return null; } LOG.trace( "Building attribute [" + ownerType.getTypeName() + "." + property.getName() + "]" ); final AttributeContext<X> attributeContext = wrap( ownerType, property ); final AttributeMetadata<X, Y> attributeMetadata = determineAttributeMetadata( attributeContext, normalMemberResolver ); if ( attributeMetadata == null ) { return null; } if ( attributeMetadata.isPlural() ) { return buildPluralAttribute( (PluralAttributeMetadata) attributeMetadata ); } final SingularAttributeMetadata<X, Y> singularAttributeMetadata = (SingularAttributeMetadata<X, Y>) attributeMetadata; final Type<Y> metaModelType = getMetaModelType( singularAttributeMetadata.getValueContext() ); return new SingularAttributeImpl<X, Y>( attributeMetadata.getName(), attributeMetadata.getJavaType(), ownerType, attributeMetadata.getMember(), false, false, property.isOptional(), metaModelType, attributeMetadata.getPersistentAttributeType() ); } private <X> AttributeContext<X> wrap(final AbstractManagedType<X> ownerType, final Property property) { return new AttributeContext<X>() { public AbstractManagedType<X> getOwnerType() { return ownerType; } public Property getPropertyMapping() { return property; } }; }
Build the identifier attribute descriptor
Params:
  • ownerType – The descriptor of the attribute owner (aka declarer).
  • property – The Hibernate property descriptor for the identifier attribute
Type parameters:
  • <X> – The type of the owner
  • <Y> – The attribute type
Returns:The built attribute descriptor
/** * Build the identifier attribute descriptor * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the identifier attribute * @param <X> The type of the owner * @param <Y> The attribute type * * @return The built attribute descriptor */
@SuppressWarnings({"unchecked"}) public <X, Y> SingularAttributeImpl<X, Y> buildIdAttribute( AbstractIdentifiableType<X> ownerType, Property property) { LOG.trace( "Building identifier attribute [" + ownerType.getTypeName() + "." + property.getName() + "]" ); final AttributeContext<X> attributeContext = wrap( ownerType, property ); final SingularAttributeMetadata<X, Y> attributeMetadata = (SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, identifierMemberResolver ); final Type<Y> metaModelType = getMetaModelType( attributeMetadata.getValueContext() ); return new SingularAttributeImpl.Identifier( property.getName(), attributeMetadata.getJavaType(), ownerType, attributeMetadata.getMember(), metaModelType, attributeMetadata.getPersistentAttributeType() ); }
Build the version attribute descriptor
Params:
  • ownerType – The descriptor of the attribute owner (aka declarer).
  • property – The Hibernate property descriptor for the version attribute
Type parameters:
  • <X> – The type of the owner
  • <Y> – The attribute type
Returns:The built attribute descriptor
/** * Build the version attribute descriptor * * @param ownerType The descriptor of the attribute owner (aka declarer). * @param property The Hibernate property descriptor for the version attribute * @param <X> The type of the owner * @param <Y> The attribute type * * @return The built attribute descriptor */
@SuppressWarnings({"unchecked"}) public <X, Y> SingularAttributeImpl<X, Y> buildVersionAttribute( AbstractIdentifiableType<X> ownerType, Property property) { LOG.trace( "Building version attribute [ownerType.getTypeName()" + "." + "property.getName()]" ); final AttributeContext<X> attributeContext = wrap( ownerType, property ); final SingularAttributeMetadata<X, Y> attributeMetadata = (SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, versionMemberResolver ); final Type<Y> metaModelType = getMetaModelType( attributeMetadata.getValueContext() ); return new SingularAttributeImpl.Version( property.getName(), attributeMetadata.getJavaType(), ownerType, attributeMetadata.getMember(), metaModelType, attributeMetadata.getPersistentAttributeType() ); } @SuppressWarnings("unchecked") private <X, Y, E, K> AttributeImplementor<X, Y> buildPluralAttribute(PluralAttributeMetadata<X, Y, E> attributeMetadata) { final Type<E> elementType = getMetaModelType( attributeMetadata.getElementValueContext() ); if ( java.util.Map.class.isAssignableFrom( attributeMetadata.getJavaType() ) ) { final Type<K> keyType = getMetaModelType( attributeMetadata.getMapKeyValueContext() ); return PluralAttributeImpl.create( attributeMetadata.getOwnerType(), elementType, attributeMetadata.getJavaType(), keyType ) .member( attributeMetadata.getMember() ) .property( attributeMetadata.getPropertyMapping() ) .persistentAttributeType( attributeMetadata.getPersistentAttributeType() ) .build(); } return PluralAttributeImpl.create( attributeMetadata.getOwnerType(), elementType, attributeMetadata.getJavaType(), null ) .member( attributeMetadata.getMember() ) .property( attributeMetadata.getPropertyMapping() ) .persistentAttributeType( attributeMetadata.getPersistentAttributeType() ) .build(); } @SuppressWarnings("unchecked") private <Y> Type<Y> getMetaModelType(ValueContext typeContext) { switch ( typeContext.getValueClassification() ) { case BASIC: { return new BasicTypeImpl<Y>( typeContext.getBindableType(), Type.PersistenceType.BASIC ); } case ENTITY: { final org.hibernate.type.EntityType type = (EntityType) typeContext.getValue().getType(); return (Type<Y>) context.locateEntityType( type.getAssociatedEntityName() ); } case EMBEDDABLE: { final Component component = (Component) typeContext.getValue(); final EmbeddableTypeImpl<Y> embeddableType = new EmbeddableTypeImpl<Y>( typeContext.getBindableType(), typeContext.getAttributeMetadata().getOwnerType(), (ComponentType) typeContext.getValue().getType() ); context.registerEmbeddedableType( embeddableType ); final Iterator<Property> subProperties = component.getPropertyIterator(); while ( subProperties.hasNext() ) { final Property property = subProperties.next(); final AttributeImplementor<Y, Object> attribute = buildAttribute( embeddableType, property ); if ( attribute != null ) { embeddableType.getBuilder().addAttribute( attribute ); } } embeddableType.lock(); return embeddableType; } default: { throw new AssertionFailure( "Unknown type : " + typeContext.getValueClassification() ); } } } private EntityMetamodel getDeclarerEntityMetamodel(AbstractIdentifiableType<?> ownerType) { final Type.PersistenceType persistenceType = ownerType.getPersistenceType(); if ( persistenceType == Type.PersistenceType.ENTITY ) { return context.getSessionFactory() .getEntityPersister( ownerType.getTypeName() ) .getEntityMetamodel(); } else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS ) { PersistentClass persistentClass = context.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType ); return context.getSessionFactory() .getEntityPersister( persistentClass.getClassName() ) .getEntityMetamodel(); } else { throw new AssertionFailure( "Cannot get the metamodel for PersistenceType: " + persistenceType ); } }
A contract for defining the meta information about a Value
/** * A contract for defining the meta information about a {@link Value} */
private interface ValueContext {
Enum of the simplified types a value might be. These relate more to the Hibernate classification then the JPA classification
/** * Enum of the simplified types a value might be. These relate more to the Hibernate classification * then the JPA classification */
enum ValueClassification { EMBEDDABLE, ENTITY, BASIC }
Retrieve the value itself
Returns:The value
/** * Retrieve the value itself * * @return The value */
public Value getValue(); public Class getBindableType();
Retrieve the simplified value classification
Returns:The value type
/** * Retrieve the simplified value classification * * @return The value type */
public ValueClassification getValueClassification();
Retrieve the metadata about the attribute from which this value comes
Returns:The "containing" attribute metadata.
/** * Retrieve the metadata about the attribute from which this value comes * * @return The "containing" attribute metadata. */
public AttributeMetadata getAttributeMetadata(); }
Basic contract for describing an attribute. The "description" is partially in terms of JPA (getPersistentAttributeType and getOwnerType), partially in terms of Hibernate (getPropertyMapping) and partially just in terms of the java model itself (getName, getMember and getJavaType).
Type parameters:
  • <X> – The attribute owner type
  • <Y> – The attribute type.
/** * Basic contract for describing an attribute. The "description" is partially in terms * of JPA ({@link #getPersistentAttributeType} and {@link #getOwnerType}), partially in * terms of Hibernate ({@link #getPropertyMapping}) and partially just in terms of the java * model itself ({@link #getName}, {@link #getMember} and {@link #getJavaType}). * * @param <X> The attribute owner type * @param <Y> The attribute type. */
private interface AttributeMetadata<X, Y> {
Retrieve the name of the attribute
Returns:The attribute name
/** * Retrieve the name of the attribute * * @return The attribute name */
public String getName();
Retrieve the member defining the attribute
Returns:The attribute member
/** * Retrieve the member defining the attribute * * @return The attribute member */
public Member getMember();
Retrieve the attribute java type.
Returns:The java type of the attribute.
/** * Retrieve the attribute java type. * * @return The java type of the attribute. */
public Class<Y> getJavaType();
Get the JPA attribute type classification for this attribute.
Returns:The JPA attribute type classification
/** * Get the JPA attribute type classification for this attribute. * * @return The JPA attribute type classification */
public Attribute.PersistentAttributeType getPersistentAttributeType();
Retrieve the attribute owner's metamodel information
Returns:The metamodel information for the attribute owner
/** * Retrieve the attribute owner's metamodel information * * @return The metamodel information for the attribute owner */
public AbstractManagedType<X> getOwnerType();
Retrieve the Hibernate property mapping related to this attribute.
Returns:The Hibernate property mapping
/** * Retrieve the Hibernate property mapping related to this attribute. * * @return The Hibernate property mapping */
public Property getPropertyMapping();
Is the attribute plural (a collection)?
Returns:True if it is plural, false otherwise.
/** * Is the attribute plural (a collection)? * * @return True if it is plural, false otherwise. */
public boolean isPlural(); }
Attribute metadata contract for a non-plural attribute.
Type parameters:
  • <X> – The owner type
  • <Y> – The attribute type
/** * Attribute metadata contract for a non-plural attribute. * * @param <X> The owner type * @param <Y> The attribute type */
private interface SingularAttributeMetadata<X, Y> extends AttributeMetadata<X, Y> {
Retrieve the value context for this attribute
Returns:The attributes value context
/** * Retrieve the value context for this attribute * * @return The attributes value context */
public ValueContext getValueContext(); }
Attribute metadata contract for a plural attribute.
Type parameters:
  • <X> – The owner type
  • <Y> – The attribute type (the collection type)
  • <E> – The collection element type
/** * Attribute metadata contract for a plural attribute. * * @param <X> The owner type * @param <Y> The attribute type (the collection type) * @param <E> The collection element type */
@SuppressWarnings("UnusedDeclaration") private interface PluralAttributeMetadata<X, Y, E> extends AttributeMetadata<X, Y> {
Retrieve the JPA collection type classification for this attribute
Returns:The JPA collection type classification
/** * Retrieve the JPA collection type classification for this attribute * * @return The JPA collection type classification */
public PluralAttribute.CollectionType getAttributeCollectionType();
Retrieve the value context for the collection's elements.
Returns:The value context for the collection's elements.
/** * Retrieve the value context for the collection's elements. * * @return The value context for the collection's elements. */
public ValueContext getElementValueContext();
Retrieve the value context for the collection's keys (if a map, null otherwise).
Returns:The value context for the collection's keys (if a map, null otherwise).
/** * Retrieve the value context for the collection's keys (if a map, null otherwise). * * @return The value context for the collection's keys (if a map, null otherwise). */
public ValueContext getMapKeyValueContext(); }
Bundle's a Hibernate property mapping together with the JPA metamodel information of the attribute owner.
Type parameters:
  • <X> – The owner type.
/** * Bundle's a Hibernate property mapping together with the JPA metamodel information * of the attribute owner. * * @param <X> The owner type. */
private interface AttributeContext<X> {
Retrieve the attribute owner.
Returns:The owner.
/** * Retrieve the attribute owner. * * @return The owner. */
public AbstractManagedType<X> getOwnerType();
Retrieve the Hibernate property mapping.
Returns:The Hibernate property mapping.
/** * Retrieve the Hibernate property mapping. * * @return The Hibernate property mapping. */
public Property getPropertyMapping(); }
Contract for how we resolve the Member for a give attribute context.
/** * Contract for how we resolve the {@link Member} for a give attribute context. */
private interface MemberResolver { public Member resolveMember(AttributeContext attributeContext); }
Here is most of the nuts and bolts of this factory, where we interpret the known JPA metadata against the known Hibernate metadata and build a descriptor for the attribute.
Params:
  • attributeContext – The attribute to be described
  • memberResolver – Strategy for how to resolve the member defining the attribute.
Type parameters:
  • <X> – The owner type
  • <Y> – The attribute type
Returns:The attribute description
/** * Here is most of the nuts and bolts of this factory, where we interpret the known JPA metadata * against the known Hibernate metadata and build a descriptor for the attribute. * * @param attributeContext The attribute to be described * @param memberResolver Strategy for how to resolve the member defining the attribute. * @param <X> The owner type * @param <Y> The attribute type * * @return The attribute description */
@SuppressWarnings({"unchecked"}) private <X, Y> AttributeMetadata<X, Y> determineAttributeMetadata( AttributeContext<X> attributeContext, MemberResolver memberResolver) { LOG.trace( "Starting attribute metadata determination [" + attributeContext.getPropertyMapping() .getName() + "]" ); final Member member = memberResolver.resolveMember( attributeContext ); LOG.trace( " Determined member [" + member + "]" ); final Value value = attributeContext.getPropertyMapping().getValue(); final org.hibernate.type.Type type = value.getType(); LOG.trace( " Determined type [name=" + type.getName() + ", class=" + type.getClass().getName() + "]" ); if ( type.isAnyType() ) { // ANY mappings are currently not supported in the JPA metamodel; see HHH-6589 if ( context.isIgnoreUnsupported() ) { return null; } else { throw new UnsupportedOperationException( "ANY not supported" ); } } else if ( type.isAssociationType() ) { // collection or entity if ( type.isEntityType() ) { // entity return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, determineSingularAssociationAttributeType( member ) ); } // collection if ( value instanceof Collection ) { final Collection collValue = (Collection) value; final Value elementValue = collValue.getElement(); final org.hibernate.type.Type elementType = elementValue.getType(); // First, determine the type of the elements and use that to help determine the // collection type) final Attribute.PersistentAttributeType elementPersistentAttributeType; final Attribute.PersistentAttributeType persistentAttributeType; if ( elementType.isAnyType() ) { if ( context.isIgnoreUnsupported() ) { return null; } else { throw new UnsupportedOperationException( "collection of any not supported yet" ); } } final boolean isManyToMany = isManyToMany( member ); if ( elementValue instanceof Component ) { elementPersistentAttributeType = Attribute.PersistentAttributeType.EMBEDDED; persistentAttributeType = Attribute.PersistentAttributeType.ELEMENT_COLLECTION; } else if ( elementType.isAssociationType() ) { elementPersistentAttributeType = isManyToMany ? Attribute.PersistentAttributeType.MANY_TO_MANY : Attribute.PersistentAttributeType.ONE_TO_MANY; persistentAttributeType = elementPersistentAttributeType; } else { elementPersistentAttributeType = Attribute.PersistentAttributeType.BASIC; persistentAttributeType = Attribute.PersistentAttributeType.ELEMENT_COLLECTION; } final Attribute.PersistentAttributeType keyPersistentAttributeType; // Finally, we determine the type of the map key (if needed) if ( value instanceof Map ) { final Value keyValue = ( (Map) value ).getIndex(); final org.hibernate.type.Type keyType = keyValue.getType(); if ( keyType.isAnyType() ) { if ( context.isIgnoreUnsupported() ) { return null; } else { throw new UnsupportedOperationException( "collection of any not supported yet" ); } } if ( keyValue instanceof Component ) { keyPersistentAttributeType = Attribute.PersistentAttributeType.EMBEDDED; } else if ( keyType.isAssociationType() ) { keyPersistentAttributeType = Attribute.PersistentAttributeType.MANY_TO_ONE; } else { keyPersistentAttributeType = Attribute.PersistentAttributeType.BASIC; } } else { keyPersistentAttributeType = null; } return new PluralAttributeMetadataImpl( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, persistentAttributeType, elementPersistentAttributeType, keyPersistentAttributeType ); } else if ( value instanceof OneToMany ) { // TODO : is this even possible??? Really OneToMany should be describing the // element value within a o.h.mapping.Collection (see logic branch above) throw new IllegalArgumentException( "HUH???" ); // final boolean isManyToMany = isManyToMany( member ); // //one to many with FK => entity // return new PluralAttributeMetadataImpl( // attributeContext.getPropertyMapping(), // attributeContext.getOwnerType(), // member, // isManyToMany // ? Attribute.PersistentAttributeType.MANY_TO_MANY // : Attribute.PersistentAttributeType.ONE_TO_MANY // value, // AttributeContext.TypeStatus.ENTITY, // Attribute.PersistentAttributeType.ONE_TO_MANY, // null, null, null // ); } } else if ( attributeContext.getPropertyMapping().isComposite() ) { // component return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, Attribute.PersistentAttributeType.EMBEDDED ); } else { // basic type return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, Attribute.PersistentAttributeType.BASIC ); } throw new UnsupportedOperationException( "oops, we are missing something: " + attributeContext.getPropertyMapping() ); } public static Attribute.PersistentAttributeType determineSingularAssociationAttributeType(Member member) { if ( Field.class.isInstance( member ) ) { return ( (Field) member ).getAnnotation( OneToOne.class ) != null ? Attribute.PersistentAttributeType.ONE_TO_ONE : Attribute.PersistentAttributeType.MANY_TO_ONE; } else if ( MapMember.class.isInstance( member ) ) { return Attribute.PersistentAttributeType.MANY_TO_ONE; // curious to see how this works for non-annotated methods } else { return ( (Method) member ).getAnnotation( OneToOne.class ) != null ? Attribute.PersistentAttributeType.ONE_TO_ONE : Attribute.PersistentAttributeType.MANY_TO_ONE; } } private abstract class BaseAttributeMetadata<X, Y> implements AttributeMetadata<X, Y> { private final Property propertyMapping; private final AbstractManagedType<X> ownerType; private final Member member; private final Class<Y> javaType; private final Attribute.PersistentAttributeType persistentAttributeType; @SuppressWarnings({"unchecked"}) protected BaseAttributeMetadata( Property propertyMapping, AbstractManagedType<X> ownerType, Member member, Attribute.PersistentAttributeType persistentAttributeType) { this.propertyMapping = propertyMapping; this.ownerType = ownerType; this.member = member; this.persistentAttributeType = persistentAttributeType; final Class declaredType; if ( member == null ) { // assume we have a MAP entity-mode "class" declaredType = propertyMapping.getType().getReturnedClass(); } else if ( Field.class.isInstance( member ) ) { declaredType = ( (Field) member ).getType(); } else if ( Method.class.isInstance( member ) ) { declaredType = ( (Method) member ).getReturnType(); } else if ( MapMember.class.isInstance( member ) ) { declaredType = ( (MapMember) member ).getType(); } else { throw new IllegalArgumentException( "Cannot determine java-type from given member [" + member + "]" ); } this.javaType = accountForPrimitiveTypes( declaredType ); } public String getName() { return propertyMapping.getName(); } public Member getMember() { return member; } public String getMemberDescription() { return determineMemberDescription( getMember() ); } public String determineMemberDescription(Member member) { return member.getDeclaringClass().getName() + '#' + member.getName(); } public Class<Y> getJavaType() { return javaType; } public Attribute.PersistentAttributeType getPersistentAttributeType() { return persistentAttributeType; } public AbstractManagedType<X> getOwnerType() { return ownerType; } public boolean isPlural() { return propertyMapping.getType().isCollectionType(); } public Property getPropertyMapping() { return propertyMapping; } } @SuppressWarnings({"unchecked"}) protected <Y> Class<Y> accountForPrimitiveTypes(Class<Y> declaredType) { // if ( !declaredType.isPrimitive() ) { // return declaredType; // } // // if ( Boolean.TYPE.equals( declaredType ) ) { // return (Class<Y>) Boolean.class; // } // if ( Character.TYPE.equals( declaredType ) ) { // return (Class<Y>) Character.class; // } // if( Byte.TYPE.equals( declaredType ) ) { // return (Class<Y>) Byte.class; // } // if ( Short.TYPE.equals( declaredType ) ) { // return (Class<Y>) Short.class; // } // if ( Integer.TYPE.equals( declaredType ) ) { // return (Class<Y>) Integer.class; // } // if ( Long.TYPE.equals( declaredType ) ) { // return (Class<Y>) Long.class; // } // if ( Float.TYPE.equals( declaredType ) ) { // return (Class<Y>) Float.class; // } // if ( Double.TYPE.equals( declaredType ) ) { // return (Class<Y>) Double.class; // } // // throw new IllegalArgumentException( "Unexpected type [" + declaredType + "]" ); // if the field is defined as int, return int not Integer... return declaredType; } private class SingularAttributeMetadataImpl<X, Y> extends BaseAttributeMetadata<X, Y> implements SingularAttributeMetadata<X, Y> { private final ValueContext valueContext; private SingularAttributeMetadataImpl( Property propertyMapping, AbstractManagedType<X> ownerType, Member member, Attribute.PersistentAttributeType persistentAttributeType) { super( propertyMapping, ownerType, member, persistentAttributeType ); valueContext = new ValueContext() { public Value getValue() { return getPropertyMapping().getValue(); } public Class getBindableType() { return getAttributeMetadata().getJavaType(); } public ValueClassification getValueClassification() { switch ( getPersistentAttributeType() ) { case EMBEDDED: { return ValueClassification.EMBEDDABLE; } case BASIC: { return ValueClassification.BASIC; } default: { return ValueClassification.ENTITY; } } } public AttributeMetadata getAttributeMetadata() { return SingularAttributeMetadataImpl.this; } }; } public ValueContext getValueContext() { return valueContext; } } private class PluralAttributeMetadataImpl<X, Y, E> extends BaseAttributeMetadata<X, Y> implements PluralAttributeMetadata<X, Y, E> { private final PluralAttribute.CollectionType attributeCollectionType; private final Attribute.PersistentAttributeType elementPersistentAttributeType; private final Attribute.PersistentAttributeType keyPersistentAttributeType; private final Class elementJavaType; private final Class keyJavaType; private final ValueContext elementValueContext; private final ValueContext keyValueContext; private PluralAttributeMetadataImpl( Property propertyMapping, AbstractManagedType<X> ownerType, Member member, Attribute.PersistentAttributeType persistentAttributeType, Attribute.PersistentAttributeType elementPersistentAttributeType, Attribute.PersistentAttributeType keyPersistentAttributeType) { super( propertyMapping, ownerType, member, persistentAttributeType ); this.attributeCollectionType = determineCollectionType( getJavaType() ); this.elementPersistentAttributeType = elementPersistentAttributeType; this.keyPersistentAttributeType = keyPersistentAttributeType; ParameterizedType signatureType = getSignatureType( member ); if ( keyPersistentAttributeType == null ) { elementJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) : Object.class; //FIXME and honor targetEntity? keyJavaType = null; } else { keyJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) : Object.class; //FIXME and honor targetEntity? elementJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[1] ) : Object.class; //FIXME and honor targetEntity? } this.elementValueContext = new ValueContext() { public Value getValue() { return ( (Collection) getPropertyMapping().getValue() ).getElement(); } public Class getBindableType() { return elementJavaType; } public ValueClassification getValueClassification() { switch ( PluralAttributeMetadataImpl.this.elementPersistentAttributeType ) { case EMBEDDED: { return ValueClassification.EMBEDDABLE; } case BASIC: { return ValueClassification.BASIC; } default: { return ValueClassification.ENTITY; } } } public AttributeMetadata getAttributeMetadata() { return PluralAttributeMetadataImpl.this; } }; // interpret the key, if one if ( keyPersistentAttributeType != null ) { this.keyValueContext = new ValueContext() { public Value getValue() { return ( (Map) getPropertyMapping().getValue() ).getIndex(); } public Class getBindableType() { return keyJavaType; } public ValueClassification getValueClassification() { switch ( PluralAttributeMetadataImpl.this.keyPersistentAttributeType ) { case EMBEDDED: { return ValueClassification.EMBEDDABLE; } case BASIC: { return ValueClassification.BASIC; } default: { return ValueClassification.ENTITY; } } } public AttributeMetadata getAttributeMetadata() { return PluralAttributeMetadataImpl.this; } }; } else { keyValueContext = null; } } private Class<?> getClassFromGenericArgument(java.lang.reflect.Type type) { if ( type instanceof Class ) { return (Class) type; } else if ( type instanceof TypeVariable ) { final java.lang.reflect.Type upperBound = ( (TypeVariable) type ).getBounds()[0]; return getClassFromGenericArgument( upperBound ); } else if ( type instanceof ParameterizedType ) { final java.lang.reflect.Type rawType = ( (ParameterizedType) type ).getRawType(); return getClassFromGenericArgument( rawType ); } else { throw new AssertionFailure( "Fail to process type argument in a generic declaration. Member : " + getMemberDescription() + " Type: " + type.getClass() ); } } public ValueContext getElementValueContext() { return elementValueContext; } public PluralAttribute.CollectionType getAttributeCollectionType() { return attributeCollectionType; } public ValueContext getMapKeyValueContext() { return keyValueContext; } } public static ParameterizedType getSignatureType(Member member) { final java.lang.reflect.Type type; if ( Field.class.isInstance( member ) ) { type = ( (Field) member ).getGenericType(); } else if ( Method.class.isInstance( member ) ) { type = ( (Method) member ).getGenericReturnType(); } else { type = ( (MapMember) member ).getType(); } //this is a raw type if ( type instanceof Class ) { return null; } return (ParameterizedType) type; } public static PluralAttribute.CollectionType determineCollectionType(Class javaType) { if ( java.util.List.class.isAssignableFrom( javaType ) ) { return PluralAttribute.CollectionType.LIST; } else if ( java.util.Set.class.isAssignableFrom( javaType ) ) { return PluralAttribute.CollectionType.SET; } else if ( java.util.Map.class.isAssignableFrom( javaType ) ) { return PluralAttribute.CollectionType.MAP; } else if ( java.util.Collection.class.isAssignableFrom( javaType ) ) { return PluralAttribute.CollectionType.COLLECTION; } else { throw new IllegalArgumentException( "Expecting collection type [" + javaType.getName() + "]" ); } } public static boolean isManyToMany(Member member) { if ( Field.class.isInstance( member ) ) { return ( (Field) member ).getAnnotation( ManyToMany.class ) != null; } else if ( Method.class.isInstance( member ) ) { return ( (Method) member ).getAnnotation( ManyToMany.class ) != null; } return false; } private final MemberResolver embeddedMemberResolver = new MemberResolver() { @Override public Member resolveMember(AttributeContext attributeContext) { final EmbeddableTypeImpl embeddableType = (EmbeddableTypeImpl<?>) attributeContext.getOwnerType(); final String attributeName = attributeContext.getPropertyMapping().getName(); final Getter getter = embeddableType.getHibernateType() .getComponentTuplizer() .getGetter( embeddableType.getHibernateType().getPropertyIndex( attributeName ) ); return MapAccessor.MapGetter.class.isInstance( getter ) ? new MapMember( attributeName, attributeContext.getPropertyMapping().getType().getReturnedClass() ) : getter.getMember(); } }; private final MemberResolver virtualIdentifierMemberResolver = new MemberResolver() { @Override public Member resolveMember(AttributeContext attributeContext) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType ); if ( !entityMetamodel.getIdentifierProperty().isVirtual() ) { throw new IllegalArgumentException( "expecting IdClass mapping" ); } org.hibernate.type.Type type = entityMetamodel.getIdentifierProperty().getType(); if ( !EmbeddedComponentType.class.isInstance( type ) ) { throw new IllegalArgumentException( "expecting IdClass mapping" ); } final EmbeddedComponentType componentType = (EmbeddedComponentType) type; final String attributeName = attributeContext.getPropertyMapping().getName(); final Getter getter = componentType.getComponentTuplizer() .getGetter( componentType.getPropertyIndex( attributeName ) ); return MapAccessor.MapGetter.class.isInstance( getter ) ? new MapMember( attributeName, attributeContext.getPropertyMapping().getType().getReturnedClass() ) : getter.getMember(); } };
A Member resolver for normal attributes.
/** * A {@link Member} resolver for normal attributes. */
private final MemberResolver normalMemberResolver = new MemberResolver() { @Override public Member resolveMember(AttributeContext attributeContext) { final AbstractManagedType ownerType = attributeContext.getOwnerType(); final Property property = attributeContext.getPropertyMapping(); final Type.PersistenceType persistenceType = ownerType.getPersistenceType(); if ( Type.PersistenceType.EMBEDDABLE == persistenceType ) { return embeddedMemberResolver.resolveMember( attributeContext ); } else if ( Type.PersistenceType.ENTITY == persistenceType || Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) ownerType; final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType ); final String propertyName = property.getName(); final Integer index = entityMetamodel.getPropertyIndexOrNull( propertyName ); if ( index == null ) { // just like in #determineIdentifierJavaMember , this *should* indicate we have an IdClass mapping return virtualIdentifierMemberResolver.resolveMember( attributeContext ); } else { final Getter getter = entityMetamodel.getTuplizer().getGetter( index ); return MapAccessor.MapGetter.class.isInstance( getter ) ? new MapMember( propertyName, property.getType().getReturnedClass() ) : getter.getMember(); } } else { throw new IllegalArgumentException( "Unexpected owner type : " + persistenceType ); } } }; private final MemberResolver identifierMemberResolver = new MemberResolver() { @Override public Member resolveMember(AttributeContext attributeContext) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType ); if ( !attributeContext.getPropertyMapping().getName() .equals( entityMetamodel.getIdentifierProperty().getName() ) ) { // this *should* indicate processing part of an IdClass... return virtualIdentifierMemberResolver.resolveMember( attributeContext ); } final Getter getter = entityMetamodel.getTuplizer().getIdentifierGetter(); return MapAccessor.MapGetter.class.isInstance( getter ) ? new MapMember( entityMetamodel.getIdentifierProperty().getName(), entityMetamodel.getIdentifierProperty().getType().getReturnedClass() ) : getter.getMember(); } }; private final MemberResolver versionMemberResolver = new MemberResolver() { @Override public Member resolveMember(AttributeContext attributeContext) { final AbstractIdentifiableType identifiableType = (AbstractIdentifiableType) attributeContext.getOwnerType(); final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType ); final String versionPropertyName = attributeContext.getPropertyMapping().getName(); if ( !versionPropertyName.equals( entityMetamodel.getVersionProperty().getName() ) ) { // this should never happen, but to be safe... throw new IllegalArgumentException( "Given property did not match declared version property" ); } final Getter getter = entityMetamodel.getTuplizer().getVersionGetter(); return MapAccessor.MapGetter.class.isInstance( getter ) ? new MapMember( versionPropertyName, attributeContext.getPropertyMapping().getType().getReturnedClass() ) : getter.getMember(); } }; }