    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    }