1 package org.junit.experimental.theories;
2 
3 import java.lang.annotation.Annotation;
4 import java.lang.reflect.Constructor;
5 import java.lang.reflect.Method;
6 import java.util.ArrayList;
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12 
13 public class ParameterSignature {
14 
15     private static final Map<Class<?>, Class<?>> CONVERTABLE_TYPES_MAP = buildConvertableTypesMap();
16 
buildConvertableTypesMap()17     private static Map<Class<?>, Class<?>> buildConvertableTypesMap() {
18         Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
19 
20         putSymmetrically(map, boolean.class, Boolean.class);
21         putSymmetrically(map, byte.class, Byte.class);
22         putSymmetrically(map, short.class, Short.class);
23         putSymmetrically(map, char.class, Character.class);
24         putSymmetrically(map, int.class, Integer.class);
25         putSymmetrically(map, long.class, Long.class);
26         putSymmetrically(map, float.class, Float.class);
27         putSymmetrically(map, double.class, Double.class);
28 
29         return Collections.unmodifiableMap(map);
30     }
31 
putSymmetrically(Map<T, T> map, T a, T b)32     private static <T> void putSymmetrically(Map<T, T> map, T a, T b) {
33         map.put(a, b);
34         map.put(b, a);
35     }
36 
signatures(Method method)37     public static ArrayList<ParameterSignature> signatures(Method method) {
38         return signatures(method.getParameterTypes(), method
39                 .getParameterAnnotations());
40     }
41 
signatures(Constructor<?> constructor)42     public static List<ParameterSignature> signatures(Constructor<?> constructor) {
43         return signatures(constructor.getParameterTypes(), constructor
44                 .getParameterAnnotations());
45     }
46 
signatures( Class<?>[] parameterTypes, Annotation[][] parameterAnnotations)47     private static ArrayList<ParameterSignature> signatures(
48             Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
49         ArrayList<ParameterSignature> sigs = new ArrayList<ParameterSignature>();
50         for (int i = 0; i < parameterTypes.length; i++) {
51             sigs.add(new ParameterSignature(parameterTypes[i],
52                     parameterAnnotations[i]));
53         }
54         return sigs;
55     }
56 
57     private final Class<?> type;
58 
59     private final Annotation[] annotations;
60 
ParameterSignature(Class<?> type, Annotation[] annotations)61     private ParameterSignature(Class<?> type, Annotation[] annotations) {
62         this.type = type;
63         this.annotations = annotations;
64     }
65 
canAcceptValue(Object candidate)66     public boolean canAcceptValue(Object candidate) {
67         return (candidate == null) ? !type.isPrimitive() : canAcceptType(candidate.getClass());
68     }
69 
canAcceptType(Class<?> candidate)70     public boolean canAcceptType(Class<?> candidate) {
71         return type.isAssignableFrom(candidate) ||
72                 isAssignableViaTypeConversion(type, candidate);
73     }
74 
canPotentiallyAcceptType(Class<?> candidate)75     public boolean canPotentiallyAcceptType(Class<?> candidate) {
76         return candidate.isAssignableFrom(type) ||
77                 isAssignableViaTypeConversion(candidate, type) ||
78                 canAcceptType(candidate);
79     }
80 
isAssignableViaTypeConversion(Class<?> targetType, Class<?> candidate)81     private boolean isAssignableViaTypeConversion(Class<?> targetType, Class<?> candidate) {
82         if (CONVERTABLE_TYPES_MAP.containsKey(candidate)) {
83             Class<?> wrapperClass = CONVERTABLE_TYPES_MAP.get(candidate);
84             return targetType.isAssignableFrom(wrapperClass);
85         } else {
86             return false;
87         }
88     }
89 
getType()90     public Class<?> getType() {
91         return type;
92     }
93 
getAnnotations()94     public List<Annotation> getAnnotations() {
95         return Arrays.asList(annotations);
96     }
97 
hasAnnotation(Class<? extends Annotation> type)98     public boolean hasAnnotation(Class<? extends Annotation> type) {
99         return getAnnotation(type) != null;
100     }
101 
findDeepAnnotation(Class<T> annotationType)102     public <T extends Annotation> T findDeepAnnotation(Class<T> annotationType) {
103         Annotation[] annotations2 = annotations;
104         return findDeepAnnotation(annotations2, annotationType, 3);
105     }
106 
findDeepAnnotation( Annotation[] annotations, Class<T> annotationType, int depth)107     private <T extends Annotation> T findDeepAnnotation(
108             Annotation[] annotations, Class<T> annotationType, int depth) {
109         if (depth == 0) {
110             return null;
111         }
112         for (Annotation each : annotations) {
113             if (annotationType.isInstance(each)) {
114                 return annotationType.cast(each);
115             }
116             Annotation candidate = findDeepAnnotation(each.annotationType()
117                     .getAnnotations(), annotationType, depth - 1);
118             if (candidate != null) {
119                 return annotationType.cast(candidate);
120             }
121         }
122 
123         return null;
124     }
125 
getAnnotation(Class<T> annotationType)126     public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
127         for (Annotation each : getAnnotations()) {
128             if (annotationType.isInstance(each)) {
129                 return annotationType.cast(each);
130             }
131         }
132         return null;
133     }
134 }