View Javadoc

1   package net.sf.josceleton.commons.reflect;
2   
3   import java.lang.reflect.Constructor;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.Map;
7   
8   /**
9    * @since 0.1
10   */
11  public final class ReflectUtil {
12  	
13  	private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_BOXED;
14  	static {
15  		final Map<Class<?>, Class<?>> tmp = new HashMap<Class<?>, Class<?>>();
16  		tmp.put(Boolean.TYPE, Boolean.class);
17  		tmp.put(Float.TYPE, Float.class);
18  		tmp.put(Double.TYPE, Double.class);
19  		tmp.put(Byte.TYPE, Byte.class);
20  		tmp.put(Character.TYPE, Character.class);
21  		tmp.put(Integer.TYPE, Integer.class);
22  		tmp.put(Long.TYPE, Long.class);
23  		PRIMITIVES_TO_BOXED = Collections.unmodifiableMap(tmp);
24  	}
25  	
26  	private ReflectUtil() { /* utility class should not be instantiable */ }
27  
28  	/**
29  	 * @since 0.1
30  	 */
31  	@SuppressWarnings("unchecked")
32  	public static <T> Constructor<T> findConstructor(final ClassAdapter<T> clazz, final Object[] arguments) {
33  		final Constructor<T>[] constructors = (Constructor<T>[]) clazz.getConstructors();
34  		
35  		for (final Constructor<T> currentConstructor : constructors) {
36  			
37  			final Class<?>[] parameterTypes = currentConstructor.getParameterTypes();
38  			if(ReflectUtil.typesAreCompatible(ReflectUtil.adapt(parameterTypes), arguments) == true) {
39  				// there should be only one valid constructor, so returning immediately is just fine
40  				return currentConstructor;
41  			}
42  		}
43  		
44  		throw DynamicInstantiationException.newForNotFoundConstructor(clazz, arguments);
45  	}
46  
47  	private static ClassAdapter<?>[] adapt(final Class<?>[] raw) {
48  		final ClassAdapter<?>[] result = new ClassAdapter<?>[raw.length];
49  		for (int i = 0; i < result.length; i++) {
50  			result[i] = ClassAdapterImpl.create(raw[i]);
51  		}
52  		return result;
53  	}
54  	
55  	private static boolean typesAreCompatible(final ClassAdapter<?>[] parameterTypes, final Object[] givenArguments) {
56  		if(parameterTypes.length != givenArguments.length) {
57  			return false;
58  		}
59  		for (int i = 0; i < parameterTypes.length; i++) {
60  			final ClassAdapter<?> parameterType = parameterTypes[i];
61  			final Class<?> argumentClass = givenArguments[i].getClass();
62  			final ClassAdapter<?> argumentType = ClassAdapterImpl.create(argumentClass);
63  			if(ReflectUtil.isAssignable(argumentType, parameterType) == false) {
64  				return false;
65  			}
66  		}
67  		return true;
68  	}
69  	
70  	/**
71  	 * Enhances common {@link Class#isAssignableFrom(Class)} with additional primitive type checks.
72  	 * 
73  	 * @param typeUnderInspection is the type under inspection which will be checked for assignment.
74  	 * @param superType reference type to check <code>typeUnderInspection</code> against.
75  	 * @return true if <code>typeUnderInspection</code> is of the same type or a sub-type of <code>superType</code>.
76  	 * @since 0.1
77  	 */
78  	public static boolean isAssignable(final ClassAdapter<?> typeUnderInspection, final ClassAdapter<?> superType) {
79  		if(superType.isPrimitive() == false) {
80  			return superType.isAssignableFrom(typeUnderInspection);
81  		}
82  		
83  		final Class<?> boxedSourceType = PRIMITIVES_TO_BOXED.get(superType.getInnerClass());
84  		if(boxedSourceType == null) {
85  			throw new RuntimeException("Could not find assignable for primitive type [" + superType + "]!");
86  		}
87  		
88  		return boxedSourceType == typeUnderInspection.getInnerClass();
89  	}
90  }