[hibernate-commits] Hibernate SVN: r16184 - in validator/trunk/hibernate-validator/src: main/java/org/hibernate/validation/util and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Mar 19 07:58:06 EDT 2009


Author: hardy.ferentschik
Date: 2009-03-19 07:58:06 -0400 (Thu, 19 Mar 2009)
New Revision: 16184

Modified:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ReflectionHelperTest.java
Log:
HV-128 Added support for Iterable and resolved ambiguity with Maps

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java	2009-03-18 16:00:44 UTC (rev 16183)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/MetaConstraint.java	2009-03-19 11:58:06 UTC (rev 16184)
@@ -99,24 +99,6 @@
 		return constraintTree.getDescriptor().getGroups();
 	}
 
-	/**
-	 * @return Returns <code>true</code> in case the constraint is defined on a collection, <code>false</code>
-	 *         otherwise.
-	 */
-	public boolean isCollection() {
-		Type t = typeOfAnnoatedElement();
-		return ReflectionHelper.isCollection( t );
-	}
-
-	/**
-	 * @return Returns <code>true</code> in case the constraint is defined on an array, <code>false</code>
-	 *         otherwise.
-	 */
-	public boolean isArray() {
-		Type t = typeOfAnnoatedElement();
-		return ReflectionHelper.isArray( t );
-	}
-
 	public ConstraintDescriptor getDescriptor() {
 		return constraintTree.getDescriptor();
 	}

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java	2009-03-18 16:00:44 UTC (rev 16183)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ValidatorImpl.java	2009-03-19 11:58:06 UTC (rev 16184)
@@ -36,6 +36,7 @@
 import javax.validation.Validator;
 import javax.validation.groups.Default;
 
+import com.googlecode.jtype.TypeUtils;
 import org.slf4j.Logger;
 
 import org.hibernate.validation.engine.groups.Group;
@@ -300,16 +301,16 @@
 	 */
 	private <T> Iterator<?> createIteratorForCascadedValue(ExecutionContext<T> context, Type type, Object value) {
 		Iterator<?> iter;
-		if ( ReflectionHelper.isCollection( type ) ) {
-			boolean isIterable = value instanceof Iterable;
-			Map<?, ?> map = !isIterable ? ( Map<?, ?> ) value : null;
-			Iterable<?> elements = isIterable ?
-					( Iterable<?> ) value :
-					map.entrySet();
-			iter = elements.iterator();
+		if ( ReflectionHelper.isIterable( type ) ) {
+			iter = ( ( Iterable<?> ) value ).iterator();
 			context.markCurrentPropertyAsIndexed();
 		}
-		else if ( ReflectionHelper.isArray( type ) ) {
+		else if ( ReflectionHelper.isMap( type ) ) {
+			Map<?, ?> map = ( Map<?, ?> ) value;
+			iter = map.values().iterator();
+			context.markCurrentPropertyAsIndexed();
+		}
+		else if ( TypeUtils.isArray( type ) ) {
 			List<?> arrayList = Arrays.asList( value );
 			iter = arrayList.iterator();
 			context.markCurrentPropertyAsIndexed();

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java	2009-03-18 16:00:44 UTC (rev 16183)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ReflectionHelper.java	2009-03-19 11:58:06 UTC (rev 16184)
@@ -21,7 +21,6 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
-import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
@@ -29,13 +28,15 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import javax.validation.ValidationException;
 
+import com.googlecode.jtype.TypeUtils;
+
 /**
  * Some reflection utility methods.
  *
@@ -213,7 +214,7 @@
 		}
 	}
 
-	public static Class<?> loadClass(String name, Class caller) throws ClassNotFoundException {
+	public static Class<?> loadClass(String name, Class<?> caller) throws ClassNotFoundException {
 		try {
 			//try context classloader, if fails try caller classloader
 			ClassLoader loader = Thread.currentThread().getContextClassLoader();
@@ -231,107 +232,70 @@
 	}
 
 	/**
-	 * Determines the type of elements of a generic collection or array.
+	 * Determines the type of elements of an <code>Iterable</code>, array or the value of a <code>Map</code>.
 	 *
-	 * @param type the collection or array type.
+	 * @param type the type to inspect
 	 *
-	 * @return the element type of the generic collection/array or <code>null</code> in case the specified type is not a collection/array or the
-	 *         element type cannot be determined.
-	 *
-	 * @todo Verify algorithm. Does this hold up in most cases?
+	 * @return Returns the type of elements of an <code>Iterable</code>, array or the value of a <code>Map</code>. <code>
+	 *         null</code> is returned in case the type is not indexable (in the context of JSR 303).
 	 */
 	public static Type getIndexedType(Type type) {
 		Type indexedType = null;
-		if ( isCollection( type ) && type instanceof ParameterizedType ) {
+		if ( isIterable( type ) && type instanceof ParameterizedType ) {
 			ParameterizedType paramType = ( ParameterizedType ) type;
-			Class collectionClass = getCollectionClass( type );
-			if ( collectionClass == Collection.class || collectionClass == java.util.List.class || collectionClass == java.util.Set.class || collectionClass == java.util.SortedSet.class ) {
-				indexedType = paramType.getActualTypeArguments()[0];
-			}
-			else if ( collectionClass == Map.class || collectionClass == java.util.SortedMap.class ) {
-				indexedType = paramType.getActualTypeArguments()[1];
-			}
+			indexedType = paramType.getActualTypeArguments()[0];
 		}
-		else if ( ReflectionHelper.isArray( type ) && type instanceof GenericArrayType ) {
-			GenericArrayType arrayTye = ( GenericArrayType ) type;
-			indexedType = arrayTye.getGenericComponentType();
+		else if ( isMap( type ) && type instanceof ParameterizedType ) {
+			ParameterizedType paramType = ( ParameterizedType ) type;
+			indexedType = paramType.getActualTypeArguments()[1];
 		}
+		else if ( TypeUtils.isArray( type ) ) {
+			indexedType = TypeUtils.getComponentType( type );
+		}
 		return indexedType;
 	}
 
-
 	/**
 	 * @param type the type to check.
 	 *
-	 * @return Returns <code>true</code> if <code>type</code> is an array type or <code>false</code> otherwise.
+	 * @return Returns <code>true</code> if <code>type</code> is a iterable type, <code>false</code> otherwise.
 	 */
-	public static boolean isArray(Type type) {
-		if ( type instanceof Class ) {
-			return ( ( Class ) type ).isArray();
+	public static boolean isIterable(Type type) {
+		if ( type instanceof Class && isIterableClass( ( Class ) type ) ) {
+			return true;
 		}
-		return type instanceof GenericArrayType;
+		if ( type instanceof ParameterizedType ) {
+			return isIterable( ( ( ParameterizedType ) type ).getRawType() );
+		}
+		if ( type instanceof WildcardType ) {
+			Type[] upperBounds = ( ( WildcardType ) type ).getUpperBounds();
+			if ( upperBounds.length == 0 ) {
+				return false;
+			}
+			return isIterable( upperBounds[0] );
+		}
+		return false;
 	}
 
-
 	/**
 	 * @param type the type to check.
 	 *
-	 * @return Returns <code>true</code> if <code>type</code> is a collection type or <code>false</code> otherwise.
+	 * @return Returns <code>true</code> if <code>type</code> is implementing <code>Map</code>, <code>false</code> otherwise.
 	 */
-	public static boolean isCollection(Type type) {
-		return getCollectionClass( type ) != null;
-	}
-
-
-	/**
-	 * Returns the collection class for the specified type provided it is a collection.
-	 * <p>
-	 * This is a simplified version of commons annotations </code>TypeUtils.getCollectionClass()</code>.
-	 * </p>
-	 *
-	 * @param type the <code>Type</code> to check.
-	 *
-	 * @return the collection class, or <code>null</code> if type is not a collection class.
-	 */
-	@SuppressWarnings("unchecked")
-	public static Class<? extends Collection> getCollectionClass(Type type) {
-		if ( type instanceof Class && isCollectionClass( ( Class ) type ) ) {
-			return ( Class<? extends Collection> ) type;
+	public static boolean isMap(Type type) {
+		if ( type instanceof Class && isMapClass( ( Class ) type ) ) {
+			return true;
 		}
 		if ( type instanceof ParameterizedType ) {
-			return getCollectionClass( ( ( ParameterizedType ) type ).getRawType() );
+			return isMap( ( ( ParameterizedType ) type ).getRawType() );
 		}
 		if ( type instanceof WildcardType ) {
 			Type[] upperBounds = ( ( WildcardType ) type ).getUpperBounds();
 			if ( upperBounds.length == 0 ) {
-				return null;
+				return false;
 			}
-			return getCollectionClass( upperBounds[0] );
+			return isMap( upperBounds[0] );
 		}
-		return null;
-	}
-
-	/**
-	 * Checks whether the specified class parameter is an instance of a collection class.
-	 *
-	 * @param clazz <code>Class</code> to check.
-	 *
-	 * @return <code>true</code> is <code>clazz</code> is instance of a collection class, <code>false</code> otherwise.
-	 */
-	private static boolean isCollectionClass(Class<?> clazz) {
-		Class[] interfaces = clazz.getInterfaces();
-
-		for ( Class interfaceClass : interfaces ) {
-			if ( interfaceClass == Collection.class
-					|| interfaceClass == java.util.List.class
-					|| interfaceClass == java.util.Set.class
-					|| interfaceClass == java.util.Map.class
-					|| interfaceClass == java.util.SortedSet.class // extension to the specs
-					|| interfaceClass == java.util.SortedMap.class ) { // extension to the specs)
-				return true;
-			}
-		}
-
 		return false;
 	}
 
@@ -368,16 +332,14 @@
 
 		Iterator<?> iter = null;
 		Type type = value.getClass();
-		if ( isCollection( type ) ) {
-			boolean isIterable = value instanceof Iterable;
-			Map<?, ?> map = !isIterable ? ( Map<?, ?> ) value : null;
-			Iterable<?> elements = isIterable ?
-					( Iterable<?> ) value :
-					map.values();
-			iter = elements.iterator();
-
+		if ( isIterable( type ) ) {
+			iter = ( ( Iterable<?> ) value ).iterator();
 		}
-		else if ( isArray( type ) ) {
+		else if ( isMap( type ) ) {
+			Map<?, ?> map = ( Map<?, ?> ) value;
+			iter = map.values().iterator();
+		}
+		else if ( TypeUtils.isArray( type ) ) {
 			List<?> arrayList = Arrays.asList( value );
 			iter = arrayList.iterator();
 		}
@@ -403,7 +365,7 @@
 	 * @return Returns <code>true</code> if the cass contains a field or member for the specified property, <code>
 	 *         false</code> otherwise.
 	 */
-	public static boolean containsMember(Class clazz, String property) {
+	public static boolean containsMember(Class<?> clazz, String property) {
 		try {
 			clazz.getField( property );
 			return true;
@@ -435,5 +397,47 @@
 		return Class.forName( name, true, caller.getClassLoader() );
 	}
 
+	/**
+	 * Get all superclasses and interfaces recursively.
+	 *
+	 * @param clazz The class to start the search with.
+	 * @param classes List of classes to which to add all found super classes and interfaces.
+	 */
+	private static void computeClassHierarchy(Class<?> clazz, List<Class<?>> classes) {
+		for ( Class current = clazz; current != null; current = current.getSuperclass() ) {
+			if ( classes.contains( current ) ) {
+				return;
+			}
+			classes.add( current );
+			for ( Class currentInterface : current.getInterfaces() ) {
+				computeClassHierarchy( currentInterface, classes );
+			}
+		}
+	}
 
+	/**
+	 * Checks whether the specified class parameter is an instance of a collection class.
+	 *
+	 * @param clazz <code>Class</code> to check.
+	 *
+	 * @return <code>true</code> is <code>clazz</code> is instance of a collection class, <code>false</code> otherwise.
+	 */
+	private static boolean isIterableClass(Class<?> clazz) {
+		List<Class<?>> classes = new ArrayList<Class<?>>();
+		computeClassHierarchy( clazz, classes );
+		return classes.contains( Iterable.class );
+	}
+
+	/**
+	 * Checks whether the specified class parameter is an instance of a collection class.
+	 *
+	 * @param clazz <code>Class</code> to check.
+	 *
+	 * @return <code>true</code> is <code>clazz</code> is instance of a collection class, <code>false</code> otherwise.
+	 */
+	private static boolean isMapClass(Class<?> clazz) {
+		List<Class<?>> classes = new ArrayList<Class<?>>();
+		computeClassHierarchy( clazz, classes );
+		return classes.contains( Map.class );
+	}
 }

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ReflectionHelperTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ReflectionHelperTest.java	2009-03-18 16:00:44 UTC (rev 16183)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ReflectionHelperTest.java	2009-03-19 11:58:06 UTC (rev 16184)
@@ -18,15 +18,21 @@
 package org.hibernate.validation.util;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeSet;
 import javax.validation.ValidationException;
 import javax.validation.constraints.NotNull;
 import javax.validation.groups.Default;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -38,7 +44,48 @@
  * @author Hardy Ferentschik
  */
 public class ReflectionHelperTest {
+
 	@Test
+	public void testIsIterable() throws Exception {
+		Type type = TestTypes.class.getField( "stringList" ).getGenericType();
+		assertTrue( ReflectionHelper.isIterable( type ) );
+
+		assertTrue( ReflectionHelper.isIterable( new TreeSet<Object>().getClass() ) );
+
+		assertTrue( ReflectionHelper.isIterable( List.class ) );
+		assertTrue( ReflectionHelper.isIterable( HashSet.class ) );
+		assertTrue( ReflectionHelper.isIterable( Iterable.class ) );
+		assertTrue( ReflectionHelper.isIterable( Collection.class ) );
+
+		assertFalse( ReflectionHelper.isIterable( null ) );
+		assertFalse( ReflectionHelper.isIterable( Object.class ) );
+	}
+
+	@Test
+	public void testIsMap() throws Exception {
+		assertTrue( ReflectionHelper.isMap( Map.class ) );
+		assertTrue( ReflectionHelper.isMap( SortedMap.class ) );
+
+		Type type = TestTypes.class.getField( "objectMap" ).getGenericType();
+		assertTrue( ReflectionHelper.isMap( type ) );
+
+		assertFalse( ReflectionHelper.isMap( null ) );
+		assertFalse( ReflectionHelper.isMap( Object.class ) );
+	}
+
+	@Test
+	public void testGetIndexedType() throws Exception {
+		Type type = TestTypes.class.getField( "stringList" ).getGenericType();
+		assertEquals( String.class, ReflectionHelper.getIndexedType( type ) );
+
+		type = TestTypes.class.getField( "objectMap" ).getGenericType();
+		assertEquals( Object.class, ReflectionHelper.getIndexedType( type ) );
+
+		type = TestTypes.class.getField( "stringArray" ).getGenericType();
+		assertEquals( String.class, ReflectionHelper.getIndexedType( type ) );
+	}
+
+	@Test
 	public void testGetIndexedValueForMap() {
 		Map<String, Object> map = new HashMap<String, Object>();
 		Object testObject = new Object();
@@ -118,4 +165,10 @@
 			);
 		}
 	}
+
+	public class TestTypes {
+		public List<String> stringList;
+		public Map<String, Object> objectMap;
+		public String[] stringArray;
+	}
 }




More information about the hibernate-commits mailing list