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    }