001 package org.junit.experimental.theories;
002
003 import java.lang.annotation.Annotation;
004 import java.lang.reflect.Constructor;
005 import java.lang.reflect.Method;
006 import java.util.ArrayList;
007 import java.util.Arrays;
008 import java.util.Collections;
009 import java.util.HashMap;
010 import java.util.List;
011 import java.util.Map;
012
013 public class ParameterSignature {
014
015 private static final Map<Class<?>, Class<?>> CONVERTABLE_TYPES_MAP = buildConvertableTypesMap();
016
017 private static Map<Class<?>, Class<?>> buildConvertableTypesMap() {
018 Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
019
020 putSymmetrically(map, boolean.class, Boolean.class);
021 putSymmetrically(map, byte.class, Byte.class);
022 putSymmetrically(map, short.class, Short.class);
023 putSymmetrically(map, char.class, Character.class);
024 putSymmetrically(map, int.class, Integer.class);
025 putSymmetrically(map, long.class, Long.class);
026 putSymmetrically(map, float.class, Float.class);
027 putSymmetrically(map, double.class, Double.class);
028
029 return Collections.unmodifiableMap(map);
030 }
031
032 private static <T> void putSymmetrically(Map<T, T> map, T a, T b) {
033 map.put(a, b);
034 map.put(b, a);
035 }
036
037 public static ArrayList<ParameterSignature> signatures(Method method) {
038 return signatures(method.getParameterTypes(), method
039 .getParameterAnnotations());
040 }
041
042 public static List<ParameterSignature> signatures(Constructor<?> constructor) {
043 return signatures(constructor.getParameterTypes(), constructor
044 .getParameterAnnotations());
045 }
046
047 private static ArrayList<ParameterSignature> signatures(
048 Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
049 ArrayList<ParameterSignature> sigs = new ArrayList<ParameterSignature>();
050 for (int i = 0; i < parameterTypes.length; i++) {
051 sigs.add(new ParameterSignature(parameterTypes[i],
052 parameterAnnotations[i]));
053 }
054 return sigs;
055 }
056
057 private final Class<?> type;
058
059 private final Annotation[] annotations;
060
061 private ParameterSignature(Class<?> type, Annotation[] annotations) {
062 this.type = type;
063 this.annotations = annotations;
064 }
065
066 public boolean canAcceptValue(Object candidate) {
067 return (candidate == null) ? !type.isPrimitive() : canAcceptType(candidate.getClass());
068 }
069
070 public boolean canAcceptType(Class<?> candidate) {
071 return type.isAssignableFrom(candidate) ||
072 isAssignableViaTypeConversion(type, candidate);
073 }
074
075 public boolean canPotentiallyAcceptType(Class<?> candidate) {
076 return candidate.isAssignableFrom(type) ||
077 isAssignableViaTypeConversion(candidate, type) ||
078 canAcceptType(candidate);
079 }
080
081 private boolean isAssignableViaTypeConversion(Class<?> targetType, Class<?> candidate) {
082 if (CONVERTABLE_TYPES_MAP.containsKey(candidate)) {
083 Class<?> wrapperClass = CONVERTABLE_TYPES_MAP.get(candidate);
084 return targetType.isAssignableFrom(wrapperClass);
085 } else {
086 return false;
087 }
088 }
089
090 public Class<?> getType() {
091 return type;
092 }
093
094 public List<Annotation> getAnnotations() {
095 return Arrays.asList(annotations);
096 }
097
098 public boolean hasAnnotation(Class<? extends Annotation> type) {
099 return getAnnotation(type) != null;
100 }
101
102 public <T extends Annotation> T findDeepAnnotation(Class<T> annotationType) {
103 Annotation[] annotations2 = annotations;
104 return findDeepAnnotation(annotations2, annotationType, 3);
105 }
106
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
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 }