diff --git a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java index d7f965c..b2bb8fa 100644 --- a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java +++ b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java @@ -24,6 +24,8 @@ import java.util.Set; import java.util.stream.Stream; +import static java.util.Arrays.stream; + /** * Utility class for doing reflection on types. * @@ -31,492 +33,569 @@ * @author Bojan Tomic {@literal (veggen@gmail.com)} */ public class GenericTypeReflector { - private static final WildcardType UNBOUND_WILDCARD = new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{}); - - /** - * Returns the erasure of the given type. - */ - public static Class erase(Type type) { - if (type instanceof Class) { - return (Class)type; - } else if (type instanceof ParameterizedType) { - return (Class)((ParameterizedType) type).getRawType(); - } else if (type instanceof TypeVariable) { - TypeVariable tv = (TypeVariable)type; - if (tv.getBounds().length == 0) - return Object.class; - else - return erase(tv.getBounds()[0]); - } else if (type instanceof GenericArrayType) { - GenericArrayType aType = (GenericArrayType) type; - return GenericArrayTypeImpl.createArrayType(erase(aType.getGenericComponentType())); - } else if (type instanceof WildcardType) { - WildcardType wildcardType = (WildcardType) type; - Type[] lowerBounds = wildcardType.getLowerBounds(); - return erase(lowerBounds.length > 0 ? lowerBounds[0] : wildcardType.getUpperBounds()[0]); - } else { - throw new RuntimeException("not supported: " + type.getClass()); - } - } - - /** - * Maps type parameters in a type to their values. - * @param toMapType Type possibly containing type arguments - * @param typeAndParams must be either ParameterizedType, or (in case there are no type arguments, or it's a raw type) Class - * @return toMapType, but with type parameters from typeAndParams replaced. - */ - public static AnnotatedType mapTypeParameters(AnnotatedType toMapType, AnnotatedType typeAndParams) { - if (isMissingTypeParameters(typeAndParams.getType())) { - return new AnnotatedTypeImpl(erase(toMapType.getType()), toMapType.getAnnotations()); - } else { - VarMap varMap = new VarMap(); - AnnotatedType handlingTypeAndParams = typeAndParams; - while(handlingTypeAndParams instanceof AnnotatedParameterizedType) { - AnnotatedParameterizedType pType = (AnnotatedParameterizedType)handlingTypeAndParams; - Class clazz = (Class)((ParameterizedType) pType.getType()).getRawType(); // getRawType should always be Class - TypeVariable[] vars = clazz.getTypeParameters(); - varMap.addAll(vars, pType.getAnnotatedActualTypeArguments()); - Type owner = ((ParameterizedType) pType.getType()).getOwnerType(); - handlingTypeAndParams = owner == null ? null : annotate(owner); - } - return varMap.map(toMapType); - } - } - - /** - * Checks if the given type is a class that is supposed to have type parameters, but doesn't. - * In other words, if it's a really raw type. - */ - public static boolean isMissingTypeParameters(Type type) { - if (type instanceof Class) { - for (Class clazz = (Class) type; clazz != null; clazz = clazz.getEnclosingClass()) { - if (clazz.getTypeParameters().length != 0) - return true; - } - return false; - } else if (type instanceof ParameterizedType) { - return false; - } else { - throw new AssertionError("Unexpected type " + type.getClass()); - } - } - - /** - * Returns a type representing the class, with all type parameters the unbound wildcard ("?"). - * For example, {@code addWildcardParameters(Map.class)} returns a type representing {@code Map}. - * @return - */ - public static Type addWildcardParameters(Class clazz) { - if (clazz.isArray()) { - return GenericArrayTypeImpl.createArrayType(addWildcardParameters(clazz.getComponentType())); - } else if (isMissingTypeParameters(clazz)) { - TypeVariable[] vars = clazz.getTypeParameters(); - Type[] arguments = new Type[vars.length]; - Arrays.fill(arguments, UNBOUND_WILDCARD); - Type owner = clazz.getDeclaringClass() == null ? null : addWildcardParameters(clazz.getDeclaringClass()); - return new ParameterizedTypeImpl(clazz, arguments, owner); - } else { - return clazz; - } - } - - /** - * Finds the most specific supertype of {@code type} whose erasure is {@code searchClass}. - * In other words, returns a type representing the class {@code searchClass} plus its exact type parameters in {@code type}. - * - * - * - *

For example, with {@code class StringList implements List}, {@code getExactSuperType(StringList.class, Collection.class)} - * returns a {@link ParameterizedType} representing {@code Collection}. - *

- */ - public static AnnotatedType getExactSuperType(AnnotatedType type, Class searchClass) { - if (type instanceof AnnotatedParameterizedType || type.getType() instanceof Class || type instanceof AnnotatedArrayType) { - Class clazz = erase(type.getType()); - - if (searchClass == clazz) { - return type; - } - - if (! searchClass.isAssignableFrom(clazz)) - return null; - } - - for (AnnotatedType superType: getExactDirectSuperTypes(type)) { - AnnotatedType result = getExactSuperType(superType, searchClass); - if (result != null) - return result; - } - - return null; - } - - public static Type getExactSuperType(Type type, Class searchClass) { - AnnotatedType superType = getExactSuperType(annotate(type), searchClass); - return superType == null ? null : superType.getType(); - } - - /** - * Gets the type parameter for a given type that is the value for a given type variable. - * For example, with {@code class StringList implements List}, - * {@code getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0])} - * returns {@code String}. - * - * @param type The type to inspect. - * @param variable The type variable to find the value for. - * @return The type parameter for the given variable. Or null if type is not a subtype of the - * type that declares the variable, or if the variable isn't known (because of raw types). - */ - public static AnnotatedType getTypeParameter(AnnotatedType type, TypeVariable> variable) { - Class clazz = variable.getGenericDeclaration(); - AnnotatedType superType = getExactSuperType(type, clazz); - if (superType instanceof AnnotatedParameterizedType) { - int index = Arrays.asList(clazz.getTypeParameters()).indexOf(variable); - AnnotatedType resolvedVarType = ((AnnotatedParameterizedType)superType).getAnnotatedActualTypeArguments()[index]; - return updateAnnotations(resolvedVarType, variable.getAnnotations()); - } else { - return null; - } - } - - public static Type getTypeParameter(Type type, TypeVariable> variable) { - return getTypeParameter(annotate(type), variable).getType(); - } - - /** - * Checks if the capture of subType is a subtype of superType - */ - public static boolean isSuperType(Type superType, Type subType) { - if (superType instanceof ParameterizedType || superType instanceof Class || superType instanceof GenericArrayType) { - Class superClass = erase(superType); - AnnotatedType annotatedMappedSubType = getExactSuperType(capture(annotate(subType)), superClass); - Type mappedSubType = annotatedMappedSubType == null ? null : annotatedMappedSubType.getType(); - if (mappedSubType == null) { - return false; - } else if (superType instanceof Class) { - return true; - } else if (mappedSubType instanceof Class) { - // TODO treat supertype by being raw type differently ("supertype, but with warnings") - return true; // class has no parameters, or it's a raw type - } else if (mappedSubType instanceof GenericArrayType) { - Type superComponentType = getArrayComponentType(superType); - assert superComponentType != null; - Type mappedSubComponentType = getArrayComponentType(mappedSubType); - assert mappedSubComponentType != null; - return isSuperType(superComponentType, mappedSubComponentType); - } else { - assert mappedSubType instanceof ParameterizedType; - ParameterizedType pMappedSubType = (ParameterizedType) mappedSubType; - assert pMappedSubType.getRawType() == superClass; - ParameterizedType pSuperType = (ParameterizedType)superType; - - Type[] superTypeArgs = pSuperType.getActualTypeArguments(); - Type[] subTypeArgs = pMappedSubType.getActualTypeArguments(); - assert superTypeArgs.length == subTypeArgs.length; - for (int i = 0; i < superTypeArgs.length; i++) { - if (! contains(superTypeArgs[i], subTypeArgs[i])) { - return false; - } - } - // params of the class itself match, so if the owner types are supertypes too, it's a supertype. - return pSuperType.getOwnerType() == null || isSuperType(pSuperType.getOwnerType(), pMappedSubType.getOwnerType()); - } - } else if (superType instanceof CaptureType) { - if (superType.equals(subType)) - return true; - for (Type lowerBound : ((CaptureType) superType).getLowerBounds()) { - if (isSuperType(lowerBound, subType)) { - return true; - } - } - return false; + private static final WildcardType UNBOUND_WILDCARD = new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{}); + + /** + * Returns the erasure of the given type. + */ + public static Class erase(Type type) { + if (type instanceof Class) { + return (Class)type; + } else if (type instanceof ParameterizedType) { + return (Class)((ParameterizedType) type).getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable)type; + if (tv.getBounds().length == 0) + return Object.class; + else + return erase(tv.getBounds()[0]); + } else if (type instanceof GenericArrayType) { + GenericArrayType aType = (GenericArrayType) type; + return GenericArrayTypeImpl.createArrayType(erase(aType.getGenericComponentType())); + } else if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + Type[] lowerBounds = wildcardType.getLowerBounds(); + return erase(lowerBounds.length > 0 ? lowerBounds[0] : wildcardType.getUpperBounds()[0]); + } else { + throw new RuntimeException("not supported: " + type.getClass()); + } + } + + /** + * Maps type parameters in a type to their values. + * @param toMapType Type possibly containing type arguments + * @param typeAndParams must be either ParameterizedType, or (in case there are no type arguments, or it's a raw type) Class + * @return toMapType, but with type parameters from typeAndParams replaced. + */ + public static AnnotatedType mapTypeParameters(AnnotatedType toMapType, AnnotatedType typeAndParams) { + if (isMissingTypeParameters(typeAndParams.getType())) { + return new AnnotatedTypeImpl(erase(toMapType.getType()), toMapType.getAnnotations()); + } else { + VarMap varMap = new VarMap(); + AnnotatedType handlingTypeAndParams = typeAndParams; + while(handlingTypeAndParams instanceof AnnotatedParameterizedType) { + AnnotatedParameterizedType pType = (AnnotatedParameterizedType)handlingTypeAndParams; + Class clazz = (Class)((ParameterizedType) pType.getType()).getRawType(); // getRawType should always be Class + TypeVariable[] vars = clazz.getTypeParameters(); + varMap.addAll(vars, pType.getAnnotatedActualTypeArguments()); + Type owner = ((ParameterizedType) pType.getType()).getOwnerType(); + handlingTypeAndParams = owner == null ? null : annotate(owner); + } + return varMap.map(toMapType); + } + } + + /** + * Checks if the given type is a class that is supposed to have type parameters, but doesn't. + * In other words, if it's a really raw type. + */ + public static boolean isMissingTypeParameters(Type type) { + if (type instanceof Class) { + for (Class clazz = (Class) type; clazz != null; clazz = clazz.getEnclosingClass()) { + if (clazz.getTypeParameters().length != 0) + return true; + } + return false; + } else if (type instanceof ParameterizedType) { + return false; + } else { + throw new AssertionError("Unexpected type " + type.getClass()); + } + } + + /** + * Returns a type representing the class, with all type parameters the unbound wildcard ("?"). + * For example, {@code addWildcardParameters(Map.class)} returns a type representing {@code Map}. + * @return + */ + public static Type addWildcardParameters(Class clazz) { + if (clazz.isArray()) { + return GenericArrayTypeImpl.createArrayType(addWildcardParameters(clazz.getComponentType())); + } else if (isMissingTypeParameters(clazz)) { + TypeVariable[] vars = clazz.getTypeParameters(); + Type[] arguments = new Type[vars.length]; + Arrays.fill(arguments, UNBOUND_WILDCARD); + Type owner = clazz.getDeclaringClass() == null ? null : addWildcardParameters(clazz.getDeclaringClass()); + return new ParameterizedTypeImpl(clazz, arguments, owner); + } else { + return clazz; + } + } + + /** + * The equivalent of {@link #getExactSuperType(Type, Class)} but works with {@link AnnotatedType}s + * + * @param subType The type whose supertype is to be searched for + * @param searchSuperClass The class of the supertype to search for + * + * @return The annotated type representing {@code searchSuperClass} with type parameters from {@code subType} + */ + public static AnnotatedType getExactSuperType(AnnotatedType subType, Class searchSuperClass) { + if (subType instanceof AnnotatedParameterizedType || subType.getType() instanceof Class || subType instanceof AnnotatedArrayType) { + Class superClass = erase(subType.getType()); + + if (searchSuperClass == superClass) { + return subType; + } + + if (! searchSuperClass.isAssignableFrom(superClass)) + return null; + } + + for (AnnotatedType superType: getExactDirectSuperTypes(subType)) { + AnnotatedType result = getExactSuperType(superType, searchSuperClass); + if (result != null) + return result; + } + + return null; + } + + /** + * Finds the most specific supertype of {@code subType} whose erasure is {@code searchSuperClass}. + * In other words, returns a type representing the class {@code searchSuperClass} plus its exact type parameters in {@code subType}. + * + * + * + *

For example, with {@code class StringList implements List}, {@code getExactSuperType(StringList.class, Collection.class)} + * returns a {@link ParameterizedType} representing {@code Collection}. + *

+ * + * @param subType The type whose supertype is to be searched for + * @param searchSuperClass The class of the supertype to search for + * + * @return The type representing {@code searchSuperClass} with type parameters from {@code subType} + */ + public static Type getExactSuperType(Type subType, Class searchSuperClass) { + AnnotatedType superType = getExactSuperType(annotate(subType), searchSuperClass); + return superType == null ? null : superType.getType(); + } + + /** + * The equivalent of {@link #getExactSubType(Type, Class)} but works with {@link AnnotatedType}s + * + * @param superType The type whose subtype is to be searched for + * @param searchSubClass The class of the subtype to search for + * + * @return The annotated type representing {@code searchSubClass} with type parameters from {@code superType} + */ + public static AnnotatedType getExactSubType(AnnotatedType superType, Class searchSubClass) { + Type subType = searchSubClass; + if (searchSubClass.getTypeParameters().length > 0) { + subType = TypeFactory.parameterizedClass(searchSubClass, (Type[]) searchSubClass.getTypeParameters()); + } + AnnotatedType annotatedSubType = annotate(subType); + Class rawSuperType = erase(superType.getType()); + if (searchSubClass.isArray() && superType instanceof AnnotatedArrayType) { + if (rawSuperType.isAssignableFrom(searchSubClass)) { + return AnnotatedArrayTypeImpl.createArrayType(getExactSubType(((AnnotatedArrayType) superType).getAnnotatedGenericComponentType(), searchSubClass.getComponentType()), new Annotation[0]); + } else { + return null; + } + } + if (searchSubClass.getTypeParameters().length == 0) { + return annotatedSubType; + } + AnnotatedParameterizedType parameterizedSuperType = (AnnotatedParameterizedType) superType; + AnnotatedParameterizedType matched = (AnnotatedParameterizedType) getExactSuperType(annotatedSubType, rawSuperType); + if (matched == null) return null; + VarMap varMap = new VarMap(); + for (int i = 0; i < parameterizedSuperType.getAnnotatedActualTypeArguments().length; i++) { + Type var = matched.getAnnotatedActualTypeArguments()[i].getType(); + if (var instanceof TypeVariable && ((TypeVariable) var).getGenericDeclaration() == searchSubClass) { + varMap.add(((TypeVariable) var), parameterizedSuperType.getAnnotatedActualTypeArguments()[i]); + } + } + try { + return varMap.map(annotatedSubType); + } catch (UnresolvedTypeVariableException e) { + return annotate(searchSubClass); + } + } + + /** + * Finds the most specific subtype of {@code superType} whose erasure is {@code searchSubClass}. + * In other words, returns a type representing the class {@code searchSubClass} plus its exact type parameters in {@code superType}, + * if they are possible to resolve. + * + * + * + *

For example, with {@code getExactSubType(new TypeToken>(){}.getAnnotatedType(), ArrayList.class)} + * returns a {@link AnnotatedParameterizedType} representing {@code ArrayList}. + *

+ */ + public static Type getExactSubType(Type superType, Class searchSubClass) { + AnnotatedType resolvedSubtype = getExactSubType(annotate(superType), searchSubClass); + return resolvedSubtype == null ? null : resolvedSubtype.getType(); + } + + /** + * Gets the type parameter for a given type that is the value for a given type variable. + * For example, with {@code class StringList implements List}, + * {@code getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0])} + * returns {@code String}. + * + * @param type The type to inspect. + * @param variable The type variable to find the value for. + * @return The type parameter for the given variable. Or null if type is not a subtype of the + * type that declares the variable, or if the variable isn't known (because of raw types). + */ + public static AnnotatedType getTypeParameter(AnnotatedType type, TypeVariable> variable) { + Class clazz = variable.getGenericDeclaration(); + AnnotatedType superType = getExactSuperType(type, clazz); + if (superType instanceof AnnotatedParameterizedType) { + int index = Arrays.asList(clazz.getTypeParameters()).indexOf(variable); + AnnotatedType resolvedVarType = ((AnnotatedParameterizedType)superType).getAnnotatedActualTypeArguments()[index]; + return updateAnnotations(resolvedVarType, variable.getAnnotations()); + } else { + return null; + } + } + + public static Type getTypeParameter(Type type, TypeVariable> variable) { + AnnotatedType typeParameter = getTypeParameter(annotate(type), variable); + return typeParameter == null ? null : typeParameter.getType(); + } + + /** + * Checks if the capture of subType is a subtype of superType + */ + public static boolean isSuperType(Type superType, Type subType) { + if (superType instanceof ParameterizedType || superType instanceof Class || superType instanceof GenericArrayType) { + Class superClass = erase(superType); + AnnotatedType annotatedMappedSubType = getExactSuperType(capture(annotate(subType)), superClass); + Type mappedSubType = annotatedMappedSubType == null ? null : annotatedMappedSubType.getType(); + if (mappedSubType == null) { + return false; + } else if (superType instanceof Class) { + return true; + } else if (mappedSubType instanceof Class) { + // TODO treat supertype by being raw type differently ("supertype, but with warnings") + return true; // class has no parameters, or it's a raw type + } else if (mappedSubType instanceof GenericArrayType) { + Type superComponentType = getArrayComponentType(superType); + assert superComponentType != null; + Type mappedSubComponentType = getArrayComponentType(mappedSubType); + assert mappedSubComponentType != null; + return isSuperType(superComponentType, mappedSubComponentType); + } else { + assert mappedSubType instanceof ParameterizedType; + ParameterizedType pMappedSubType = (ParameterizedType) mappedSubType; + assert pMappedSubType.getRawType() == superClass; + ParameterizedType pSuperType = (ParameterizedType)superType; + + Type[] superTypeArgs = pSuperType.getActualTypeArguments(); + Type[] subTypeArgs = pMappedSubType.getActualTypeArguments(); + assert superTypeArgs.length == subTypeArgs.length; + for (int i = 0; i < superTypeArgs.length; i++) { + if (! contains(superTypeArgs[i], subTypeArgs[i])) { + return false; + } + } + // params of the class itself match, so if the owner types are supertypes too, it's a supertype. + return pSuperType.getOwnerType() == null || isSuperType(pSuperType.getOwnerType(), pMappedSubType.getOwnerType()); + } + } else if (superType instanceof CaptureType) { + if (superType.equals(subType)) + return true; + for (Type lowerBound : ((CaptureType) superType).getLowerBounds()) { + if (isSuperType(lowerBound, subType)) { + return true; + } + } + return false; // } else if (superType instanceof GenericArrayType) { // return isArraySupertype(superType, subType); - } else { - throw new RuntimeException("Type not supported: " + superType.getClass()); - } - } - - private static boolean isArraySupertype(Type arraySuperType, Type subType) { - Type superTypeComponent = getArrayComponentType(arraySuperType); - assert superTypeComponent != null; - Type subTypeComponent = getArrayComponentType(subType); - if (subTypeComponent == null) { // subType is not an array type - return false; - } else { - return isSuperType(superTypeComponent, subTypeComponent); - } - } - - /** - * If type is an array type, returns the type of the component of the array. - * Otherwise, returns null. - */ - public static AnnotatedType getArrayComponentType(AnnotatedType type) { - if (type.getType() instanceof Class) { - Class clazz = (Class)type.getType(); - return new AnnotatedTypeImpl(clazz.getComponentType(), clazz.getAnnotations()); - } else if (type instanceof AnnotatedArrayType) { - AnnotatedArrayType aType = (AnnotatedArrayType) type; - return aType.getAnnotatedGenericComponentType(); - } else { - return null; - } - } - - public static Type getArrayComponentType(Type type) { - return getArrayComponentType(annotate(type)).getType(); - } - - private static boolean contains(Type containingType, Type containedType) { - if (containingType instanceof WildcardType) { - WildcardType wContainingType = (WildcardType)containingType; - for (Type upperBound : wContainingType.getUpperBounds()) { - if (! isSuperType(upperBound, containedType)) { - return false; - } - } - for (Type lowerBound : wContainingType.getLowerBounds()) { - if (! isSuperType(containedType, lowerBound)) { - return false; - } - } - return true; - } else { - return containingType.equals(containedType); - } - } - - /** - * Returns the direct supertypes of the given type. Resolves type parameters. - */ - private static AnnotatedType[] getExactDirectSuperTypes(AnnotatedType type) { - if (type instanceof AnnotatedParameterizedType || (type != null && type.getType() instanceof Class)) { - Class clazz; - if (type instanceof AnnotatedParameterizedType) { - clazz = (Class)((ParameterizedType)type.getType()).getRawType(); - } else { - // TODO primitive types? - clazz = (Class)type.getType(); - if (clazz.isArray()) - return getArrayExactDirectSuperTypes(annotate(clazz)); - } - - AnnotatedType[] superInterfaces = clazz.getAnnotatedInterfaces(); - AnnotatedType superClass = clazz.getAnnotatedSuperclass(); - - // the only supertype of an interface without superinterfaces is Object - if (superClass == null && superInterfaces.length == 0 && clazz.isInterface()) { - return new AnnotatedType[] {new AnnotatedTypeImpl(Object.class)}; - } - - AnnotatedType[] result; - int resultIndex; - if (superClass == null) { - result = new AnnotatedType[superInterfaces.length]; - resultIndex = 0; - } else { - result = new AnnotatedType[superInterfaces.length + 1]; - resultIndex = 1; + } else { + throw new RuntimeException("Type not supported: " + superType.getClass()); + } + } + + private static boolean isArraySupertype(Type arraySuperType, Type subType) { + Type superTypeComponent = getArrayComponentType(arraySuperType); + assert superTypeComponent != null; + Type subTypeComponent = getArrayComponentType(subType); + if (subTypeComponent == null) { // subType is not an array type + return false; + } else { + return isSuperType(superTypeComponent, subTypeComponent); + } + } + + /** + * If type is an array type, returns the type of the component of the array. + * Otherwise, returns null. + */ + public static AnnotatedType getArrayComponentType(AnnotatedType type) { + if (type.getType() instanceof Class) { + Class clazz = (Class)type.getType(); + return new AnnotatedTypeImpl(clazz.getComponentType(), clazz.getAnnotations()); + } else if (type instanceof AnnotatedArrayType) { + AnnotatedArrayType aType = (AnnotatedArrayType) type; + return aType.getAnnotatedGenericComponentType(); + } else { + return null; + } + } + + public static Type getArrayComponentType(Type type) { + return getArrayComponentType(annotate(type)).getType(); + } + + private static boolean contains(Type containingType, Type containedType) { + if (containingType instanceof WildcardType) { + WildcardType wContainingType = (WildcardType)containingType; + for (Type upperBound : wContainingType.getUpperBounds()) { + if (! isSuperType(upperBound, containedType)) { + return false; + } + } + for (Type lowerBound : wContainingType.getLowerBounds()) { + if (! isSuperType(containedType, lowerBound)) { + return false; + } + } + return true; + } else { + return containingType.equals(containedType); + } + } + + /** + * Returns the direct supertypes of the given type. Resolves type parameters. + */ + private static AnnotatedType[] getExactDirectSuperTypes(AnnotatedType type) { + if (type instanceof AnnotatedParameterizedType || (type != null && type.getType() instanceof Class)) { + Class clazz; + if (type instanceof AnnotatedParameterizedType) { + clazz = (Class)((ParameterizedType)type.getType()).getRawType(); + } else { + // TODO primitive types? + clazz = (Class)type.getType(); + if (clazz.isArray()) + return getArrayExactDirectSuperTypes(annotate(clazz)); + } + + AnnotatedType[] superInterfaces = clazz.getAnnotatedInterfaces(); + AnnotatedType superClass = clazz.getAnnotatedSuperclass(); + + // the only supertype of an interface without superinterfaces is Object + if (superClass == null && superInterfaces.length == 0 && clazz.isInterface()) { + return new AnnotatedType[] {new AnnotatedTypeImpl(Object.class)}; + } + + AnnotatedType[] result; + int resultIndex; + if (superClass == null) { + result = new AnnotatedType[superInterfaces.length]; + resultIndex = 0; + } else { + result = new AnnotatedType[superInterfaces.length + 1]; + resultIndex = 1; result[0] = mapTypeParameters(superClass, type); - } - for (AnnotatedType superInterface : superInterfaces) { - result[resultIndex++] = mapTypeParameters(superInterface, type); - } - - return result; - } else if (type instanceof AnnotatedTypeVariable) { - AnnotatedTypeVariable tv = (AnnotatedTypeVariable) type; - return tv.getAnnotatedBounds(); - } else if (type instanceof AnnotatedWildcardType) { - // This should be a rare case: normally this wildcard is already captured. - // But it does happen if the upper bound of a type variable contains a wildcard - // TODO shouldn't upper bound of type variable have been captured too? (making this case impossible?) - return ((AnnotatedWildcardType) type).getAnnotatedUpperBounds(); - } else if (type instanceof AnnotatedCaptureTypeImpl) { - return ((AnnotatedCaptureTypeImpl)type).getAnnotatedUpperBounds(); - } else if (type instanceof AnnotatedArrayType) { - return getArrayExactDirectSuperTypes(type); - } else if (type == null) { - throw new NullPointerException(); - } else { - throw new RuntimeException("not implemented type: " + type); - } - } - - private static AnnotatedType[] getArrayExactDirectSuperTypes(AnnotatedType arrayType) { - // see http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.3 - AnnotatedType typeComponent = getArrayComponentType(arrayType); - - AnnotatedType[] result; - int resultIndex; - if (typeComponent != null && typeComponent.getType() instanceof Class && ((Class)typeComponent.getType()).isPrimitive()) { - resultIndex = 0; - result = new AnnotatedType[3]; - } else { - AnnotatedType[] componentSupertypes = getExactDirectSuperTypes(typeComponent); - result = new AnnotatedType[componentSupertypes.length + 3]; - for (resultIndex = 0; resultIndex < componentSupertypes.length; resultIndex++) { - result[resultIndex] = AnnotatedArrayTypeImpl.createArrayType(componentSupertypes[resultIndex], new Annotation[0]); - } - } - result[resultIndex++] = new AnnotatedTypeImpl(Object.class); - result[resultIndex++] = new AnnotatedTypeImpl(Cloneable.class); - result[resultIndex++] = new AnnotatedTypeImpl(Serializable.class); - return result; - } - - /** - * Returns the exact return type of the given method in the given type. - * This may be different from {@code m.getGenericReturnType()} when the method was declared in a superclass, - * or {@code declaringType} has a type parameter that is used in the return type, or {@code declaringType} is a raw type. - */ - public static AnnotatedType getExactReturnType(Method m, AnnotatedType declaringType) { - AnnotatedType returnType = m.getAnnotatedReturnType(); - AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), m.getDeclaringClass()); - if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() - throw new IllegalArgumentException("The method " + m + " is not a member of type " + declaringType); - } - return mapTypeParameters(returnType, exactDeclaringType); - } - - public static Type getExactReturnType(Method m, Type declaringType) { - return getExactReturnType(m, annotate(declaringType)).getType(); - } - - /** - * Returns the exact type of the given field in the given type. - * This may be different from {@code f.getGenericType()} when the field was declared in a superclass, - * or {@code declaringType} has a type parameter that is used in the type of the field, or {@code declaringType} is a raw type. - */ - public static AnnotatedType getExactFieldType(Field f, AnnotatedType declaringType) { - AnnotatedType returnType = f.getAnnotatedType(); - AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), f.getDeclaringClass()); - if (exactDeclaringType == null) { // capture(type) is not a subtype of f.getDeclaringClass() - throw new IllegalArgumentException("The field " + f + " is not a member of type " + declaringType); - } - return mapTypeParameters(returnType, exactDeclaringType); - } - - public static Type getExactFieldType(Field f, Type type) { - return getExactFieldType(f, annotate(type)).getType(); - } - - /** - * Returns the exact parameter types of the given method in the given type. - * This may be different from {@code m.getGenericParameterTypes()} when the method was declared in a superclass, - * or {@code declaringType} has a type parameter that is used in one of the parameters, or {@code declaringType} is a raw type. - */ - public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType declaringType) { - AnnotatedType[] parameterTypes = m.getAnnotatedParameterTypes(); - AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), m.getDeclaringClass()); - if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() - throw new IllegalArgumentException("The method " + m + " is not a member of type " + declaringType); - } - - AnnotatedType[] result = new AnnotatedType[parameterTypes.length]; - for (int i = 0; i < parameterTypes.length; i++) { - result[i] = mapTypeParameters(parameterTypes[i], exactDeclaringType); - } - return result; - } - - public static Type[] getExactParameterTypes(Method m, Type declaringType) { - return Arrays.stream(getExactParameterTypes(m, annotate(declaringType))).map(AnnotatedType::getType).toArray(Type[]::new); - } - - /** - * Applies capture conversion to the given type. - */ - public static AnnotatedType capture(AnnotatedType type) { - if (type instanceof AnnotatedParameterizedType) { - return capture((AnnotatedParameterizedType)type); - } else { - return type; - } - } + } + for (AnnotatedType superInterface : superInterfaces) { + result[resultIndex++] = mapTypeParameters(superInterface, type); + } + + return result; + } else if (type instanceof AnnotatedTypeVariable) { + AnnotatedTypeVariable tv = (AnnotatedTypeVariable) type; + return tv.getAnnotatedBounds(); + } else if (type instanceof AnnotatedWildcardType) { + // This should be a rare case: normally this wildcard is already captured. + // But it does happen if the upper bound of a type variable contains a wildcard + // TODO shouldn't upper bound of type variable have been captured too? (making this case impossible?) + return ((AnnotatedWildcardType) type).getAnnotatedUpperBounds(); + } else if (type instanceof AnnotatedCaptureTypeImpl) { + return ((AnnotatedCaptureTypeImpl)type).getAnnotatedUpperBounds(); + } else if (type instanceof AnnotatedArrayType) { + return getArrayExactDirectSuperTypes(type); + } else if (type == null) { + throw new NullPointerException(); + } else { + throw new RuntimeException("not implemented type: " + type); + } + } + + private static AnnotatedType[] getArrayExactDirectSuperTypes(AnnotatedType arrayType) { + // see http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.3 + AnnotatedType typeComponent = getArrayComponentType(arrayType); + + AnnotatedType[] result; + int resultIndex; + if (typeComponent != null && typeComponent.getType() instanceof Class && ((Class)typeComponent.getType()).isPrimitive()) { + resultIndex = 0; + result = new AnnotatedType[3]; + } else { + AnnotatedType[] componentSupertypes = getExactDirectSuperTypes(typeComponent); + result = new AnnotatedType[componentSupertypes.length + 3]; + for (resultIndex = 0; resultIndex < componentSupertypes.length; resultIndex++) { + result[resultIndex] = AnnotatedArrayTypeImpl.createArrayType(componentSupertypes[resultIndex], new Annotation[0]); + } + } + result[resultIndex++] = new AnnotatedTypeImpl(Object.class); + result[resultIndex++] = new AnnotatedTypeImpl(Cloneable.class); + result[resultIndex++] = new AnnotatedTypeImpl(Serializable.class); + return result; + } + + /** + * Returns the exact return type of the given method in the given type. + * This may be different from {@code m.getGenericReturnType()} when the method was declared in a superclass, + * or {@code declaringType} has a type parameter that is used in the return type, or {@code declaringType} is a raw type. + */ + public static AnnotatedType getExactReturnType(Method m, AnnotatedType declaringType) { + AnnotatedType returnType = m.getAnnotatedReturnType(); + AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), m.getDeclaringClass()); + if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() + throw new IllegalArgumentException("The method " + m + " is not a member of type " + declaringType); + } + return mapTypeParameters(returnType, exactDeclaringType); + } + + public static Type getExactReturnType(Method m, Type declaringType) { + return getExactReturnType(m, annotate(declaringType)).getType(); + } + + /** + * Returns the exact type of the given field in the given type. + * This may be different from {@code f.getGenericType()} when the field was declared in a superclass, + * or {@code declaringType} has a type parameter that is used in the type of the field, or {@code declaringType} is a raw type. + */ + public static AnnotatedType getExactFieldType(Field f, AnnotatedType declaringType) { + AnnotatedType returnType = f.getAnnotatedType(); + AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), f.getDeclaringClass()); + if (exactDeclaringType == null) { // capture(type) is not a subtype of f.getDeclaringClass() + throw new IllegalArgumentException("The field " + f + " is not a member of type " + declaringType); + } + return mapTypeParameters(returnType, exactDeclaringType); + } + + public static Type getExactFieldType(Field f, Type type) { + return getExactFieldType(f, annotate(type)).getType(); + } + + /** + * Returns the exact parameter types of the given method in the given type. + * This may be different from {@code m.getGenericParameterTypes()} when the method was declared in a superclass, + * or {@code declaringType} has a type parameter that is used in one of the parameters, or {@code declaringType} is a raw type. + */ + public static AnnotatedType[] getExactParameterTypes(Method m, AnnotatedType declaringType) { + AnnotatedType[] parameterTypes = m.getAnnotatedParameterTypes(); + AnnotatedType exactDeclaringType = getExactSuperType(capture(declaringType), m.getDeclaringClass()); + if (exactDeclaringType == null) { // capture(type) is not a subtype of m.getDeclaringClass() + throw new IllegalArgumentException("The method " + m + " is not a member of type " + declaringType); + } + + AnnotatedType[] result = new AnnotatedType[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + result[i] = mapTypeParameters(parameterTypes[i], exactDeclaringType); + } + return result; + } + + public static Type[] getExactParameterTypes(Method m, Type declaringType) { + return stream(getExactParameterTypes(m, annotate(declaringType))).map(AnnotatedType::getType).toArray(Type[]::new); + } + + /** + * Applies capture conversion to the given type. + */ + public static AnnotatedType capture(AnnotatedType type) { + if (type instanceof AnnotatedParameterizedType) { + return capture((AnnotatedParameterizedType)type); + } else { + return type; + } + } // - /** - * Applies capture conversion to the given type. - * @see #capture(AnnotatedType) - */ - public static AnnotatedParameterizedType capture(AnnotatedParameterizedType type) { - // the map from parameters to their captured equivalent - - VarMap varMap = new VarMap(); - // list of CaptureTypes we've created but aren't fully initialized yet - // we can only initialize them *after* we've fully populated varMap - List toInit = new ArrayList<>(); - - Class clazz = (Class)((ParameterizedType)type.getType()).getRawType(); - AnnotatedType[] arguments = type.getAnnotatedActualTypeArguments(); - TypeVariable[] vars = clazz.getTypeParameters(); - AnnotatedType[] capturedArguments = new AnnotatedType[arguments.length]; - - assert arguments.length == vars.length; // NICE throw an explaining exception - - for (int i = 0; i < arguments.length; i++) { - AnnotatedType argument = arguments[i]; - if (argument instanceof AnnotatedWildcardType) { - AnnotatedCaptureTypeImpl captured = new AnnotatedCaptureTypeImpl((AnnotatedWildcardType)argument, new AnnotatedTypeVariableImpl(vars[i])); - argument = captured; - toInit.add(captured); - } - capturedArguments[i] = argument; - varMap.add(vars[i], argument); - } - for (AnnotatedCaptureTypeImpl captured : toInit) { - captured.init(varMap); - } - ParameterizedType inner = (ParameterizedType) type.getType(); - AnnotatedType ownerType = (inner.getOwnerType() == null) ? null : capture(annotate(inner.getOwnerType())); - Type[] rawArgs = Arrays.stream(capturedArguments).map(AnnotatedType::getType).toArray(Type[]::new); - ParameterizedType nn = new ParameterizedTypeImpl(clazz, rawArgs, ownerType == null ? null : ownerType.getType()); - return new AnnotatedParameterizedTypeImpl(nn, type.getAnnotations(), capturedArguments); - } - - /** - * Returns the display name of a Type. - */ - public static String getTypeName(Type type) { - if(type instanceof Class) { - Class clazz = (Class) type; - return clazz.isArray() ? (getTypeName(clazz.getComponentType()) + "[]") : clazz.getName(); - } else { - return type.toString(); - } - } - - /** - * Returns list of classes and interfaces that are supertypes of the given type. - * For example given this class: - * {@code class {@literal Foo, B extends A>}}
- * calling this method on type parameters {@code B} ({@code Foo.class.getTypeParameters()[1]}) - * returns a list containing {@code Number} and {@code Iterable}. - *

- * This is mostly useful if you get a type from one of the other methods in {@code GenericTypeReflector}, - * but you don't want to deal with all the different sorts of types, - * and you are only really interested in concrete classes and interfaces. - *

- * - * @return A List of classes, each of them a supertype of the given type. - * If the given type is a class or interface itself, returns a List with just the given type. - * The list contains no duplicates, and is ordered in the order the upper bounds are defined on the type. - */ - public static List> getUpperBoundClassAndInterfaces(Type type) { - LinkedHashSet> result = new LinkedHashSet<>(); - buildUpperBoundClassAndInterfaces(type, result); - return new ArrayList<>(result); - } + /** + * Applies capture conversion to the given type. + * @see #capture(AnnotatedType) + */ + public static AnnotatedParameterizedType capture(AnnotatedParameterizedType type) { + // the map from parameters to their captured equivalent + + VarMap varMap = new VarMap(); + // list of CaptureTypes we've created but aren't fully initialized yet + // we can only initialize them *after* we've fully populated varMap + List toInit = new ArrayList<>(); + + Class clazz = (Class)((ParameterizedType)type.getType()).getRawType(); + AnnotatedType[] arguments = type.getAnnotatedActualTypeArguments(); + TypeVariable[] vars = clazz.getTypeParameters(); + AnnotatedType[] capturedArguments = new AnnotatedType[arguments.length]; + + assert arguments.length == vars.length; // NICE throw an explaining exception + + for (int i = 0; i < arguments.length; i++) { + AnnotatedType argument = arguments[i]; + if (argument instanceof AnnotatedWildcardType) { + AnnotatedCaptureTypeImpl captured = new AnnotatedCaptureTypeImpl((AnnotatedWildcardType)argument, new AnnotatedTypeVariableImpl(vars[i])); + argument = captured; + toInit.add(captured); + } + capturedArguments[i] = argument; + varMap.add(vars[i], argument); + } + for (AnnotatedCaptureTypeImpl captured : toInit) { + captured.init(varMap); + } + ParameterizedType inner = (ParameterizedType) type.getType(); + AnnotatedType ownerType = (inner.getOwnerType() == null) ? null : capture(annotate(inner.getOwnerType())); + Type[] rawArgs = stream(capturedArguments).map(AnnotatedType::getType).toArray(Type[]::new); + ParameterizedType nn = new ParameterizedTypeImpl(clazz, rawArgs, ownerType == null ? null : ownerType.getType()); + return new AnnotatedParameterizedTypeImpl(nn, type.getAnnotations(), capturedArguments); + } + + /** + * Returns the display name of a Type. + */ + public static String getTypeName(Type type) { + if(type instanceof Class) { + Class clazz = (Class) type; + return clazz.isArray() ? (getTypeName(clazz.getComponentType()) + "[]") : clazz.getName(); + } else { + return type.toString(); + } + } + + /** + * Returns list of classes and interfaces that are supertypes of the given type. + * For example given this class: + * {@code class {@literal Foo
, B extends A>}}
+ * calling this method on type parameters {@code B} ({@code Foo.class.getTypeParameters()[1]}) + * returns a list containing {@code Number} and {@code Iterable}. + *

+ * This is mostly useful if you get a type from one of the other methods in {@code GenericTypeReflector}, + * but you don't want to deal with all the different sorts of types, + * and you are only really interested in concrete classes and interfaces. + *

+ * + * @return A List of classes, each of them a supertype of the given type. + * If the given type is a class or interface itself, returns a List with just the given type. + * The list contains no duplicates, and is ordered in the order the upper bounds are defined on the type. + */ + public static List> getUpperBoundClassAndInterfaces(Type type) { + LinkedHashSet> result = new LinkedHashSet<>(); + buildUpperBoundClassAndInterfaces(type, result); + return new ArrayList<>(result); + } /** * Recursively wraps a {@link Type} into an {@link AnnotatedType} using the annotations found on @@ -525,9 +604,9 @@ public static List> getUpperBoundClassAndInterfaces(Type type) { * @param type Type to annotate * @return Type whose structure has been recursively annotated */ - public static AnnotatedType annotate(Type type) { - return annotate(type, new HashMap<>()); - } + public static AnnotatedType annotate(Type type) { + return annotate(type, new HashMap<>()); + } /** * Recursively wraps a {@link Type} into an {@link AnnotatedType} using the annotations found on @@ -539,7 +618,7 @@ public static AnnotatedType annotate(Type type) { * *

See {@link #annotate(Type)}

*/ - public static AnnotatedType annotate(Type type, Annotation[] annotations) { + public static AnnotatedType annotate(Type type, Annotation[] annotations) { return updateAnnotations(annotate(type), annotations); } @@ -561,56 +640,62 @@ public static AnnotatedType annotate(Type type, Annotation[] annotations) { *

See {@link CaptureCacheKey}

*

See {@link CaptureType}

*/ - private static AnnotatedType annotate(Type type, Map cache) { - if (type instanceof ParameterizedType) { - ParameterizedType parameterized = (ParameterizedType) type; - AnnotatedType[] params = new AnnotatedType[parameterized.getActualTypeArguments().length]; - for (int i = 0; i < params.length; i++) { - AnnotatedType param = annotate(parameterized.getActualTypeArguments()[i], cache); - params[i] = updateAnnotations(param, erase(type).getTypeParameters()[i].getAnnotations()); - } - return new AnnotatedParameterizedTypeImpl(parameterized, erase(type).getAnnotations(), params); - } - if (type instanceof CaptureType) { - CaptureCacheKey key = new CaptureCacheKey(((CaptureType) type)); - if (cache.containsKey(key)) { - return cache.get(key); - } - CaptureType capture = ((CaptureType) type); - AnnotatedCaptureType annotatedCapture = new AnnotatedCaptureTypeImpl( - ((AnnotatedWildcardType) annotate(capture.getWildcardType(), cache)), - (AnnotatedTypeVariable) annotate(capture.getTypeVariable(), cache), - null); - - cache.put(new CaptureCacheKey(capture), annotatedCapture); - AnnotatedType[] upperBounds = Arrays.stream(capture.getUpperBounds()) - .map(bound -> annotate(bound, cache)) - .toArray(AnnotatedType[]::new); - annotatedCapture.setAnnotatedUpperBounds(upperBounds); //complete the type - return annotatedCapture; - } - if (type instanceof WildcardType) { - WildcardType wildcard = (WildcardType) type; - AnnotatedType[] lowerBounds = Arrays.stream(wildcard.getLowerBounds()) - .map(bound -> annotate(bound, cache)) - .toArray(AnnotatedType[]::new); - AnnotatedType[] upperBounds = Arrays.stream(wildcard.getUpperBounds()) - .map(bound -> annotate(bound, cache)) - .toArray(AnnotatedType[]::new); - return new AnnotatedWildcardTypeImpl(wildcard, erase(type).getAnnotations(), lowerBounds, upperBounds); - } - if (type instanceof TypeVariable) { - return new AnnotatedTypeVariableImpl((TypeVariable) type); - } - if (type instanceof GenericArrayType) { - GenericArrayType genArray = (GenericArrayType) type; - return new AnnotatedArrayTypeImpl(genArray, new Annotation[0], annotate(genArray.getGenericComponentType())); - } - if (type instanceof Class) { - return new AnnotatedTypeImpl(type, ((Class) type).getAnnotations()); - } - throw new IllegalArgumentException("Unrecognized type: " + type.getTypeName()); - } + private static AnnotatedType annotate(Type type, Map cache) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterized = (ParameterizedType) type; + AnnotatedType[] params = new AnnotatedType[parameterized.getActualTypeArguments().length]; + for (int i = 0; i < params.length; i++) { + AnnotatedType param = annotate(parameterized.getActualTypeArguments()[i], cache); + params[i] = updateAnnotations(param, erase(type).getTypeParameters()[i].getAnnotations()); + } + return new AnnotatedParameterizedTypeImpl(parameterized, erase(type).getAnnotations(), params); + } + if (type instanceof CaptureType) { + CaptureCacheKey key = new CaptureCacheKey(((CaptureType) type)); + if (cache.containsKey(key)) { + return cache.get(key); + } + CaptureType capture = ((CaptureType) type); + AnnotatedCaptureType annotatedCapture = new AnnotatedCaptureTypeImpl( + ((AnnotatedWildcardType) annotate(capture.getWildcardType(), cache)), + (AnnotatedTypeVariable) annotate(capture.getTypeVariable(), cache), + null); + + cache.put(new CaptureCacheKey(capture), annotatedCapture); + AnnotatedType[] upperBounds = stream(capture.getUpperBounds()) + .map(bound -> annotate(bound, cache)) + .toArray(AnnotatedType[]::new); + annotatedCapture.setAnnotatedUpperBounds(upperBounds); //complete the type + return annotatedCapture; + } + if (type instanceof WildcardType) { + WildcardType wildcard = (WildcardType) type; + AnnotatedType[] lowerBounds = stream(wildcard.getLowerBounds()) + .map(bound -> annotate(bound, cache)) + .toArray(AnnotatedType[]::new); + AnnotatedType[] upperBounds = stream(wildcard.getUpperBounds()) + .map(bound -> annotate(bound, cache)) + .toArray(AnnotatedType[]::new); + return new AnnotatedWildcardTypeImpl(wildcard, erase(type).getAnnotations(), lowerBounds, upperBounds); + } + if (type instanceof TypeVariable) { + return new AnnotatedTypeVariableImpl((TypeVariable) type); + } + if (type instanceof GenericArrayType) { + GenericArrayType genArray = (GenericArrayType) type; + return new AnnotatedArrayTypeImpl(genArray, new Annotation[0], annotate(genArray.getGenericComponentType())); + } + if (type instanceof Class) { + Class clazz = (Class) type; + if (clazz.isArray()) { + Class componentClass = clazz.getComponentType(); + return AnnotatedArrayTypeImpl.createArrayType( + new AnnotatedTypeImpl(componentClass, componentClass.getAnnotations()), new Annotation[0]); + } + return new AnnotatedTypeImpl(clazz, clazz.getAnnotations()); + } + throw new IllegalArgumentException("Unrecognized type: " + type.getTypeName()); + } /** * Creates a new {@link AnnotatedType} of the same type as the original, but with its annotations @@ -621,32 +706,32 @@ private static AnnotatedType annotate(Type type, Map) original.getType(), annotations); - } - if (original instanceof AnnotatedArrayType) { - return new AnnotatedArrayTypeImpl(original.getType(), annotations, - ((AnnotatedArrayType) original).getAnnotatedGenericComponentType()); - } - return new AnnotatedTypeImpl(original.getType(), annotations); - } + public static AnnotatedType replaceAnnotations(AnnotatedType original, Annotation[] annotations) { + if (original instanceof AnnotatedParameterizedType) { + return new AnnotatedParameterizedTypeImpl((ParameterizedType) original.getType(), annotations, + ((AnnotatedParameterizedType) original).getAnnotatedActualTypeArguments()); + } + if (original instanceof AnnotatedCaptureType) { + return new AnnotatedCaptureTypeImpl( + ((AnnotatedCaptureType) original).getAnnotatedWildcardType(), + ((AnnotatedCaptureType) original).getAnnotatedTypeVariable(), + ((AnnotatedCaptureType) original).getAnnotatedUpperBounds(), + annotations); + } + if (original instanceof AnnotatedWildcardType) { + return new AnnotatedWildcardTypeImpl((WildcardType) original.getType(), annotations, + ((AnnotatedWildcardType) original).getAnnotatedLowerBounds(), + ((AnnotatedWildcardType) original).getAnnotatedUpperBounds()); + } + if (original instanceof AnnotatedTypeVariable) { + return new AnnotatedTypeVariableImpl((TypeVariable) original.getType(), annotations); + } + if (original instanceof AnnotatedArrayType) { + return new AnnotatedArrayTypeImpl(original.getType(), annotations, + ((AnnotatedArrayType) original).getAnnotatedGenericComponentType()); + } + return new AnnotatedTypeImpl(original.getType(), annotations); + } /** * Creates a new {@link AnnotatedType} of the same structure as the original, but with its annotations @@ -657,15 +742,15 @@ public static AnnotatedType replaceAnnotations(AnnotatedType original, Annotatio * * @return A type of the same structure as the original but with replaced annotations */ - public static AnnotatedType updateAnnotations(AnnotatedType original, Annotation[] annotations) { - if (annotations == null || annotations.length == 0 || Arrays.equals(original.getAnnotations(), annotations)) { - return original; - } - return replaceAnnotations(original, merge(original.getAnnotations(), annotations)); - } - - public static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, AnnotatedType[] typeArguments) { - Type[] rawArguments = Arrays.stream(typeArguments).map(AnnotatedType::getType).toArray(Type[]::new); + public static AnnotatedType updateAnnotations(AnnotatedType original, Annotation[] annotations) { + if (annotations == null || annotations.length == 0 || Arrays.equals(original.getAnnotations(), annotations)) { + return original; + } + return replaceAnnotations(original, merge(original.getAnnotations(), annotations)); + } + + public static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, AnnotatedType[] typeArguments) { + Type[] rawArguments = stream(typeArguments).map(AnnotatedType::getType).toArray(Type[]::new); ParameterizedType rawType = (ParameterizedType) TypeFactory.parameterizedClass(erase(type.getType()), rawArguments); return new AnnotatedParameterizedTypeImpl(rawType, type.getAnnotations(), typeArguments); } @@ -678,7 +763,7 @@ public static AnnotatedParameterizedType replaceParameters(AnnotatedParameterize * * @return A type of the same structure and with the same annotation as the provided one */ - public static AnnotatedType clone(AnnotatedType type) { + public static AnnotatedType clone(AnnotatedType type) { return replaceAnnotations(type, type.getAnnotations()); } @@ -689,24 +774,24 @@ public static AnnotatedType clone(AnnotatedType type) { * @return An array containing all annotations from the given arrays, without duplicates */ public static Annotation[] merge(Annotation[]... annotations) { - return Arrays.stream(annotations).reduce( - (acc, arr) -> Stream.concat(Arrays.stream(acc), Arrays.stream(arr)).distinct().toArray(Annotation[]::new)) + return stream(annotations).reduce( + (acc, arr) -> Stream.concat(stream(acc), stream(arr)).distinct().toArray(Annotation[]::new)) .orElse(new Annotation[0]); } - public static boolean typeArraysEqual(AnnotatedType[] t1, AnnotatedType[] t2) { - if (t1 == null && t2 != null) return false; - if (t2 == null && t1 != null) return false; - if (t1 == null) return true; - if (t1.length != t2.length) return false; - - for (int i = 0; i < t1.length; i++) { - if (!t1[i].getType().equals(t2[i].getType()) || !Arrays.equals(t1[i].getAnnotations(), t2[i].getAnnotations())) { - return false; - } - } - return true; - } + public static boolean typeArraysEqual(AnnotatedType[] t1, AnnotatedType[] t2) { + if (t1 == null && t2 != null) return false; + if (t2 == null && t1 != null) return false; + if (t1 == null) return true; + if (t1.length != t2.length) return false; + + for (int i = 0; i < t1.length; i++) { + if (!t1[i].getType().equals(t2[i].getType()) || !Arrays.equals(t1[i].getAnnotations(), t2[i].getAnnotations())) { + return false; + } + } + return true; + } /** * Checks whether the two provided types are of the same structure and annotations on all levels. @@ -725,19 +810,19 @@ public static boolean equals(AnnotatedType t1, AnnotatedType t2) { return t1.equals(t2); } - /** - * Helper method for getUpperBoundClassAndInterfaces, adding the result to the given set. - */ - private static void buildUpperBoundClassAndInterfaces(Type type, Set> result) { - if (type instanceof ParameterizedType || type instanceof Class) { - result.add(erase(type)); - return; - } - - for (AnnotatedType superType : getExactDirectSuperTypes(annotate(type))) { - buildUpperBoundClassAndInterfaces(superType.getType(), result); - } - } + /** + * Helper method for getUpperBoundClassAndInterfaces, adding the result to the given set. + */ + private static void buildUpperBoundClassAndInterfaces(Type type, Set> result) { + if (type instanceof ParameterizedType || type instanceof Class) { + result.add(erase(type)); + return; + } + + for (AnnotatedType superType : getExactDirectSuperTypes(annotate(type))) { + buildUpperBoundClassAndInterfaces(superType.getType(), result); + } + } /** * A key representing a {@link CaptureType}. Used for caching incomplete {@link CaptureType}s @@ -746,29 +831,29 @@ private static void buildUpperBoundClassAndInterfaces(Type type, Set> r * *

See {@link #annotate(Type, Map)}

*/ - private static class CaptureCacheKey { - CaptureType capture; - - CaptureCacheKey(CaptureType capture) { - this.capture = capture; - } - - @Override - public int hashCode() { - return capture.getWildcardType().hashCode() + capture.getTypeVariable().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CaptureCacheKey)) { - return false; - } - CaptureType other = ((CaptureCacheKey) obj).capture; - if (!capture.getWildcardType().equals(other.getWildcardType()) - || !capture.getTypeVariable().equals(other.getTypeVariable())) { - return false; - } - return Arrays.equals(capture.getUpperBounds(), other.getUpperBounds()); - } - } + private static class CaptureCacheKey { + CaptureType capture; + + CaptureCacheKey(CaptureType capture) { + this.capture = capture; + } + + @Override + public int hashCode() { + return capture.getWildcardType().hashCode() + capture.getTypeVariable().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CaptureCacheKey)) { + return false; + } + CaptureType other = ((CaptureCacheKey) obj).capture; + if (!capture.getWildcardType().equals(other.getWildcardType()) + || !capture.getTypeVariable().equals(other.getTypeVariable())) { + return false; + } + return Arrays.equals(capture.getUpperBounds(), other.getUpperBounds()); + } + } } diff --git a/src/main/java/io/leangen/geantyref/TypeFactory.java b/src/main/java/io/leangen/geantyref/TypeFactory.java index d152642..6a810c9 100644 --- a/src/main/java/io/leangen/geantyref/TypeFactory.java +++ b/src/main/java/io/leangen/geantyref/TypeFactory.java @@ -43,7 +43,7 @@ public static AnnotatedType parameterizedAnnotatedClass(Class clazz, Annotati Type[] typeArguments = Arrays.stream(arguments).map(AnnotatedType::getType).toArray(Type[]::new); return new AnnotatedParameterizedTypeImpl((ParameterizedType) parameterizedClass(clazz, typeArguments), annotations, arguments); } - + /** * Creates a type of {@code clazz} nested in {@code owner}. * diff --git a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java index d55b4b5..d2facca 100644 --- a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java +++ b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java @@ -1,6 +1,9 @@ package io.leangen.geantyref; import java.awt.*; +import java.lang.reflect.AnnotatedArrayType; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; @@ -9,6 +12,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Set; + +import static io.leangen.geantyref.GenericTypeReflector.getExactSubType; /** * Test for reflection done in GenericTypeReflector. @@ -62,7 +68,7 @@ public void testGetExactFieldTypeIllegalArgument() throws SecurityException, NoS } } - public void testgetExactParameterTypes() throws SecurityException, NoSuchMethodException { + public void testGetExactParameterTypes() throws SecurityException, NoSuchMethodException { // method: boolean add(int index, E o), erasure is boolean add(int index, Object o) Method getMethod = List.class.getMethod("add", int.class, Object.class); Type[] result = GenericTypeReflector.getExactParameterTypes(getMethod, new TypeToken>() { @@ -71,4 +77,47 @@ public void testgetExactParameterTypes() throws SecurityException, NoSuchMethodE assertEquals(int.class, result[0]); assertEquals(String.class, result[1]); } + + public void testGetExactSubType() { + AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken>(){}.getAnnotatedType(); + AnnotatedParameterizedType subType = (AnnotatedParameterizedType) getExactSubType(parent, C.class); + assertNotNull(subType); + assertEquals(Integer.class, subType.getAnnotatedActualTypeArguments()[0].getType()); + assertEquals(String.class, subType.getAnnotatedActualTypeArguments()[1].getType()); + } + + public void testGetExactSubTypeUnresolvable() { + AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken>(){}.getAnnotatedType(); + AnnotatedType resolved = GenericTypeReflector.getExactSubType(parent, C1.class); + assertNotNull(resolved); + assertEquals(C1.class, resolved.getType()); + } + + public void testGetExactSubTypeNotOverlapping() { + AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken>(){}.getAnnotatedType(); + AnnotatedType subType = getExactSubType(parent, Set.class); + assertNull(subType); + } + + public void testGetExactSubTypeNotParameterized() { + AnnotatedParameterizedType parent = (AnnotatedParameterizedType) new TypeToken>(){}.getAnnotatedType(); + AnnotatedType subType = getExactSubType(parent, String.class); + assertNotNull(subType); + assertEquals(String.class, subType.getType()); + } + + public void testGetExactSubTypeArray() { + AnnotatedType parent = new TypeToken[]>(){}.getAnnotatedType(); + AnnotatedType subType = getExactSubType(parent, ArrayList[].class); + assertNotNull(subType); + assertTrue(subType instanceof AnnotatedArrayType); + AnnotatedType componentType = ((AnnotatedArrayType) subType).getAnnotatedGenericComponentType(); + assertTrue(componentType instanceof AnnotatedParameterizedType); + assertEquals(String.class, ((AnnotatedParameterizedType) componentType).getAnnotatedActualTypeArguments()[0].getType()); + } + + private class P {} + private class M extends P{} + private class C extends M{} + private class C1 extends M{} } diff --git a/src/test/java/io/leangen/geantyref/factory/TypeFactoryTest.java b/src/test/java/io/leangen/geantyref/factory/TypeFactoryTest.java index df3500e..817a68d 100644 --- a/src/test/java/io/leangen/geantyref/factory/TypeFactoryTest.java +++ b/src/test/java/io/leangen/geantyref/factory/TypeFactoryTest.java @@ -62,7 +62,7 @@ public void testEmptyArgumentsForGenericClass() { try { parameterizedClass(List.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -70,7 +70,7 @@ public void testTooManyTypeArguments() { try { parameterizedClass(List.class, String.class, String.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -121,7 +121,7 @@ public void testOwnerForTopLevel() { try { innerClass(String.class, Integer.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -196,7 +196,7 @@ public void testWrongOwnerGeneric() { try { parameterizedInnerClass(String.class, SimpleOuter.GenericInner.class, String.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -204,7 +204,7 @@ public void testWrongOwnerRaw() { try { innerClass(String.class, SimpleOuter.GenericInner.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -257,7 +257,7 @@ public void testStaticInnerWithWrongOwner() { try { parameterizedInnerClass(String.class, GenericOuter.StaticGenericInner.class, Integer.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -268,7 +268,7 @@ public void testNullTypeArgument() { try { parameterizedClass(List.class, new Type[]{null}); fail("expected exception"); - } catch (NullPointerException expected) { + } catch (NullPointerException expected) {//expected } } @@ -282,7 +282,7 @@ public void testTypeArgumentsNotInBound() { try { parameterizedClass(Bound.class, String.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -296,7 +296,7 @@ public void testTypeArgumentsNotInReferingBound() { try { parameterizedClass(ReferingBound.class, parameterizedClass(List.class, Integer.class), Number.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -311,7 +311,7 @@ public void testTypeArgumentNotInRecursiveBound() { try { parameterizedClass(RecursiveBound.class, NotInRecursiveBound.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -325,7 +325,7 @@ public void testTypeArgumentNotInRawBound() { try { parameterizedClass(RawBound.class, parameterizedClass(Collection.class, String.class)); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -342,7 +342,7 @@ public void ignoredTestRawTypeArgumentInParameterizedBoundNotValid() { try { parameterizedClass(ParameterizedBound.class, List.class); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -381,7 +381,7 @@ public void testTypeArgumentNotInBoundReferingToOwner() { String.class ); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -401,7 +401,7 @@ public void testWildcardExtendsTypeArgumentNotInBound() { try { parameterizedClass(Bound.class, wildcardExtends(Thread.class)); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) {//expected } } @@ -421,7 +421,7 @@ public void testWildcardSuperTypeArgumentNotInBound() { try { parameterizedClass(Bound.class, wildcardSuper(String.class)); fail("expected exception"); - } catch (IllegalArgumentException expected) { + } catch (IllegalArgumentException expected) { //expected } } @@ -450,15 +450,12 @@ public void testNotInReferingToWildcardBound() { wildcardExtends(Integer.class) ); fail("expected exception"); - } catch (TypeArgumentNotInBoundException expected) { + } catch (TypeArgumentNotInBoundException expected) { //expected } } public void testLocalClass() { - class Local { - } - System.out.println(Local.class.getDeclaringClass()); - + class Local {} assertEquals(new TypeToken>() { }.getType(), parameterizedClass(Local.class, String.class)); @@ -470,8 +467,7 @@ class Local { * represented). (Java reflection also doesn't see the enclosing class as owner). */ public void testLocalClassWithOwner() { - class Local { - } + class Local {} try { parameterizedInnerClass(TypeFactoryTest.class, Local.class, String.class); fail("expected exception"); @@ -510,10 +506,10 @@ public void testGenericArray() { TypeFactory.arrayOf(parameterizedClass(List.class, String.class))); } - static class Bound { + private static class Bound { } - static class ReferingBound
, B> { + private static class ReferingBound, B> { } static class RecursiveBound> { @@ -522,17 +518,17 @@ static class RecursiveBound> { static class InRecursiveBound extends RecursiveBound { } - static class NotInRecursiveBound extends RecursiveBound { + private static class NotInRecursiveBound extends RecursiveBound { } @SuppressWarnings("rawtypes") - static class RawBound { + private static class RawBound { } - static class ParameterizedBound> { + private static class ParameterizedBound> { } - static class BoundReferingToOwner { + private static class BoundReferingToOwner { class In { } }