package com.googlecode.totallylazy.reflection; import com.googlecode.totallylazy.Objects; import com.googlecode.totallylazy.Option; import com.googlecode.totallylazy.Pair; import com.googlecode.totallylazy.predicates.Predicate; import com.googlecode.totallylazy.Sequence; import com.googlecode.totallylazy.Unchecked; import com.googlecode.totallylazy.numbers.Numbers; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import static com.googlecode.totallylazy.Option.none; import static com.googlecode.totallylazy.Option.some; import static com.googlecode.totallylazy.Sequences.sequence; public class Types { public static ParameterizedType parameterizedType(final Type rawType, final Type... typeArguments) { return new AParameterizedType(null, rawType, typeArguments); } public static ParameterizedType parameterizedType(final Type rawType, final Iterable<Type> typeArguments) { return new AParameterizedType(null, rawType, sequence(typeArguments).toArray(Type.class)); } public static Sequence<Type> classTypeParameters(Type concrete) { if (concrete instanceof Class) { return sequence(((Class) concrete).getTypeParameters()).safeCast(Type.class); } if (concrete instanceof ParameterizedType) { return sequence(((ParameterizedType) concrete).getActualTypeArguments()); } throw new UnsupportedOperationException(); } public static <T> Class<T> classOf(Type concrete) { return Types.<T>classOption(concrete).getOrThrow(new UnsupportedOperationException()); } public static <T> Option<Class<T>> classOption(Type concrete) { if (concrete instanceof Class) { return some(Unchecked.<Class<T>>cast(concrete)); } if (concrete instanceof ParameterizedType) { return some(Types.<T>classOf(((ParameterizedType) concrete).getRawType())); } return none(); } public static boolean equalTo(Type a, Type b) { if (a == null && b == null) { return true; } if (a instanceof ParameterizedType && b instanceof ParameterizedType) { return equalTo((ParameterizedType) a, (ParameterizedType) b); } if (a instanceof WildcardType && b instanceof WildcardType) { return equalTo((WildcardType) a, (WildcardType) b); } return Objects.equalTo(a, b); } public static boolean equalTo(WildcardType aWildcard, WildcardType bWildcard) { return sequence(aWildcard.getUpperBounds()).zip(sequence(bWildcard.getUpperBounds())).forAll(equalTo()) && sequence(aWildcard.getLowerBounds()).zip(sequence(bWildcard.getLowerBounds())).forAll(equalTo()); } public static boolean equalTo(ParameterizedType pa, ParameterizedType pb) { return equalTo(pa.getOwnerType(), pb.getOwnerType()) && equalTo(pa.getRawType(), pb.getRawType()) && sequence(pa.getActualTypeArguments()).zip(sequence(pb.getActualTypeArguments())).forAll(equalTo()); } public static Predicate<? super Pair<Type, Type>> equalTo() { return pair -> equalTo(pair.first(), pair.second()); } public static Predicate<? super Type> equalTo(final Type type) { return other -> equalTo(type, other); } public static boolean matches(Type concrete, Type possibleWildCard) { if (concrete == null && possibleWildCard == null) { return true; } if (concrete instanceof ParameterizedType && possibleWildCard instanceof ParameterizedType) { return matches((ParameterizedType) concrete, (ParameterizedType) possibleWildCard); } if (concrete instanceof WildcardType && possibleWildCard instanceof WildcardType) { return matches((WildcardType) concrete, (WildcardType) possibleWildCard); } if (possibleWildCard instanceof WildcardType) { return withinBounds(concrete, (WildcardType) possibleWildCard); } return Objects.equalTo(concrete, possibleWildCard); } public static boolean matches(WildcardType aWildcard, WildcardType bWildcard) { return sequence(aWildcard.getUpperBounds()).zip(sequence(bWildcard.getUpperBounds())).forAll(matches()) && sequence(aWildcard.getLowerBounds()).zip(sequence(bWildcard.getLowerBounds())).forAll(matches()); } public static boolean matches(ParameterizedType type, ParameterizedType anotherType) { return matches(type.getOwnerType(), anotherType.getOwnerType()) && matches(type.getRawType(), anotherType.getRawType()) && sequence(type.getActualTypeArguments()).zip(sequence(anotherType.getActualTypeArguments())).forAll(matches()); } public static boolean withinBounds(Type concrete, WildcardType wildcardType) { return withInUpperBounds(concrete, sequence(wildcardType.getUpperBounds())) && withInLowerBounds(concrete, sequence(wildcardType.getLowerBounds())); } @SuppressWarnings("unchecked") public static boolean withInUpperBounds(Type concrete, Sequence<Type> upperBounds) { if (upperBounds.isEmpty()) { return true; } if (Numbers.equalTo(upperBounds.size(), 1)) { return (classOf(upperBounds.first())).isAssignableFrom(classOf(concrete)); } throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") public static boolean withInLowerBounds(Type concrete, Sequence<Type> lowerBounds) { if (lowerBounds.isEmpty()) { return true; } if (Numbers.equalTo(lowerBounds.size(), 1)) { return (classOf(concrete)).isAssignableFrom(classOf(lowerBounds.first())); } throw new UnsupportedOperationException(); } public static Predicate<? super Pair<Type, Type>> matches() { return pair -> matches(pair.first(), pair.second()); } public static Predicate<? super Type> matches(final Type type) { return other -> matches(type, other); } }