[hibernate-commits] Hibernate SVN: r18310 - in core/trunk/entitymanager/src: test/java/org/hibernate/ejb/metamodel and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Dec 21 18:34:11 EST 2009


Author: steve.ebersole at jboss.com
Date: 2009-12-21 18:34:10 -0500 (Mon, 21 Dec 2009)
New Revision: 18310

Added:
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/JoinedManyToOneOwner.java
Modified:
   core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractManagedType.java
   core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/EmbeddedTypeTest.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/Info.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java
   core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/StaticMetadataTest.java
Log:
HHH-4720 - Improve javax.persistence.metamodel.Attribute support


Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractManagedType.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractManagedType.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AbstractManagedType.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -218,6 +218,9 @@
 			String name,
 			Class<Y> javaType) {
 		if ( attribute == null || ( javaType != null && !attribute.getBindableJavaType().equals( javaType ) ) ) {
+			if ( isPrimitiveVariant( attribute, javaType ) ) {
+				return;
+			}
 			throw new IllegalArgumentException(
 					attributeType + " named " + name
 					+ ( javaType != null ? " and of type " + javaType.getName() : "" )
@@ -226,6 +229,38 @@
 		}
 	}
 
+	@SuppressWarnings({ "SimplifiableIfStatement" })
+	protected <Y> boolean isPrimitiveVariant(SingularAttribute<?,?> attribute, Class<Y> javaType) {
+		if ( attribute == null ) {
+			return false;
+		}
+		Class declaredType = attribute.getBindableJavaType();
+
+		if ( declaredType.isPrimitive() ) {
+			return ( Boolean.class.equals( javaType ) && Boolean.TYPE.equals( declaredType ) )
+					|| ( Character.class.equals( javaType ) && Character.TYPE.equals( declaredType ) )
+					|| ( Byte.class.equals( javaType ) && Byte.TYPE.equals( declaredType ) )
+					|| ( Short.class.equals( javaType ) && Short.TYPE.equals( declaredType ) )
+					|| ( Integer.class.equals( javaType ) && Integer.TYPE.equals( declaredType ) )
+					|| ( Long.class.equals( javaType ) && Long.TYPE.equals( javaType ) )
+					|| ( Float.class.equals( javaType ) && Float.TYPE.equals( declaredType ) )
+					|| ( Double.class.equals( javaType ) && Double.TYPE.equals( declaredType ) );
+		}
+
+		if ( javaType.isPrimitive() ) {
+			return ( Boolean.class.equals( declaredType ) && Boolean.TYPE.equals( javaType ) )
+					|| ( Character.class.equals( declaredType ) && Character.TYPE.equals( javaType ) )
+					|| ( Byte.class.equals( declaredType ) && Byte.TYPE.equals( javaType ) )
+					|| ( Short.class.equals( declaredType ) && Short.TYPE.equals( javaType ) )
+					|| ( Integer.class.equals( declaredType ) && Integer.TYPE.equals( javaType ) )
+					|| ( Long.class.equals( declaredType ) && Long.TYPE.equals( javaType ) )
+					|| ( Float.class.equals( declaredType ) && Float.TYPE.equals( javaType ) )
+					|| ( Double.class.equals( declaredType ) && Double.TYPE.equals( javaType ) );
+		}
+
+		return false;
+	}
+
 	/**
 	 * {@inheritDoc}
 	 */

Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/metamodel/AttributeFactory.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -26,8 +26,12 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
 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 javax.persistence.metamodel.IdentifiableType;
 
@@ -43,9 +47,14 @@
 import org.hibernate.mapping.Value;
 import org.hibernate.mapping.PersistentClass;
 import org.hibernate.tuple.entity.EntityMetamodel;
+import org.hibernate.type.EntityType;
 
 /**
- * TODO : javadoc
+ * 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
@@ -57,157 +66,169 @@
 		this.context = context;
 	}
 
+	/**
+	 * 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
+	 */
 	@SuppressWarnings({ "unchecked" })
 	public <X, Y> AttributeImplementor<X, Y> buildAttribute(AbstractManagedType<X> ownerType, Property property) {
-		AttributeContext attrContext = getAttributeContext( property );
-		final AttributeImplementor<X, Y> attribute;
-		if ( attrContext.isCollection() ) {
-			attribute = buildPluralAttribute( ownerType, property, attrContext);
+		final AttributeContext<X> attributeContext = wrap( ownerType, property );
+		final AttributeMetadata<X,Y> attributeMetadata =
+				determineAttributeMetadata( attributeContext, NORMAL_MEMBER_RESOLVER );
+
+		if ( attributeMetadata.isPlural() ) {
+			return buildPluralAttribute( (PluralAttributeMetadata) attributeMetadata );
 		}
 		else {
-			final Type<Y> attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() );
-			attribute = new SingularAttributeImpl<X,Y>(
-					property.getName(),
-					property.getType().getReturnedClass(),
+			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,
-					determineStandardJavaMember( ownerType, property ),
+					attributeMetadata.getMember(),
 					false,
 					false,
 					property.isOptional(),
-					attrType,
-					attrContext.getElementAttributeType()
+					metaModelType,
+					attributeMetadata.getPersistentAttributeType()
 			);
 		}
-		return attribute;
 	}
 
-	@SuppressWarnings( "unchecked" )
-	private <X, Y, V, K> AttributeImplementor<X, Y> buildPluralAttribute(AbstractManagedType<X> ownerType, Property property, AttributeContext attrContext) {
-		AttributeImplementor<X, Y> attribute;
-		final Type<V> attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() );
-		final Member member = determineStandardJavaMember( ownerType, property );
-		final Class<Y> collectionClass = (Class<Y>) ( member instanceof Field
-				? ( ( Field ) member ).getType()
-				: ( ( Method ) member ).getReturnType() );
-		if ( java.util.Map.class.isAssignableFrom( collectionClass ) ) {
-			final Type<K> keyType = getType( ownerType, attrContext.getKeyTypeStatus(), attrContext.getKeyValue() );
-			attribute = PluralAttributeImpl.create( ownerType, attrType, collectionClass, keyType )
-					.member( member )
-					.property( property )
-					.persistentAttributeType( attrContext.getElementAttributeType() )
-					.build();
-		}
-		else {
-			attribute =  PluralAttributeImpl.create( ownerType, attrType, collectionClass, null )
-					.member( member )
-					.property( property )
-					.persistentAttributeType( attrContext.getElementAttributeType() )
-					.build();
-		}
-		return attribute;
-	}
+	private <X> AttributeContext<X> wrap(final AbstractManagedType<X> ownerType, final Property property) {
+		return new AttributeContext<X>() {
+			public AbstractManagedType<X> getOwnerType() {
+				return ownerType;
+			}
 
-	private <X> Type<X> getType(AbstractManagedType owner, AttributeContext.TypeStatus elementTypeStatus, Value value) {
-		final org.hibernate.type.Type type = value.getType();
-		switch ( elementTypeStatus ) {
-			case BASIC:
-				return buildBasicType( type );
-			case EMBEDDABLE:
-				return buildEmbeddableType( owner, value, type );
-			case ENTITY:
-				return buildEntityType( type );
-			default:
-				throw new AssertionFailure( "Unknown AttributeContext.TypeStatus: " + elementTypeStatus );
-
-		}
+			public Property getPropertyMapping() {
+				return property;
+			}
+		};
 	}
 
-	@SuppressWarnings( "unchecked" )
-	private <X> Type<X> buildBasicType(org.hibernate.type.Type type) {
-		final Class<X> clazz = type.getReturnedClass();
-		return new BasicTypeImpl<X>( clazz, Type.PersistenceType.BASIC );
-	}
-
-	@SuppressWarnings( "unchecked" )
-	private <X> Type<X> buildEntityType(org.hibernate.type.Type type) {
-		String entityName = ( (org.hibernate.type.EntityType) type ).getAssociatedEntityName();
-		return ( Type<X> ) context.locateEntityType( entityName );
-	}
-
-	@SuppressWarnings( "unchecked" )
-	private <X> Type<X> buildEmbeddableType(AbstractManagedType owner, Value value, org.hibernate.type.Type type) {
-		//build embedable type
-		final Class<X> clazz = type.getReturnedClass();
-		final EmbeddableTypeImpl<X> embeddableType = new EmbeddableTypeImpl<X>( clazz, owner, (ComponentType) type );
-		context.registerEmbeddedableType( embeddableType );
-		final Component component = (Component) value;
-		final Iterator<Property> subProperties = component.getPropertyIterator();
-		while ( subProperties.hasNext() ) {
-			final Property property = subProperties.next();
-			embeddableType.getBuilder().addAttribute( buildAttribute( embeddableType, property) );
-		}
-		embeddableType.lock();
-		return embeddableType;
-	}
-
+	/**
+	 * 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) {
-		final AttributeContext attrContext = getAttributeContext( property );
-		final Type<Y> attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() );
-		final Class<Y> idJavaType = property.getType().getReturnedClass();
+		final AttributeContext<X> attributeContext = wrap( ownerType, property );
+		final SingularAttributeMetadata<X,Y> attributeMetadata =
+				(SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, IDENTIFIER_MEMBER_RESOLVER );
+		final Type<Y> metaModelType = getMetaModelType( attributeMetadata.getValueContext() );
 		return new SingularAttributeImpl.Identifier(
 				property.getName(),
-				idJavaType,
+				attributeMetadata.getJavaType(),
 				ownerType,
-				determineIdentifierJavaMember( ownerType, property ),
-				attrType,
-				attrContext.getElementAttributeType()
+				attributeMetadata.getMember(),
+				metaModelType,
+				attributeMetadata.getPersistentAttributeType()
 		);
 	}
 
-	private Member determineIdentifierJavaMember(IdentifiableType ownerType, Property property) {
-// see below
-//		final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( property );
-		final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( ownerType );
-		if ( ! property.getName().equals( entityMetamodel.getIdentifierProperty().getName() ) ) {
-			// this *should* indicate processing part of an IdClass...
-			return determineVirtualIdentifierJavaMember( entityMetamodel, property );
-		}
-		return entityMetamodel.getTuplizer( EntityMode.POJO ).getIdentifierGetter().getMember();
+	/**
+	 * 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) {
+		final AttributeContext<X> attributeContext = wrap( ownerType, property );
+		final SingularAttributeMetadata<X,Y> attributeMetadata =
+				(SingularAttributeMetadata<X, Y>) determineAttributeMetadata( attributeContext, VERSION_MEMBER_RESOLVER );
+		final Type<Y> metaModelType = getMetaModelType( attributeMetadata.getValueContext() );
+		return new SingularAttributeImpl.Version(
+				property.getName(),
+				attributeMetadata.getJavaType(),
+				ownerType,
+				attributeMetadata.getMember(),
+				metaModelType,
+				attributeMetadata.getPersistentAttributeType()
+		);
 	}
 
-	private Member determineVirtualIdentifierJavaMember(EntityMetamodel entityMetamodel, Property property) {
-		if ( ! entityMetamodel.getIdentifierProperty().isVirtual() ) {
-			throw new IllegalArgumentException( "expecting IdClass mapping" );
+	@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();
 		}
-		org.hibernate.type.Type type = entityMetamodel.getIdentifierProperty().getType();
-		if ( ! EmbeddedComponentType.class.isInstance( type ) ) {
-			throw new IllegalArgumentException( "expecting IdClass mapping" );
+		else {
+			return PluralAttributeImpl.create( attributeMetadata.getOwnerType(), elementType, attributeMetadata.getJavaType(), null )
+					.member( attributeMetadata.getMember() )
+					.property( attributeMetadata.getPropertyMapping() )
+					.persistentAttributeType( attributeMetadata.getPersistentAttributeType() )
+					.build();
 		}
-		final EmbeddedComponentType componentType = (EmbeddedComponentType) type;
-		return componentType.getTuplizerMapping()
-				.getTuplizer( EntityMode.POJO )
-				.getGetter( componentType.getPropertyIndex( property.getName() ) )
-				.getMember();
 	}
 
-// getting the owning PersistentClass from the Property is broken in certain cases with annotations...
-//	private EntityMetamodel getDeclarerEntityMetamodel(Property property) {
-//		return context.getSessionFactory()
-//				.getEntityPersister( property.getPersistentClass().getEntityName() )
-//				.getEntityMetamodel();
-//	}
-// so we use the owner's java class to lookup the persister/entitymetamodel
+	@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();
+					embeddableType.getBuilder().addAttribute( buildAttribute( embeddableType, property) );
+				}
+				embeddableType.lock();
+				return embeddableType;
+			}
+			default: {
+				throw new AssertionFailure( "Unknown type : " + typeContext.getValueClassification() );
+			}
+		}
+	}
+
 	private EntityMetamodel getDeclarerEntityMetamodel(IdentifiableType<?> ownerType) {
-	final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
-	if ( persistenceType == Type.PersistenceType.ENTITY) {
+		final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
+		if ( persistenceType == Type.PersistenceType.ENTITY) {
 			return context.getSessionFactory()
 					.getEntityPersister( ownerType.getJavaType().getName() )
 					.getEntityMetamodel();
 		}
 		else if ( persistenceType == Type.PersistenceType.MAPPED_SUPERCLASS) {
 			PersistentClass persistentClass =
-					context.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType );	
+					context.getPersistentClassHostingProperties( (MappedSuperclassTypeImpl<?>) ownerType );
 			return context.getSessionFactory()
 				.getEntityPersister( persistentClass.getClassName() )
 				.getEntityMetamodel();
@@ -217,243 +238,708 @@
 		}
 	}
 
-
-// getting the owning PersistentClass from the Property is broken in certain cases with annotations...
-//	private Member determineStandardJavaMember(Property property) {
-//		final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( property );
-//
-//		final String propertyName = property.getName();
-//		final int index = entityMetamodel.getPropertyIndex( propertyName );
-//		return entityMetamodel.getTuplizer( EntityMode.POJO ).getGetter( index ).getMember();
-//	}
-// so we use the owner's java class to lookup the persister/entitymetamodel
-	private Member determineStandardJavaMember(AbstractManagedType<?> ownerType, Property property) {
-	final Type.PersistenceType persistenceType = ownerType.getPersistenceType();
-	if ( Type.PersistenceType.EMBEDDABLE == persistenceType ) {
-			EmbeddableTypeImpl embeddableType = ( EmbeddableTypeImpl<?> ) ownerType;
-			return embeddableType.getHibernateType().getTuplizerMapping()
-					.getTuplizer( EntityMode.POJO )
-					.getGetter( embeddableType.getHibernateType().getPropertyIndex( property.getName() ) )
-					.getMember();
+	/**
+	 * 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 ValueClassification {
+			EMBEDDABLE,
+			ENTITY,
+			BASIC
 		}
-		else if ( Type.PersistenceType.ENTITY == persistenceType
-			|| Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) {
-			return determineStandardJavaMemberOutOfIdentifiableType( (IdentifiableType<?>) ownerType, property );
-		}
-		else {
-			throw new IllegalArgumentException( "Unexpected owner type : " + persistenceType );
-		}
-	}
 
-	private Member determineStandardJavaMemberOutOfIdentifiableType(IdentifiableType<?> ownerType, Property property) {
-		final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( ownerType );
-		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 determineVirtualIdentifierJavaMember( entityMetamodel, property );
-		}
-		else {
-			return entityMetamodel.getTuplizer( EntityMode.POJO ).getGetter( index ).getMember();
-		}
-	}
+		/**
+		 * Retrieve the value itself
+		 *
+		 * @return The value
+		 */
+		public Value getValue();
 
-	@SuppressWarnings({ "unchecked" })
-	public <X, Y> SingularAttributeImpl<X, Y> buildVersionAttribute(AbstractIdentifiableType<X> ownerType, Property property) {
-		final AttributeContext attrContext = getAttributeContext( property );
-		final Class<Y> javaType = property.getType().getReturnedClass();
-		final Type<Y> attrType = getType( ownerType, attrContext.getElementTypeStatus(), attrContext.getElementValue() );
-		return new SingularAttributeImpl.Version(
-				property.getName(),
-				javaType,
-				ownerType,
-				determineVersionJavaMember( ownerType, property ),
-				attrType,
-				attrContext.getElementAttributeType()
-		);
-	}
+		public Class getBindableType();
 
-	private Member determineVersionJavaMember(IdentifiableType ownerType, Property property) {
-		final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( ownerType );
-		if ( ! property.getName().equals( entityMetamodel.getVersionProperty().getName() ) ) {
-			// this should never happen, but to be safe...
-			throw new IllegalArgumentException( "Given property did not match declared version property" );
-		}
-		return entityMetamodel.getTuplizer( EntityMode.POJO ).getVersionGetter().getMember();
+		/**
+		 * Retrieve the simplified value classification
+		 *
+		 * @return The value type
+		 */
+		public ValueClassification getValueClassification();
+
+		/**
+		 * Retrieve the metadata about the attribute from which this value comes
+		 *
+		 * @return The "containing" attribute metadata.
+		 */
+		public AttributeMetadata getAttributeMetadata();
 	}
 
-	private static class AttributeContext {
-		private final Value elementValue;
-		private final TypeStatus typeStatus;
-		private final Class<?> collectionClass;
-		private final Attribute.PersistentAttributeType attrType;
-		private final Value keyValue;
-		private final TypeStatus keyTypeStatus;
+	/**
+	 * 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
+		 *
+		 * @return The attribute name
+		 */
+		public String getName();
 
-		enum TypeStatus {
-			EMBEDDABLE,
-			ENTITY,
-			BASIC
-		}
+		/**
+		 * Retrieve the member defining the attribute
+		 *
+		 * @return The attribute member
+		 */
+		public Member getMember();
 
-		private AttributeContext(
-				Value elementValue,
-				TypeStatus elementTypeStatus,
-				Attribute.PersistentAttributeType elementPAT,
-				Class<?> collectionClass,
-				Value keyValue,
-				TypeStatus keyTypeStatus) {
-			this.elementValue = elementValue;
-			this.typeStatus = elementTypeStatus;
-			this.collectionClass = collectionClass;
-			this.attrType = elementPAT;
-			this.keyValue = keyValue;
-			this.keyTypeStatus = keyTypeStatus;
-		}
+		/**
+		 * Retrieve the attribute java type.
+		 *
+		 * @return The java type of the attribute.
+		 */
+		public Class<Y> getJavaType();
 
-		public Value getElementValue() {
-			return elementValue;
-		}
+		/**
+		 * Get the JPA attribute type classification for this attribute.
+		 *
+		 * @return The JPA attribute type classification
+		 */
+		public Attribute.PersistentAttributeType getPersistentAttributeType();
 
-		public TypeStatus getElementTypeStatus() {
-			return typeStatus;
-		}
+		/**
+		 * Retrieve the attribute owner's metamodel information
+		 *
+		 * @return The metamodel information for the attribute owner
+		 */
+		public AbstractManagedType<X> getOwnerType();
 
-		public boolean isCollection() {
-			return collectionClass != null;
-		}
+		/**
+		 * Retrieve the Hibernate property mapping related to this attribute.
+		 *
+		 * @return The Hibernate property mapping
+		 */
+		public Property getPropertyMapping();
 
-		public Attribute.PersistentAttributeType getElementAttributeType() {
-			return attrType;
-		}
+		/**
+		 * Is the attribute plural (a collection)?
+		 *
+		 * @return True if it is plural, false otherwise.
+		 */
+		public boolean isPlural();
+	}
 
-		public Value getKeyValue() {
-			return keyValue;
-		}
+	/**
+	 * 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
+		 *
+		 * @return The attributes value context
+		 */
+		public ValueContext getValueContext();
+	}
 
-		public TypeStatus getKeyTypeStatus() {
-			return keyTypeStatus;
-		}
+	/**
+	 * 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
+	 */
+	private interface PluralAttributeMetadata<X,Y,E> extends AttributeMetadata<X,Y> {
+		/**
+		 * 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.
+		 *
+		 * @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).
+		 *
+		 * @return The value context for the collection's keys (if a map, null otherwise).
+		 */
+		public ValueContext getMapKeyValueContext();
 	}
 
-	private static AttributeContext getAttributeContext(Property property) {
+	/**
+	 * 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.
+		 *
+		 * @return The owner.
+		 */
+		public AbstractManagedType<X> getOwnerType();
+
+		/**
+		 * Retrieve the Hibernate property mapping.
+		 *
+		 * @return The Hibvernate property mapping.
+		 */
+		public Property getPropertyMapping();
+	}
+
+	/**
+	 * 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.
+	 *
+	 * @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) {
+		final Member member = memberResolver.resolveMember( attributeContext );
+		// TODO (steve->emmanuel) not so sure this is true any longer...
 		// FIXME the logical level for *To* is different from the Hibernate physical model.
 		//		ie a @ManyToOne @AssocTable is a many-to-many for hibernate
 		//		and a @OneToMany @AssocTable is a many-to-many for hibernate
 		// FIXME so basically Attribute.PersistentAttributeType is crap at the moment
-		final Value value = property.getValue();
+		final Value value = attributeContext.getPropertyMapping().getValue();
 		final org.hibernate.type.Type type = value.getType();
 		if ( type.isAnyType() ) {
 			throw new UnsupportedOperationException( "any not supported yet" );
 		}
 		else if ( type.isAssociationType() ) {
-			//collection or entity
-			if ( type.isCollectionType() ) {
-				//do collection
+			// collection or entity
+			if ( type.isEntityType() ) {
+				// entity
+				return new SingularAttributeMetadataImpl<X,Y>(
+						attributeContext.getPropertyMapping(),
+						attributeContext.getOwnerType(),
+						member,
+						determineSingularAssociationAttributeType( member )
+				);
+			}
+			else {
+				// collection
 				if ( value instanceof Collection ) {
-					final Collection collValue = ( Collection ) value;
+					final Collection collValue = (Collection) value;
 					final Value elementValue = collValue.getElement();
 					final org.hibernate.type.Type elementType = elementValue.getType();
-					final AttributeContext.TypeStatus elementTypeStatus;
-					final Attribute.PersistentAttributeType elementPAT;
-					final Class<?> collectionClass = collValue.getCollectionType().getReturnedClass();
 
-					final Value keyValue;
-					final org.hibernate.type.Type keyType;
-					final AttributeContext.TypeStatus keyTypeStatus;
+					// 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() ) {
+						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 ) {
-						keyValue = ( ( Map ) value ).getIndex();
-						keyType = keyValue.getType();
+						final Value keyValue = ( ( Map ) value ).getIndex();
+						final org.hibernate.type.Type keyType = keyValue.getType();
+
+						if ( keyType.isAnyType() ) {
+							throw new UnsupportedOperationException( "collection of any not supported yet" );
+						}
 						if ( keyValue instanceof Component ) {
-							keyTypeStatus = AttributeContext.TypeStatus.EMBEDDABLE;
+							keyPersistentAttributeType = Attribute.PersistentAttributeType.EMBEDDED;
 						}
-						else if ( keyType.isAnyType() ) {
-							throw new UnsupportedOperationException( "collection of any not supported yet" );
-						}
 						else if ( keyType.isAssociationType() ) {
-							keyTypeStatus = AttributeContext.TypeStatus.ENTITY;
+							keyPersistentAttributeType = Attribute.PersistentAttributeType.MANY_TO_ONE;
 						}
 						else {
-							keyTypeStatus = AttributeContext.TypeStatus.BASIC;
+							keyPersistentAttributeType = Attribute.PersistentAttributeType.BASIC;
 						}
 					}
 					else {
-						keyValue = null;
-						keyTypeStatus = null;
+						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() );
+	}
 
-					if ( elementValue instanceof Component ) {
-						//collection of components
-						elementTypeStatus = AttributeContext.TypeStatus.EMBEDDABLE;
-						elementPAT = Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
+	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 {
+			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;
+			// we can support method or field members here.  Is there really any other valid type?
+			if ( Field.class.isInstance( member ) ) {
+				declaredType = ( (Field) member ).getType();
+			}
+			else if ( Method.class.isInstance( member ) ) {
+				declaredType = ( (Method) member ).getReturnType();
+			}
+			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 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;
+						}
 					}
-					else if ( elementType.isAnyType() ) {
-						throw new UnsupportedOperationException( "collection of any not supported yet" );
+				}
+
+				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 = (Class) signatureType.getActualTypeArguments()[0];
+				keyJavaType = null;
+			}
+			else {
+				keyJavaType = (Class) signatureType.getActualTypeArguments()[0];
+				elementJavaType = (Class) signatureType.getActualTypeArguments()[1];
+			}
+
+			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;
+						}
 					}
-					else if ( elementType.isAssociationType() ) {
-						//collection of entity
-						elementTypeStatus = AttributeContext.TypeStatus.ENTITY;
-						elementPAT = Attribute.PersistentAttributeType.MANY_TO_MANY;
-					}
-					else {
-						//collection of basic type
-						elementTypeStatus = AttributeContext.TypeStatus.BASIC;
-						elementPAT = Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
-					}
-					return new AttributeContext(
-							elementValue,
-							elementTypeStatus,
-							elementPAT,
-							collectionClass,
-							keyValue,
-							keyTypeStatus
-					);
 				}
-				else if ( value instanceof OneToMany ) {
-					//one to many with FK => entity
-					return new AttributeContext(
-							value,
-							AttributeContext.TypeStatus.ENTITY,
-							Attribute.PersistentAttributeType.ONE_TO_MANY,
-							null, null, null
-					);
+
+				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 {
-				//ToOne association
-				return new AttributeContext(
-						value,
-						AttributeContext.TypeStatus.ENTITY,
-						Attribute.PersistentAttributeType.MANY_TO_MANY,
-						//FIXME how to differentiate the logical many to one from the one to one (not physical level)
-						null,
-						null,
-						null
-				);
+				keyValueContext = null;
 			}
 		}
-		else if ( property.isComposite() ) {
-			//embeddable
-			return new AttributeContext(
-					value,
-					AttributeContext.TypeStatus.EMBEDDABLE,
-					Attribute.PersistentAttributeType.EMBEDDED,
-					null, null, null
-			);
 
+		public ValueContext getElementValueContext() {
+			return elementValueContext;
 		}
+
+		public PluralAttribute.CollectionType getAttributeCollectionType() {
+			return attributeCollectionType;
+		}
+
+		public ValueContext getMapKeyValueContext() {
+			return keyValueContext;
+		}
+	}
+
+	public static ParameterizedType getSignatureType(Member member) {
+		return (ParameterizedType) ( Field.class.isInstance( member )
+						? ( (Field) member ).getGenericType()
+						: ( (Method) member ).getGenericReturnType());
+	}
+
+	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 {
-			//basic type
-			return new AttributeContext(
-					value,
-					AttributeContext.TypeStatus.BASIC,
-					Attribute.PersistentAttributeType.BASIC,
-					null, null, null
-			);
+			throw new IllegalArgumentException( "Expecting collection type [" + javaType.getName() + "]" );
 		}
-		throw new UnsupportedOperationException( "oops, we are missing something: " + property.toString() );
 	}
 
+	public static boolean isManyToMany(Member member) {
+		return Field.class.isInstance( member )
+				? ( (Field) member ).getAnnotation( ManyToMany.class ) != null
+				: ( (Method) member ).getAnnotation( ManyToMany.class ) != null;
+	}
 
+	private final MemberResolver EMBEDDED_MEMBER_RESOLVER = new MemberResolver() {
+		/**
+		 * {@inheritDoc}
+		 */
+		public Member resolveMember(AttributeContext attributeContext) {
+			final EmbeddableTypeImpl embeddableType = ( EmbeddableTypeImpl<?> ) attributeContext.getOwnerType();
+			final String attributeName = attributeContext.getPropertyMapping().getName();
+			return embeddableType.getHibernateType().getTuplizerMapping()
+					.getTuplizer( EntityMode.POJO )
+					.getGetter( embeddableType.getHibernateType().getPropertyIndex( attributeName ) )
+					.getMember();
+		}
+	};
+
+
+	private final MemberResolver VIRTUAL_IDENTIFIER_MEMBER_RESOLVER = new MemberResolver() {
+		/**
+		 * {@inheritDoc}
+		 */
+		public Member resolveMember(AttributeContext attributeContext) {
+			final IdentifiableType identifiableType = (IdentifiableType) 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();
+			return componentType.getTuplizerMapping()
+					.getTuplizer( EntityMode.POJO )
+					.getGetter( componentType.getPropertyIndex( attributeName ) )
+					.getMember();
+		}
+	};
+
+	/**
+	 * A {@link Member} resolver for normal attributes.
+	 */
+	private final MemberResolver NORMAL_MEMBER_RESOLVER = new MemberResolver() {
+		/**
+		 * {@inheritDoc}
+		 */
+		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 EMBEDDED_MEMBER_RESOLVER.resolveMember( attributeContext );
+			}
+			else if ( Type.PersistenceType.ENTITY == persistenceType
+					|| Type.PersistenceType.MAPPED_SUPERCLASS == persistenceType ) {
+				final IdentifiableType identifiableType = (IdentifiableType) 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 VIRTUAL_IDENTIFIER_MEMBER_RESOLVER.resolveMember( attributeContext );
+				}
+				else {
+					return entityMetamodel.getTuplizer( EntityMode.POJO )
+							.getGetter( index )
+							.getMember();
+				}
+			}
+			else {
+				throw new IllegalArgumentException( "Unexpected owner type : " + persistenceType );
+			}
+		}
+	};
+
+	private final MemberResolver IDENTIFIER_MEMBER_RESOLVER = new MemberResolver() {
+		public Member resolveMember(AttributeContext attributeContext) {
+			final IdentifiableType identifiableType = (IdentifiableType) attributeContext.getOwnerType();
+			final EntityMetamodel entityMetamodel = getDeclarerEntityMetamodel( identifiableType );
+			if ( ! attributeContext.getPropertyMapping().getName()
+					.equals( entityMetamodel.getIdentifierProperty().getName() ) ) {
+				// this *should* indicate processing part of an IdClass...
+				return VIRTUAL_IDENTIFIER_MEMBER_RESOLVER.resolveMember( attributeContext );
+			}
+			return entityMetamodel.getTuplizer( EntityMode.POJO ).getIdentifierGetter().getMember();
+		}
+	};
+
+	private final MemberResolver VERSION_MEMBER_RESOLVER = new MemberResolver() {
+		public Member resolveMember(AttributeContext attributeContext) {
+			final IdentifiableType identifiableType = (IdentifiableType) 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" );
+			}
+			return entityMetamodel.getTuplizer( EntityMode.POJO ).getVersionGetter().getMember();
+		}
+	};
 }

Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/EmbeddedTypeTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/EmbeddedTypeTest.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/EmbeddedTypeTest.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -41,7 +41,7 @@
 		};
 	}
 
-	public void testSingularAttributeAccessByNameFailureExpected() {
+	public void testSingularAttributeAccessByName() {
 		// HHH-4702
 		EntityManager em = getOrCreateEntityManager();
 		em.getTransaction().begin();

Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/Info.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/Info.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/metamodel/Info.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -26,6 +26,7 @@
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
+import javax.persistence.JoinTable;
 import javax.persistence.OneToOne;
 import javax.persistence.Table;
 
@@ -112,7 +113,7 @@
 		zip = v;
 	}
 
-	@OneToOne(mappedBy = "info")
+	@OneToOne(mappedBy = "info") @JoinTable
 	public Spouse getSpouse() {
 		return spouse;
 	}

Added: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/JoinedManyToOneOwner.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/JoinedManyToOneOwner.java	                        (rev 0)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/JoinedManyToOneOwner.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -0,0 +1,60 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2009, Red Hat Inc. or third-party contributors as
+ * indicated by the @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.ejb.test.metadata;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToOne;
+
+/**
+ * An entity that defines a @ManyToOne @JoinTable
+ * <p/>
+ * See HHH-4720 for details
+ *
+ * @author Steve Ebersole
+ */
+ at Entity
+public class JoinedManyToOneOwner {
+	private Long id;
+	private House house;
+
+	@Id
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	@ManyToOne @JoinTable( name = "SOME_OTHER_TABLE" )
+	public House getHouse() {
+		return house;
+	}
+
+	public void setHouse(House house) {
+		this.house = house;
+	}
+}

Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/MetadataTest.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -36,7 +36,6 @@
 import javax.persistence.metamodel.MapAttribute;
 import javax.persistence.metamodel.ListAttribute;
 import javax.persistence.metamodel.MappedSuperclassType;
-import javax.persistence.metamodel.CollectionAttribute;
 import javax.persistence.metamodel.IdentifiableType;
 
 import org.hibernate.ejb.test.TestCase;
@@ -53,11 +52,23 @@
 		assertNotNull( entityType );
 	}
 
+	public void testLogicalManyToOne() throws Exception {
+		final EntityType<JoinedManyToOneOwner> entityType = factory.getMetamodel().entity( JoinedManyToOneOwner.class );
+		final SingularAttribute attr = entityType.getDeclaredSingularAttribute( "house" );
+		assertEquals( Attribute.PersistentAttributeType.MANY_TO_ONE, attr.getPersistentAttributeType() );
+		assertEquals( House.class, attr.getBindableJavaType() );
+		final EntityType<House> houseType = factory.getMetamodel().entity( House.class );
+		assertEquals( houseType.getBindableJavaType(), attr.getBindableJavaType() );
+	}
+
 	public void testEntity() throws Exception {
 		final EntityType<Fridge> fridgeType = factory.getMetamodel().entity( Fridge.class );
 		assertEquals( Fridge.class, fridgeType.getBindableJavaType() );
 		assertEquals( Bindable.BindableType.ENTITY_TYPE, fridgeType.getBindableType() );
-		assertNotNull( fridgeType.getDeclaredSingularAttribute( "temperature", Integer.class ) );
+		SingularAttribute<Fridge,Integer> wrapped = fridgeType.getDeclaredSingularAttribute( "temperature", Integer.class );
+		assertNotNull( wrapped );
+		SingularAttribute<Fridge,Integer> primitive = fridgeType.getDeclaredSingularAttribute( "temperature", int.class );
+		assertNotNull( primitive );
 		assertNotNull( fridgeType.getDeclaredSingularAttribute( "temperature" ) );
 		assertNotNull( fridgeType.getDeclaredAttribute( "temperature" ) );
 		final SingularAttribute<Fridge, Long> id = fridgeType.getDeclaredId( Long.class );
@@ -112,19 +123,22 @@
 				"temperature",
 				Integer.class
 		);
-		assertEquals( Integer.class, singularAttribute.getBindableJavaType() );
+//		assertEquals( Integer.class, singularAttribute.getBindableJavaType() );
+//		assertEquals( Integer.class, singularAttribute.getType().getJavaType() );
+		assertEquals( int.class, singularAttribute.getBindableJavaType() );
+		assertEquals( int.class, singularAttribute.getType().getJavaType() );
 		assertEquals( Bindable.BindableType.SINGULAR_ATTRIBUTE, singularAttribute.getBindableType() );
 		assertFalse( singularAttribute.isId() );
 		assertFalse( singularAttribute.isOptional() );
 		assertFalse( entityType.getDeclaredSingularAttribute( "brand", String.class ).isOptional() );
-		assertEquals( Integer.class, singularAttribute.getType().getJavaType() );
 		assertEquals( Type.PersistenceType.BASIC, singularAttribute.getType().getPersistenceType() );
 		final Attribute<? super Fridge, ?> attribute = entityType.getDeclaredAttribute( "temperature" );
 		assertNotNull( attribute );
 		assertEquals( "temperature", attribute.getName() );
 		assertEquals( Fridge.class, attribute.getDeclaringType().getJavaType() );
 		assertEquals( Attribute.PersistentAttributeType.BASIC, attribute.getPersistentAttributeType() );
-		assertEquals( Integer.class, attribute.getJavaType() );
+//		assertEquals( Integer.class, attribute.getJavaType() );
+		assertEquals( int.class, attribute.getJavaType() );
 		assertFalse( attribute.isAssociation() );
 		assertFalse( attribute.isCollection() );
 
@@ -306,7 +320,8 @@
 				Cattish.class,
 				Feline.class,
 				Garden.class,
-				Flower.class
+				Flower.class,
+				JoinedManyToOneOwner.class
 		};
 	}
 

Modified: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/StaticMetadataTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/StaticMetadataTest.java	2009-12-21 20:06:42 UTC (rev 18309)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/metadata/StaticMetadataTest.java	2009-12-21 23:34:10 UTC (rev 18310)
@@ -53,7 +53,8 @@
 		assertTrue( Animal_.id.isId() );
 		assertEquals( Long.class, Animal_.id.getJavaType() );
 		assertNotNull( Animal_.legNbr );
-		assertEquals( Integer.class, Animal_.legNbr.getJavaType() );
+//		assertEquals( Integer.class, Animal_.legNbr.getJavaType() );
+		assertEquals( int.class, Animal_.legNbr.getJavaType() );
 
 		// Cat (hierarchy)
 		assertNotNull( Cat_.id );
@@ -72,9 +73,12 @@
 		assertNotNull( Fridge_.temperature );
 		assertEquals( "temperature", Fridge_.temperature.getName() );
 		assertEquals( Fridge.class, Fridge_.temperature.getDeclaringType().getJavaType() );
-		assertEquals( Integer.class, Fridge_.temperature.getJavaType() );
-		assertEquals( Integer.class, Fridge_.temperature.getBindableJavaType() );
-		assertEquals( Integer.class, Fridge_.temperature.getType().getJavaType() );
+//		assertEquals( Integer.class, Fridge_.temperature.getJavaType() );
+//		assertEquals( Integer.class, Fridge_.temperature.getBindableJavaType() );
+//		assertEquals( Integer.class, Fridge_.temperature.getType().getJavaType() );
+		assertEquals( int.class, Fridge_.temperature.getJavaType() );
+		assertEquals( int.class, Fridge_.temperature.getBindableJavaType() );
+		assertEquals( int.class, Fridge_.temperature.getType().getJavaType() );
 		assertEquals( Bindable.BindableType.SINGULAR_ATTRIBUTE, Fridge_.temperature.getBindableType() );
 		assertEquals( Type.PersistenceType.BASIC, Fridge_.temperature.getType().getPersistenceType() );
 		assertEquals( Attribute.PersistentAttributeType.BASIC, Fridge_.temperature.getPersistentAttributeType() );



More information about the hibernate-commits mailing list