Author: nusco Date: 2006-03-13 11:20:09 -0500 (Mon, 13 Mar 2006) New Revision: 9612 Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/DNA.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/Gene.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/UnresolvedTypeTest.java Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/XClass.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXArrayType.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXClass.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXCollectionType.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXSimpleType.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXType.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/ApproximatingTypeEnvironment.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/SimpleTypeEnvironment.java trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/TypeUtils.java trunk/HibernateExt/metadata/src/java/org/hibernate/validator/ClassValidator.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/TestCase.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/JavaXClassTest.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/BigBlob.java Log: Accepts unresolved types where a target entity is explicitly set Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -869,14 +869,26 @@ log.debug( "Processing " + propertyHolder.getEntityName() + " " + accessType + " annotation" ); List<XProperty> properties = annotatedClass.getDeclaredProperties(accessType); for ( XProperty p : properties ) { - if( !p.isTypeResolved() ) - throw new IllegalStateException( "Couldn't bind the type for property " + p ); + if( !p.isTypeResolved() && !hasExplicitTargetEntity( p ) ) + throw new IllegalStateException( "Property " + p + " has an unbound type and no explicit target entity."); final boolean currentHasIdentifier = addProperty( p, elements, localPropertyAccessor ); hasIdentifier = hasIdentifier || currentHasIdentifier; } return hasIdentifier; } + private static boolean hasExplicitTargetEntity(XProperty p) { + if( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class ).targetEntity().equals( void.class ) ) + return true; + if( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class ).targetEntity().equals( void.class ) ) + return true; + if( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class ).targetEntity().equals( void.class ) ) + return true; + if( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class ).targetEntity().equals( void.class ) ) + return true; + return false; + } + private static boolean addProperty( XProperty property, List<PropertyData> annElts, String propertyAccessor Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/XClass.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/XClass.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/XClass.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -8,6 +8,9 @@ */ public interface XClass extends XAnnotatedElement { + public static final String ACCESS_PROPERTY = "property"; + public static final String ACCESS_FIELD = "field"; + static final Filter DEFAULT_FILTER = new Filter() { public boolean returnStatic() { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXArrayType.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXArrayType.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXArrayType.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -47,7 +47,7 @@ public Type defaultCase(Type t) { throw new IllegalArgumentException( t + " is not an array type" ); } - }.doSwitch( resolve() ); + }.doSwitch( approximate() ); } public XClass getClassOrElementClass() { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXClass.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXClass.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXClass.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -81,9 +81,9 @@ } public List<XProperty> getDeclaredProperties(String accessType, Filter filter) { - if ( accessType.equals( "field" ) ) + if ( accessType.equals( ACCESS_FIELD ) ) return getDeclaredFieldProperties(filter); - if ( accessType.equals( "property" ) ) + if ( accessType.equals( ACCESS_PROPERTY ) ) return getDeclaredMethodProperties(filter); throw new IllegalArgumentException( "Unknown access type " + accessType ); } Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXCollectionType.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXCollectionType.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXCollectionType.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -42,7 +42,7 @@ } return toXClass( componentType ); } - }.doSwitch( resolve() ); + }.doSwitch( approximate() ); } public XClass getMapKey() { @@ -54,18 +54,18 @@ } return null; } - }.doSwitch( resolve() ); + }.doSwitch( approximate() ); } public XClass getClassOrElementClass() { - return toXClass( resolve() ); + return toXClass( approximate() ); } public Class<? extends Collection> getCollectionClass() { - return TypeUtils.getCollectionClass( resolve() ); + return TypeUtils.getCollectionClass( approximate() ); } public XClass getType() { - return toXClass( resolve() ); + return toXClass( approximate() ); } } \ No newline at end of file Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXFactory.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -36,25 +36,25 @@ private EntityResolver entityResolver; private XMLContext xmlContext; - private static class PropertyKey extends Pair<Member, XClass> { - PropertyKey(Member member, XClass owner) { - super( member, owner ); - } - } - private static class TypeKey extends Pair<Type, TypeEnvironment> { TypeKey(Type t, TypeEnvironment context) { super( t, context ); } } + private static class MemberKey extends Pair<Member, TypeKey> { + MemberKey(Member member, Type owner, TypeEnvironment context) { + super( member, new TypeKey( owner, context ) ); + } + } + private final Map<TypeKey, JavaXClass> xClasses = new HashMap<TypeKey, JavaXClass>(); private final Map<Package, JavaXPackage> packagesToXPackages = new HashMap<Package, JavaXPackage>(); - private final Map<PropertyKey, JavaXProperty> xProperties = new HashMap<PropertyKey, JavaXProperty>(); + private final Map<MemberKey, JavaXProperty> xProperties = new HashMap<MemberKey, JavaXProperty>(); - private final Map<PropertyKey, JavaXMethod> xMethods = new HashMap<PropertyKey, JavaXMethod>(); + private final Map<MemberKey, JavaXMethod> xMethods = new HashMap<MemberKey, JavaXMethod>(); private final TypeEnvironmentFactory typeEnvs = new TypeEnvironmentFactory(); @@ -121,7 +121,7 @@ } XProperty getXProperty(Member member, JavaXClass owner) { - PropertyKey key = new PropertyKey( member, owner ); + MemberKey key = new MemberKey( member, owner.toClass(), owner.getTypeEnvironment() ); JavaXProperty xProperty = xProperties.get( key ); if ( ! xProperties.containsKey( key ) ) { xProperty = JavaXProperty.create( member, owner.getTypeEnvironment(), this ); @@ -131,7 +131,7 @@ } XMethod getXMethod(Member member, JavaXClass owner) { - PropertyKey key = new PropertyKey( member, owner ); + MemberKey key = new MemberKey( member, owner.toClass(), owner.getTypeEnvironment() ); JavaXMethod xMethod = xMethods.get( key ); if ( ! xMethods.containsKey( key ) ) { xMethod = JavaXMethod.create( member, owner.getTypeEnvironment(), this ); @@ -165,9 +165,8 @@ return new JavaXArrayType( propType, context, this ); if ( TypeUtils.isCollection( boundType ) ) return new JavaXCollectionType( propType, context, this ); - if ( TypeUtils.isBase( boundType ) ) + if ( TypeUtils.isSimple( boundType ) ) return new JavaXSimpleType( propType, context, this ); - assert TypeUtils.isVoid( boundType ); throw new IllegalArgumentException( "No PropertyTypeExtractor available for type void "); } Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXSimpleType.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXSimpleType.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXSimpleType.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -25,7 +25,7 @@ } public XClass getElementClass() { - return toXClass( resolve() ); + return toXClass( approximate() ); } public XClass getClassOrElementClass() { @@ -37,7 +37,7 @@ } public XClass getType() { - return toXClass( resolve() ); + return toXClass( approximate() ); } public XClass getMapKey() { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXType.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXType.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/JavaXType.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -16,16 +16,16 @@ */ abstract class JavaXType { - private final Type unboundType; private final TypeEnvironment context; - private final TypeEnvironment approximatingContext; private final JavaXFactory factory; + private final Type approximatedType; + private final Type boundType; protected JavaXType( Type unboundType, TypeEnvironment context, JavaXFactory factory ) { - this.unboundType = unboundType; this.context = context; - this.approximatingContext = factory.toApproximatingEnvironment( context ); this.factory = factory; + this.boundType = context.bind( unboundType ); + this.approximatedType = factory.toApproximatingEnvironment( context ).bind( unboundType ); } abstract public boolean isArray(); @@ -37,11 +37,11 @@ abstract public XClass getType(); public boolean isResolved() { - return TypeUtils.isResolved( context.bind( unboundType ) ); + return TypeUtils.isResolved( boundType ); } - protected Type resolve() { - return approximatingContext.bind( unboundType ); + protected Type approximate() { + return approximatedType; } protected XClass toXClass(Type type) { Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/ApproximatingTypeEnvironment.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/ApproximatingTypeEnvironment.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/ApproximatingTypeEnvironment.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -8,21 +8,32 @@ import java.lang.reflect.WildcardType; /** - * A <code>TypeEnvironment</code> that approximates the unresolved components of a <code>Type</code> to their nearest upper binding. + * A <code>TypeEnvironment</code> that approximates the unresolved components of a generic simple + * type or collection to their nearest upper binding. The returned type is always fully resolved. * <p> + * The concept of "type approximation" is not really sound in general. This class just does what we need + * within the Hibernate Annotations environment. It's more or less a hack. The idea is that certain + * types can provide useful information even if they're not fully resolved in the environment. This class + * tries to turn those types into the nearest fully resolved type that still carries that information. + * <p> * For example:<br> * <code>T</code> becomes <code>Object</code>.<br> - * Array of <code>? extends Foo</code> becomes array of <code>Foo</code>.<br> + * <code>T extends Foo</code> becomes <code>Foo</code>.<br> * <code>List<T></code> becomes <code>List<Object></code>.<br> - * <code>List<T extends Foo></code> becomes <code>List<Foo></code>.<p> + * <code>List<T extends Foo></code> becomes <code>List<Foo></code>.<br> + * An array of <code>T extends Foo</code> becomes an array of <code>Foo</code>.<p> * - * If a wildcard or type variable has multiple upper bounds, it will be approximated to Object. - * Information on lower bounds is discarded during approximation.<p> + * If a type variable has multiple upper bounds, it will be approximated to <code>Object</code>. + * Lower bounds are ignored.<p> * - * An generic array is always approximated to an array of <code>Object</code>s. Example:<br> - * <code>List<T>[]</code> becomes <code>Object[]</code>. This is sub-optimal, but a finer - * approximations has implementation problems. + * Wildcards are generally not approximated. <code>Class<?></code> stays <code>Class<?></code>. + * A wildcard within a generic type is approximated to its upper binding. <code>List<?></code> becomes + * <code>List<Object></code><p> * + * Note that <code>Class<T></code> is <emp>not</emp> approximated <code>Class<Object></code>. + * That would be wrong in any situation. All parametric types that are not type variables, collections or + * arrays are coarsely approximated to <code>Object.class</code>. + * * @return a type where the generic arguments have been replaced by raw classes. * * @author Paolo Perrotta @@ -30,9 +41,15 @@ class ApproximatingTypeEnvironment implements TypeEnvironment { public Type bind(final Type type) { - Type result = new TypeSwitch<Type>() { + Type result = fineApproximation( type ); + assert TypeUtils.isResolved( result ); + return result; + } + + private Type fineApproximation(final Type type) { + return new TypeSwitch<Type>() { public Type caseWildcardType(WildcardType wildcardType) { - return collapseToUpperBound( wildcardType.getUpperBounds() ); + return wildcardType; } @Override @@ -46,36 +63,73 @@ return genericArrayType; Type componentType = genericArrayType.getGenericComponentType(); Type boundComponentType = bind( componentType ); - if( !( boundComponentType instanceof Class) ) - return Object[].class; // this is because I found no standard way to instance arrays of a generic type - return Array.newInstance( (Class)boundComponentType, 0 ).getClass(); + if( boundComponentType instanceof Class ) + return Array.newInstance( (Class)boundComponentType, 0 ).getClass(); + // fall back to coarse approximation, because I found no standard way + // to instance arrays of a generic type + return Object[].class; } @Override public Type caseParameterizedType(ParameterizedType parameterizedType) { if( TypeUtils.isResolved( parameterizedType ) ) return parameterizedType; + + if( !TypeUtils.isCollection( parameterizedType ) ) + return Object.class; // fall back to coarse approximation + Type[] typeArguments = parameterizedType.getActualTypeArguments(); - Type[] boundTypeArguments = new Type[typeArguments.length]; + Type[] approximatedTypeArguments = new Type[typeArguments.length]; for(int i = 0; i < typeArguments.length; i++) - boundTypeArguments[i] = bind( typeArguments[i] ); - return TypeFactory.createParameterizedType( bind( parameterizedType.getRawType() ), boundTypeArguments, parameterizedType.getOwnerType() ); + approximatedTypeArguments[i] = coarseApproximation( typeArguments[i] ); + + return TypeFactory.createParameterizedType( + bind( parameterizedType.getRawType() ), + approximatedTypeArguments, + parameterizedType.getOwnerType() ); } @Override - public Type caseTypeVariable(TypeVariable typeVariable) { - return collapseToUpperBound( typeVariable.getBounds() ); + public Type defaultCase(Type t) { + return coarseApproximation( t ); } + }.doSwitch( type ); + } + + public Type coarseApproximation(final Type type) { + Type result = new TypeSwitch<Type>() { + public Type caseWildcardType(WildcardType wildcardType) { + return approximateTo( wildcardType.getUpperBounds() ); + } - private Type collapseToUpperBound(Type[] upperBounds) { - if( upperBounds.length != 1 ) - return Object.class; // we can't do much about multiple upper bounds - return bind( upperBounds[0] ); + @Override + public Type caseGenericArrayType(GenericArrayType genericArrayType) { + if( TypeUtils.isResolved( genericArrayType ) ) + return genericArrayType; + return Object[].class; } @Override + public Type caseParameterizedType(ParameterizedType parameterizedType) { + if( TypeUtils.isResolved( parameterizedType ) ) + return parameterizedType; + return Object.class; + } + + @Override + public Type caseTypeVariable(TypeVariable typeVariable) { + return approximateTo( typeVariable.getBounds() ); + } + + private Type approximateTo(Type[] bounds) { + if( bounds.length != 1 ) + return Object.class; + return coarseApproximation( bounds[0] ); + } + + @Override public Type defaultCase(Type t) { - throw new IllegalArgumentException( "Unknown subclass of Type: " + t.getClass() ); + return t; } }.doSwitch( type ); assert TypeUtils.isResolved( result ); Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/SimpleTypeEnvironment.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/SimpleTypeEnvironment.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/SimpleTypeEnvironment.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -24,16 +24,34 @@ @Override public Type caseGenericArrayType(GenericArrayType genericArrayType) { - Type componentType = bind( genericArrayType.getGenericComponentType() ); - return TypeFactory.createArrayType( componentType ); + Type originalComponentType = genericArrayType.getGenericComponentType(); + Type boundComponentType = bind( originalComponentType ); + // try to keep the original type if possible + if( originalComponentType == boundComponentType ) + return genericArrayType; + return TypeFactory.createArrayType( boundComponentType ); } @Override public Type caseParameterizedType(ParameterizedType parameterizedType) { - return TypeFactory.createParameterizedType( parameterizedType.getRawType(), substitute( parameterizedType - .getActualTypeArguments() ), parameterizedType.getOwnerType() ); + Type[] originalArguments = parameterizedType.getActualTypeArguments(); + Type[] boundArguments = substitute( originalArguments ); + // try to keep the original type if possible + if( areSame( originalArguments, boundArguments ) ) + return parameterizedType; + return TypeFactory.createParameterizedType( parameterizedType.getRawType(), boundArguments, parameterizedType.getOwnerType() ); } + private boolean areSame(Object[] array1, Object[] array2) { + if( array1.length != array2.length ) + return false; + for ( int i = 0; i < array1.length; i++ ) { + if( array1[i] != array2[i] ) + return false; + } + return true; + } + @Override public Type caseTypeVariable(TypeVariable typeVariable) { int idx = indexOf( formalArguments, typeVariable ); Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/TypeUtils.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/TypeUtils.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/reflection/java/generics/TypeUtils.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -40,11 +40,18 @@ @Override public Boolean caseWildcardType(WildcardType wildcardType) { - return false; + return areResolved( wildcardType.getUpperBounds() ) && areResolved( wildcardType.getLowerBounds() ); } }.doSwitch( t ); } + private static Boolean areResolved(Type[] types) { + for( Type t : types ) + if( !isResolved( t ) ) + return false; + return true; + } + public static Class<? extends Collection> getCollectionClass(Type type) { return new TypeSwitch<Class<? extends Collection>>() { @Override @@ -54,14 +61,16 @@ @Override public Class<? extends Collection> caseParameterizedType(ParameterizedType parameterizedType) { - Class rawType = (Class) parameterizedType.getRawType(); - for ( Type actualTypeArgument : parameterizedType.getActualTypeArguments() ) { - if ( !isBase( actualTypeArgument ) ) { - return null; - } - } - return getCollectionClass( rawType ); + return getCollectionClass( (Class) parameterizedType.getRawType() ); } + + @Override + public Class<? extends Collection> caseWildcardType(WildcardType wildcardType) { + Type[] upperBounds = wildcardType.getUpperBounds(); + if( upperBounds.length == 0 ) + return null; + return getCollectionClass( upperBounds[0] ); + } @Override public Class<? extends Collection> defaultCase(Type t) { @@ -79,7 +88,7 @@ || clazz == java.util.SortedMap.class; // extension to the specs } - public static boolean isBase(Type type) { + public static boolean isSimple(Type type) { return new TypeSwitch<Boolean>() { @Override public Boolean caseClass(Class clazz) { @@ -90,7 +99,7 @@ @Override public Boolean caseParameterizedType(ParameterizedType parameterizedType) { for ( Type actualTypeArgument : parameterizedType.getActualTypeArguments() ) { - if ( !isBase( actualTypeArgument ) ) { + if ( !isSimple( actualTypeArgument ) ) { return false; } } @@ -98,12 +107,24 @@ } @Override + public Boolean caseWildcardType(WildcardType wildcardType) { + return areSimple( wildcardType.getUpperBounds() ) && areSimple( wildcardType.getLowerBounds() ); + } + + @Override public Boolean defaultCase(Type t) { return false; } }.doSwitch( type ); } + private static Boolean areSimple(Type[] types) { + for( Type t : types ) + if( !isSimple( t ) ) + return false; + return true; + } + public static boolean isVoid(Type type) { return void.class.equals( type ); } @@ -117,7 +138,7 @@ @Override public Boolean caseGenericArrayType(GenericArrayType genericArrayType) { - return isBase( genericArrayType.getGenericComponentType() ); + return isSimple( genericArrayType.getGenericComponentType() ); } @Override Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/validator/ClassValidator.java =================================================================== --- trunk/HibernateExt/metadata/src/java/org/hibernate/validator/ClassValidator.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/java/org/hibernate/validator/ClassValidator.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -161,7 +161,6 @@ if ( beanValidator != null ) beanValidators.add( beanValidator ); } } - ; //Check on all selected classes for ( XClass currClass : classes ) { @@ -183,9 +182,6 @@ } ); for ( XProperty field : fields ) { - if ( !field.isTypeResolved() ) { - throw new IllegalStateException( "Couldn't bind the type of property field " + field ); - } createMemberValidator( field ); createChildValidator( resourceBundle, field ); } @@ -221,6 +217,8 @@ } private void createMemberValidator(XMember member) { + if( !member.isTypeResolved() ) + log.warn( "Original type of property " + member + " is unbound and has been approximated."); Annotation[] memberAnnotations = member.getAnnotations(); for ( int j = 0; j < memberAnnotations.length ; j++ ) { Annotation methodAnnotation = memberAnnotations[j]; Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/TestCase.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/TestCase.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/TestCase.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -25,7 +25,7 @@ super(x); } - private void buildSessionFactory(Class[] classes, String[] packages) throws Exception { + protected void buildSessionFactory(Class[] classes, String[] packages) throws Exception { if ( getSessions()!=null ) getSessions().close(); try { Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/DNA.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/DNA.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/DNA.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -0,0 +1,24 @@ +//$Id: Address.java 8967 2006-01-03 12:27:34Z epbernard $ +package org.hibernate.test.annotations.generics; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Paolo Perrotta + */ +@Entity +public class DNA { + + private Integer id; + + @Id @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } +} Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/Gene.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/Gene.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/Gene.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -0,0 +1,29 @@ +package org.hibernate.test.annotations.generics; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +/** + * @author Paolo Perrotta + */ +@Entity +public class Gene<T> { + + private Integer id; + + @Id @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @ManyToOne(targetEntity=DNA.class) + public T getGeneticCode() { return null; } + + public void setGeneticCode(T gene) {} +} Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/UnresolvedTypeTest.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/UnresolvedTypeTest.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/generics/UnresolvedTypeTest.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -0,0 +1,28 @@ +package org.hibernate.test.annotations.generics; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.test.annotations.TestCase; + +/** + * @author Paolo Perrotta + */ +public class UnresolvedTypeTest extends TestCase { + + public void testAcceptsUnresolvedPropertyTypesIfATargetEntityIsExplicitlySet() { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + Gene item = new Gene(); + s.persist( item ); + tx.commit(); + s.close(); + } + + @Override + protected Class[] getMappings() { + return new Class[] { + Gene.class, + DNA.class + }; + } +} Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/JavaXClassTest.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/JavaXClassTest.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/JavaXClassTest.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -18,68 +18,61 @@ */ public class JavaXClassTest extends XAnnotatedElementTestCase { - XClass clazz; + XClass fatherAsSeenFromSon = ReflectionManager.INSTANCE.toXClass( Son.class ).getSuperclass(); + XClass grandpa = ReflectionManager.INSTANCE.toXClass( Grandpa.class ); - XClass fatherClazz = ReflectionManager.INSTANCE.toXClass( Grandpa.class ); - - @Override - protected void setUp() { - clazz = ReflectionManager.INSTANCE.toXClass( Son.class ).getSuperclass(); - } - - public void testHasAnOwnerClass() { - // The "owner" is the "point of view" through which we see a class. + public void testHasAPointOfViewClass() { // Since Dad is an Entity, getting it through Son.getSuperclass() gives - // us a view of Dad with Son as an owner. + // us a view of properties from Dad with Son as a point of view. XClass sameView = ReflectionManager.INSTANCE.toXClass( Son.class ).getSuperclass(); XClass differentView = ReflectionManager.INSTANCE.toXClass( Dad.class ); - assertSame( "Should be the same instance: same owner", sameView, clazz ); - assertNotSame( "Should be a different instance: different owner", differentView, clazz ); + assertSame( "Should be the same instance: same owner", sameView, fatherAsSeenFromSon ); + assertNotSame( "Should be a different instance: different owner", differentView, fatherAsSeenFromSon ); assertEquals( ".equals() should show equality", sameView, differentView ); } public void testHasAName() { - assertSame( "org.hibernate.test.reflection.java.generics.Dad", clazz.getName() ); + assertSame( "org.hibernate.test.reflection.java.generics.Dad", fatherAsSeenFromSon.getName() ); } public void testHasASuperclass() { - assertEquals( fatherClazz, clazz.getSuperclass() ); + assertEquals( grandpa, fatherAsSeenFromSon.getSuperclass() ); } public void testHasInterfaces() { - XClass[] interfaces = clazz.getSuperclass().getInterfaces(); + XClass[] interfaces = fatherAsSeenFromSon.getSuperclass().getInterfaces(); assertEquals( 2, interfaces.length ); assertTrue( ReflectionManager.INSTANCE.equals( interfaces[0], Serializable.class) ); assertTrue( ReflectionManager.INSTANCE.equals( interfaces[1], Language.class) ); } public void testCanBeAssignableFromAnotherXClass() { - assertFalse( clazz.isAssignableFrom( fatherClazz ) ); - assertTrue( fatherClazz.isAssignableFrom( clazz ) ); + assertFalse( fatherAsSeenFromSon.isAssignableFrom( grandpa ) ); + assertTrue( grandpa.isAssignableFrom( fatherAsSeenFromSon ) ); } public void testExtractsPublicFieldsAsProperties() { - List<XProperty> fieldProperties = clazz.getDeclaredProperties( "field" ); + List<XProperty> fieldProperties = fatherAsSeenFromSon.getDeclaredProperties( "field" ); assertEquals( 1, fieldProperties.size() ); } public void testExtractsPublicMethodsAsProperties() { - List<XProperty> methodProperties = clazz.getDeclaredProperties( "property" ); + List<XProperty> methodProperties = fatherAsSeenFromSon.getDeclaredProperties( "property" ); assertEquals( 7, methodProperties.size() ); } public void testCanBeAbstract() { - assertFalse( clazz.isAbstract() ); + assertFalse( fatherAsSeenFromSon.isAbstract() ); assertTrue( ReflectionManager.INSTANCE.toXClass( Grandpa.class ).isAbstract() ); } public void testCanBeAPrimitive() { - assertFalse( clazz.isPrimitive() ); + assertFalse( fatherAsSeenFromSon.isPrimitive() ); assertTrue( ReflectionManager.INSTANCE.toXClass( int.class ).isPrimitive() ); } public void testCanBeAnEnum() { - assertFalse( clazz.isEnum() ); + assertFalse( fatherAsSeenFromSon.isEnum() ); assertTrue( ReflectionManager.INSTANCE.toXClass( CacheModeType.class ).isEnum() ); } Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -2,6 +2,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Collection; import java.util.Map; import junit.framework.TestCase; @@ -22,37 +23,49 @@ assertEquals( String[].class, approximatingUnboundContext.bind( String[].class ) ); } + public void testDoesNothingOnWildcards() throws Exception { + Type type = BigBlob.class.getMethod( "genericClass", new Class[0] ).getGenericReturnType(); + Type approxType = approximatingBoundContext.bind( type ); + assertEquals( "java.lang.Class<?>", approxType.toString() ); + } + public void testDoesNothingOnParameterizedTypesThatAreAlreadyFullyBound() throws Exception { - Type type = BigBlob.class.getMethod( "genTypeWithoutWildcards", new Class[0] ).getGenericReturnType(); + Type type = BigBlob.class.getMethod( "simpleGenericType", new Class[0] ).getGenericReturnType(); assertEquals( boundContext.bind( type ), approximatingBoundContext.bind( type ) ); } + public void testDoesNothingOnComplexParameterizedTypesThatAreNotCollections() throws Exception { + Type type = BigBlob.class.getMethod( "genericType", new Class[0] ).getGenericReturnType(); + assertEquals( boundContext.bind( type ), approximatingBoundContext.bind( type ) ); + } + public void testDoesNothingOnGenericArraysThatAreAlreadyFullyBound() throws Exception { - Type type = BigBlob.class.getMethod( "genArrayType", new Class[0] ).getGenericReturnType(); + Type type = BigBlob.class.getMethod( "array", new Class[0] ).getGenericReturnType(); assertEquals( boundContext.bind( type ), approximatingBoundContext.bind( type ) ); } - public void testApproximatesUnboundGenericArraysToArraysOfObjects() throws Exception { - Type type = BigBlob.class.getMethod( "genArrayType", new Class[0] ).getGenericReturnType(); + public void testApproximatesSimpleGenericTypesToTheirUpperBound() throws Exception { + Type type = BigBlob.class.getMethod( "simpleGenericType", new Class[0] ).getGenericReturnType(); + assertEquals( "java.util.List<java.lang.String>", approximatingBoundContext.bind( type ).toString() ); + } + + public void testApproximatesGenericsInArraysToTheirUpperBounds() throws Exception { + Type type = BigBlob.class.getMethod( "array", new Class[0] ).getGenericReturnType(); + assertEquals( Collection[].class, approximatingUnboundContext.bind( type ) ); + } + + public void testApproximatesArraysOfComplexTypesToArraysOfObjects() throws Exception { + Type type = BigBlob.class.getMethod( "complexGenericArray", new Class[0] ).getGenericReturnType(); assertEquals( Object[].class, approximatingUnboundContext.bind( type ) ); } - public void testApproximatesWildcardsToTheirUpperBound() throws Exception { - Type type = BigBlob.class.getMethod( "genTypeContainingWildcardsWithUpperBounds", new Class[0] ).getGenericReturnType(); + public void testApproximatesGenericsAndWildcardsInCollectionsToTheirUpperBounds() throws Exception { + Type type = BigBlob.class.getMethod( "genericCollection", new Class[0] ).getGenericReturnType(); ParameterizedType approxType = (ParameterizedType)approximatingUnboundContext.bind( type ); assertEquals( Map.class, approxType.getRawType() ); assertNull( approxType.getOwnerType() ); assertEquals( 2, approxType.getActualTypeArguments().length ); assertEquals( Object.class, approxType.getActualTypeArguments()[0] ); - assertEquals( Comparable.class, approxType.getActualTypeArguments()[1] ); + assertEquals( Collection.class, approxType.getActualTypeArguments()[1] ); } - - public void testApproximatesWildcardsWithoutUpperBoundsToObject() throws Exception { - Type type = BigBlob.class.getMethod( "genTypeContainingWildcardsWithoutUpperBounds", new Class[0] ).getGenericReturnType(); - ParameterizedType approxType = (ParameterizedType)approximatingUnboundContext.bind( type ); - assertEquals( Class.class, approxType.getRawType() ); - assertNull( approxType.getOwnerType() ); - assertEquals( 1, approxType.getActualTypeArguments().length ); - assertEquals( Object.class, approxType.getActualTypeArguments()[0] ); - } } Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/BigBlob.java =================================================================== --- trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/BigBlob.java 2006-03-13 13:12:35 UTC (rev 9611) +++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/reflection/java/generics/BigBlob.java 2006-03-13 16:20:09 UTC (rev 9612) @@ -1,15 +1,20 @@ package org.hibernate.test.reflection.java.generics; +import java.util.Collection; import java.util.List; import java.util.Map; -public class BigBlob<T, E> { +public class BigBlob<T, E extends Collection> { - public List<T> genTypeWithoutWildcards() { return null; } + public E simpleGenericType() { return null; } - public Map<T, ? extends Comparable> genTypeContainingWildcardsWithUpperBounds() { return null; } + public Class<?> genericClass() { return null; } - public Class<?> genTypeContainingWildcardsWithoutUpperBounds() { return null; } + public Class<T> genericType() { return null; } - public List<T>[] genArrayType() { return null; } + public Map<T, ? extends E> genericCollection() { return null; } + + public E[] array() { return null; } + + public List<? extends T>[] complexGenericArray() { return null; } } |