diff --git a/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java index 58c5a2a..cc72c1a 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedArrayTypeImpl.java @@ -45,9 +45,4 @@ public int hashCode() { public String toString() { return componentType.toString() + " " + annotationsString() + "[]"; } - - @Override - public AnnotatedType getAnnotatedOwnerType() { - return null; - } } diff --git a/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java index b64a405..9300122 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedParameterizedTypeImpl.java @@ -16,9 +16,10 @@ class AnnotatedParameterizedTypeImpl extends AnnotatedTypeImpl implements Annota private final AnnotatedType[] typeArguments; - AnnotatedParameterizedTypeImpl(ParameterizedType rawType, Annotation[] annotations, AnnotatedType[] typeArguments) { + AnnotatedParameterizedTypeImpl(ParameterizedType rawType, Annotation[] annotations, AnnotatedType[] typeArguments, AnnotatedType ownerType) { super(rawType, annotations); this.typeArguments = typeArguments; + this.ownerType = ownerType; } @Override @@ -46,8 +47,8 @@ public String toString() { String rawName = GenericTypeReflector.getTypeName(rawType.getRawType()); StringBuilder typeName = new StringBuilder(); - if (rawType.getOwnerType() != null) { - typeName.append(GenericTypeReflector.getTypeName(rawType.getOwnerType())).append('$'); + if (ownerType != null) { + typeName.append(ownerType).append('$'); String prefix = (rawType.getOwnerType() instanceof ParameterizedType) ? ((Class) ((ParameterizedType) rawType.getOwnerType()).getRawType()).getName() + '$' : ((Class) rawType.getOwnerType()).getName() + '$'; @@ -55,11 +56,6 @@ public String toString() { rawName = rawName.substring(prefix.length()); } typeName.append(rawName); - return annotationsString() + typeName + "<" + typesString(typeArguments) + ">"; - } - - @Override - public AnnotatedType getAnnotatedOwnerType() { - return null; + return annotationsString() + typeName + (typeArguments != null && typeArguments.length > 0 ? "<" + typesString(typeArguments) + ">" : ""); } } diff --git a/src/main/java/io/leangen/geantyref/AnnotatedTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedTypeImpl.java index d69b328..08d5b09 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedTypeImpl.java @@ -19,14 +19,20 @@ class AnnotatedTypeImpl implements AnnotatedType { protected Type type; protected Map, Annotation> annotations; + protected AnnotatedType ownerType; AnnotatedTypeImpl(Type type) { - this(type, new Annotation[0]); + this(type, new Annotation[0], null); } AnnotatedTypeImpl(Type type, Annotation[] annotations) { + this(type, annotations, null); + } + + AnnotatedTypeImpl(Type type, Annotation[] annotations, AnnotatedType ownerType) { this.type = Objects.requireNonNull(type); this.annotations = toMap(annotations); + this.ownerType = ownerType; } @Override @@ -51,6 +57,11 @@ public Annotation[] getDeclaredAnnotations() { return getAnnotations(); } + @Override + public AnnotatedType getAnnotatedOwnerType() { + return ownerType; + } + @Override public boolean equals(Object other) { if (this == other) { diff --git a/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java index cf445b7..ff9b65c 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedTypeVariableImpl.java @@ -24,7 +24,7 @@ class AnnotatedTypeVariableImpl extends AnnotatedTypeImpl implements AnnotatedTy } AnnotatedTypeVariableImpl(TypeVariable type, Annotation[] annotations) { - super(type, annotations); + super(type, annotations, null); AnnotatedType[] annotatedBounds = type.getAnnotatedBounds(); if (annotatedBounds == null || annotatedBounds.length == 0) { annotatedBounds = new AnnotatedType[0]; @@ -58,9 +58,4 @@ public boolean equals(Object other) { public String toString() { return annotationsString() + ((TypeVariable) type).getName(); } - - @Override - public AnnotatedType getAnnotatedOwnerType() { - return null; - } } diff --git a/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java b/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java index 5587493..e046fc4 100644 --- a/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java +++ b/src/main/java/io/leangen/geantyref/AnnotatedWildcardTypeImpl.java @@ -87,9 +87,4 @@ private static void validateBounds(WildcardType type, AnnotatedType[] lowerBound } } } - - @Override - public AnnotatedType getAnnotatedOwnerType() { - return null; - } } diff --git a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java index 41f2950..f2591dd 100644 --- a/src/main/java/io/leangen/geantyref/GenericTypeReflector.java +++ b/src/main/java/io/leangen/geantyref/GenericTypeReflector.java @@ -7,29 +7,8 @@ import java.io.Serializable; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedArrayType; -import java.lang.reflect.AnnotatedParameterizedType; -import java.lang.reflect.AnnotatedType; -import java.lang.reflect.AnnotatedTypeVariable; -import java.lang.reflect.AnnotatedWildcardType; -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.lang.reflect.*; +import java.util.*; import java.util.function.Function; import java.util.function.IntFunction; import java.util.stream.Stream; @@ -46,21 +25,16 @@ public class GenericTypeReflector { private static final WildcardType UNBOUND_WILDCARD = new WildcardTypeImpl(new Type[]{Object.class}, new Type[]{}); - private static final Map, Class> BOX_TYPES; - - static { - Map, Class> boxTypes = new HashMap<>(); - boxTypes.put(boolean.class, Boolean.class); - boxTypes.put(byte.class, Byte.class); - boxTypes.put(char.class, Character.class); - boxTypes.put(double.class, Double.class); - boxTypes.put(float.class, Float.class); - boxTypes.put(int.class, Integer.class); - boxTypes.put(long.class, Long.class); - boxTypes.put(short.class, Short.class); - boxTypes.put(void.class, Void.class); - BOX_TYPES = Collections.unmodifiableMap(boxTypes); - } + private static final Map, Class> BOX_TYPES = Map.of( + boolean.class, Boolean.class, + byte.class, Byte.class, + char.class, Character.class, + double.class, Double.class, + float.class, Float.class, + int.class, Integer.class, + long.class, Long.class, + short.class, Short.class, + void.class, Void.class); /** * Returns the erasure of the given type. @@ -128,17 +102,16 @@ private static AnnotatedType mapTypeParameters(AnnotatedType toMapType, Annotate private static AnnotatedType mapTypeParameters(AnnotatedType toMapType, AnnotatedType typeAndParams, VarMap.MappingMode mappingMode) { if (isMissingTypeParameters(typeAndParams.getType())) { - return new AnnotatedTypeImpl(erase(toMapType.getType()), toMapType.getAnnotations()); + return new AnnotatedTypeImpl(erase(toMapType.getType()), toMapType.getAnnotations(), toMapType.getAnnotatedOwnerType()); } else { VarMap varMap = new VarMap(); AnnotatedType handlingTypeAndParams = typeAndParams; - while(handlingTypeAndParams instanceof AnnotatedParameterizedType) { - AnnotatedParameterizedType pType = (AnnotatedParameterizedType)handlingTypeAndParams; + 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); + handlingTypeAndParams = pType.getAnnotatedOwnerType(); } return varMap.map(toMapType, mappingMode); } @@ -165,7 +138,11 @@ private static AnnotatedType resolveType(AnnotatedType unresolved, AnnotatedType AnnotatedParameterizedType parameterizedType = (AnnotatedParameterizedType) unresolved; AnnotatedType[] params = mapArray(parameterizedType.getAnnotatedActualTypeArguments(), AnnotatedType[]::new, p -> resolveType(p, typeAndParams, mappingMode)); - return replaceParameters(parameterizedType, params); + AnnotatedType owner = parameterizedType.getAnnotatedOwnerType(); + if (owner != null) { + owner = resolveType(owner, typeAndParams.getAnnotatedOwnerType(), mappingMode); + } + return replaceParameters(parameterizedType, params, owner); } if (unresolved instanceof AnnotatedWildcardType) { AnnotatedType[] lower = mapArray(((AnnotatedWildcardType) unresolved).getAnnotatedLowerBounds(), AnnotatedType[]::new, @@ -187,7 +164,7 @@ private static AnnotatedType resolveType(AnnotatedType unresolved, AnnotatedType return unresolved; } throw new IllegalArgumentException("Variable " + var.getName() + " is not declared by the given type " - + typeAndParams.getType().getTypeName() + " or its super types"); + + typeAndParams.getType().getTypeName() + " or its super types"); } if (unresolved instanceof AnnotatedArrayType) { AnnotatedType componentType = resolveType( @@ -464,7 +441,7 @@ private static boolean isArraySupertype(Type arraySuperType, Type subType) { public static AnnotatedType getArrayComponentType(AnnotatedType type) { if (type.getType() instanceof Class) { Class clazz = (Class)type.getType(); - return new AnnotatedTypeImpl(clazz.getComponentType(), clazz.getAnnotations()); + return new AnnotatedTypeImpl(clazz.getComponentType(), clazz.getAnnotations(), type.getAnnotatedOwnerType()); } else if (type instanceof AnnotatedArrayType) { AnnotatedArrayType aType = (AnnotatedArrayType) type; return aType.getAnnotatedGenericComponentType(); @@ -617,7 +594,7 @@ public static Type getExactReturnType(Method m, Type declaringType) { /** * Resolves the return type of the given method in the given type. Any unresolvable variables will be kept - * (in contrast to {@link #getExactReturnType(Method, AnnotatedType)}. + * (in contrast to {@link #getExactReturnType(Method, AnnotatedType)}). * This may be different from {@code m.getAnnotatedReturnType()} 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. */ @@ -627,7 +604,7 @@ public static AnnotatedType getReturnType(Method m, AnnotatedType declaringType) /** * Resolves the return type of the given method in the given type. Any unresolvable variables will be kept - * (in contrast to {@link #getExactReturnType(Method, Type)}. + * (in contrast to {@link #getExactReturnType(Method, 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. */ @@ -750,7 +727,7 @@ 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 + // 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<>(); @@ -775,10 +752,10 @@ public static AnnotatedParameterizedType capture(AnnotatedParameterizedType type captured.init(varMap); } ParameterizedType inner = (ParameterizedType) type.getType(); - AnnotatedType ownerType = (inner.getOwnerType() == null) ? null : capture(annotate(inner.getOwnerType())); + AnnotatedType ownerType = capture(type.getAnnotatedOwnerType()); Type[] rawArgs = mapArray(capturedArguments, Type[]::new, AnnotatedType::getType); ParameterizedType nn = new ParameterizedTypeImpl(clazz, rawArgs, ownerType == null ? null : ownerType.getType()); - return new AnnotatedParameterizedTypeImpl(nn, type.getAnnotations(), capturedArguments); + return new AnnotatedParameterizedTypeImpl(nn, type.getAnnotations(), capturedArguments, ownerType); } /** @@ -846,8 +823,8 @@ public static AnnotatedType annotate(Type type, Annotation[] annotations) { /** * This is the method underlying both {@link #annotate(Type)} and {@link #annotate(Type, Annotation[])}. - * If goes recursively through the structure of the provided {@link Type} wrapping all type parameters, - * bounds etc encountered into {@link AnnotatedType}s using annotations found directly on the + * It goes recursively through the structure of the provided {@link Type} wrapping all type parameters, + * bounds etc. encountered into {@link AnnotatedType}s using annotations found directly on the * corresponding erasure class, with a special treatment for {@link CaptureType} which can have * infinitely recursive structure by having itself as its upper bound. * @@ -863,6 +840,9 @@ public static AnnotatedType annotate(Type type, Annotation[] annotations) { *

See {@link CaptureType}

*/ private static AnnotatedType annotate(Type type, boolean expandGenerics, Map cache) { + if (type == null) { + return null; + } if (type instanceof ParameterizedType) { ParameterizedType parameterized = (ParameterizedType) type; AnnotatedType[] params = new AnnotatedType[parameterized.getActualTypeArguments().length]; @@ -870,7 +850,8 @@ private static AnnotatedType annotate(Type type, boolean expandGenerics, Map clazz = (Class) type; if (clazz.isArray()) { Class componentClass = clazz.getComponentType(); - return AnnotatedArrayTypeImpl.createArrayType( - new AnnotatedTypeImpl(componentClass, componentClass.getAnnotations()), new Annotation[0]); + return AnnotatedArrayTypeImpl.createArrayType(annotate(componentClass), new Annotation[0]); } if (clazz.getTypeParameters().length > 0 && expandGenerics) { return expandClassGenerics(clazz); } - return new AnnotatedTypeImpl(clazz, clazz.getAnnotations()); + AnnotatedType owner = clazz.getDeclaringClass() != null ? annotate(clazz.getDeclaringClass()) : null; + return new AnnotatedTypeImpl(clazz, clazz.getAnnotations(), owner); } throw new IllegalArgumentException("Unrecognized type: " + type.getTypeName()); } @@ -931,7 +912,7 @@ private static AnnotatedType annotate(Type type, boolean expandGenerics, Map T replaceAnnotations(T original, Annotation[] annotations) { if (original instanceof AnnotatedParameterizedType) { return (T) new AnnotatedParameterizedTypeImpl((ParameterizedType) original.getType(), annotations, - ((AnnotatedParameterizedType) original).getAnnotatedActualTypeArguments()); + ((AnnotatedParameterizedType) original).getAnnotatedActualTypeArguments(), original.getAnnotatedOwnerType()); } if (original instanceof AnnotatedCaptureType) { AnnotatedCaptureTypeImpl capture = (AnnotatedCaptureTypeImpl) original; @@ -956,7 +937,7 @@ public static T replaceAnnotations(T original, Annotat return (T) new AnnotatedArrayTypeImpl(original.getType(), annotations, ((AnnotatedArrayType) original).getAnnotatedGenericComponentType()); } - return (T) new AnnotatedTypeImpl(original.getType(), annotations); + return (T) new AnnotatedTypeImpl(original.getType(), annotations, original.getAnnotatedOwnerType()); } /** @@ -985,7 +966,7 @@ public static T mergeAnnotations(T t1, T t2) { for (int i = 0; i < p1.length; i++) { params[i] = mergeAnnotations(p1[i], p2[i]); } - return (T) new AnnotatedParameterizedTypeImpl((ParameterizedType) t1.getType(), merged, params); + return (T) new AnnotatedParameterizedTypeImpl((ParameterizedType) t1.getType(), merged, params, t1.getAnnotatedOwnerType()); } if (t1 instanceof AnnotatedWildcardType) { AnnotatedType[] l1 = ((AnnotatedWildcardType) t1).getAnnotatedLowerBounds(); @@ -1011,7 +992,7 @@ public static T mergeAnnotations(T t1, T t2) { ((AnnotatedArrayType) t2).getAnnotatedGenericComponentType()); return (T) new AnnotatedArrayTypeImpl(t1.getType(), merged, componentType); } - return (T) new AnnotatedTypeImpl(t1.getType(), merged); + return (T) new AnnotatedTypeImpl(t1.getType(), merged, t1.getAnnotatedOwnerType()); } /** @@ -1023,14 +1004,18 @@ public static T mergeAnnotations(T t1, T t2) { * @return The new parameterized type */ public static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, AnnotatedType[] typeParameters) { - return replaceParameters(type, new Annotation[0], typeParameters); + return replaceParameters(type, new Annotation[0], typeParameters, null); + } + + public static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, AnnotatedType[] typeParameters, AnnotatedType ownerType) { + return replaceParameters(type, new Annotation[0], typeParameters, ownerType); } - private static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, Annotation[] annotations, AnnotatedType[] typeParameters) { + private static AnnotatedParameterizedType replaceParameters(AnnotatedParameterizedType type, Annotation[] annotations, AnnotatedType[] typeParameters, AnnotatedType ownerType) { Type[] rawArguments = mapArray(typeParameters, Type[]::new, AnnotatedType::getType); ParameterizedType inner = (ParameterizedType) type.getType(); - ParameterizedType rawType = (ParameterizedType) TypeFactory.parameterizedInnerClass(inner.getOwnerType(), erase(inner), rawArguments); - return new AnnotatedParameterizedTypeImpl(rawType, merge(type.getAnnotations(), annotations), typeParameters); + ParameterizedType rawType = (ParameterizedType) TypeFactory.parameterizedInnerClass(ownerType != null ? ownerType.getType() : inner.getOwnerType(), erase(inner), rawArguments); + return new AnnotatedParameterizedTypeImpl(rawType, merge(type.getAnnotations(), annotations), typeParameters, ownerType != null ? ownerType : type.getAnnotatedOwnerType()); } /** @@ -1080,7 +1065,11 @@ protected AnnotatedType visitClass(AnnotatedType type) { Annotation[] annotations = type.getAnnotations(); Class raw = (Class) type.getType(); annotations = merge(annotations, raw.getAnnotations()); - return new AnnotatedTypeImpl(leafTransformer.apply(type.getType()), annotations); + AnnotatedType owner = type.getAnnotatedOwnerType(); + if (owner != null) { + owner = transform(owner, this); + } + return new AnnotatedTypeImpl(leafTransformer.apply(type.getType()), annotations, owner); } @Override @@ -1095,8 +1084,12 @@ protected AnnotatedType visitParameterizedType(AnnotatedParameterizedType type) .map(param -> transform(param, this)) .toArray(AnnotatedType[]::new); + AnnotatedType owner = type.getAnnotatedOwnerType(); + if (owner != null) { + owner = transform(owner, this); + } Class raw = (Class)((ParameterizedType) type.getType()).getRawType(); - return GenericTypeReflector.replaceParameters(type, raw.getAnnotations(), params); + return GenericTypeReflector.replaceParameters(type, raw.getAnnotations(), params, owner); } }); } @@ -1170,8 +1163,8 @@ protected AnnotatedType visitWildcardType(AnnotatedWildcardType type) { @Override protected AnnotatedType visitCaptureType(AnnotatedCaptureType type) { AnnotatedType bound = type.getAnnotatedLowerBounds().length > 0 - ? type.getAnnotatedLowerBounds()[0] - : type.getAnnotatedUpperBounds()[0]; + ? type.getAnnotatedLowerBounds()[0] + : type.getAnnotatedUpperBounds()[0]; if (bound instanceof AnnotatedParameterizedType) { AnnotatedType[] typeArguments = ((AnnotatedParameterizedType) bound).getAnnotatedActualTypeArguments(); @@ -1192,7 +1185,7 @@ protected AnnotatedType visitCaptureType(AnnotatedCaptureType type) { private static AnnotatedParameterizedType expandClassGenerics(Class type) { ParameterizedType inner = new ParameterizedTypeImpl(type, type.getTypeParameters(), type.getDeclaringClass()); AnnotatedType[] params = mapArray(type.getTypeParameters(), AnnotatedType[]::new, GenericTypeReflector::annotate); - return new AnnotatedParameterizedTypeImpl(inner, type.getAnnotations(), params); + return new AnnotatedParameterizedTypeImpl(inner, type.getAnnotations(), params, null); } /** @@ -1307,9 +1300,9 @@ public boolean equals(Object obj) { CaptureType that = ((CaptureCacheKey) obj).capture; return this.capture == that - || (capture.getWildcardType().equals(that.getWildcardType()) - && capture.getTypeVariable().equals(that.getTypeVariable()) - && Arrays.equals(capture.getUpperBounds(), that.getUpperBounds())); + || (capture.getWildcardType().equals(that.getWildcardType()) + && capture.getTypeVariable().equals(that.getTypeVariable()) + && Arrays.equals(capture.getUpperBounds(), that.getUpperBounds())); } } @@ -1334,8 +1327,8 @@ public boolean equals(Object obj) { AnnotatedCaptureCacheKey that = ((AnnotatedCaptureCacheKey) obj); return this.capture == that.capture || - (new CaptureCacheKey(raw).equals(new CaptureCacheKey(that.raw)) - && Arrays.equals(capture.getAnnotations(), that.capture.getAnnotations())); + (new CaptureCacheKey(raw).equals(new CaptureCacheKey(that.raw)) + && Arrays.equals(capture.getAnnotations(), that.capture.getAnnotations())); } } } diff --git a/src/main/java/io/leangen/geantyref/TypeFactory.java b/src/main/java/io/leangen/geantyref/TypeFactory.java index fca30ad..43dd5d7 100644 --- a/src/main/java/io/leangen/geantyref/TypeFactory.java +++ b/src/main/java/io/leangen/geantyref/TypeFactory.java @@ -50,7 +50,7 @@ public static AnnotatedType annotatedClass(Class clazz, Annotation[] annotati } public static AnnotatedType parameterizedAnnotatedClass(Class clazz, Annotation[] annotations, AnnotatedType... arguments) { - return parameterizedAnnotatedInnerClass(null, clazz, annotations, arguments); + return parameterizedAnnotatedInnerClass((AnnotatedType) null, clazz, annotations, arguments); } public static AnnotatedType annotatedInnerClass(Type owner, Class clazz, Annotation[] annotations) { @@ -58,11 +58,17 @@ public static AnnotatedType annotatedInnerClass(Type owner, Class clazz, Anno } public static AnnotatedType parameterizedAnnotatedInnerClass(Type owner, Class clazz, Annotation[] annotations, AnnotatedType... arguments) { + AnnotatedType ownerType = owner != null ? GenericTypeReflector.annotate(owner) : null; + return parameterizedAnnotatedInnerClass(ownerType, clazz, annotations, arguments); + } + + public static AnnotatedType parameterizedAnnotatedInnerClass(AnnotatedType owner, Class clazz, Annotation[] annotations, AnnotatedType... arguments) { if (arguments == null || arguments.length == 0) { return GenericTypeReflector.annotate(clazz, annotations); } Type[] typeArguments = Arrays.stream(arguments).map(AnnotatedType::getType).toArray(Type[]::new); - return new AnnotatedParameterizedTypeImpl((ParameterizedType) parameterizedInnerClass(owner, clazz, typeArguments), annotations, arguments); + Type innerOwner = owner != null ? owner.getType() : null; + return new AnnotatedParameterizedTypeImpl((ParameterizedType) parameterizedInnerClass(innerOwner, clazz, typeArguments), annotations, arguments, owner); } public static AnnotatedParameterizedType parameterizedAnnotatedType(ParameterizedType type, Annotation[] typeAnnotations, Annotation[]... argumentAnnotations) { diff --git a/src/main/java/io/leangen/geantyref/TypeVisitor.java b/src/main/java/io/leangen/geantyref/TypeVisitor.java index 624e790..ce1a200 100644 --- a/src/main/java/io/leangen/geantyref/TypeVisitor.java +++ b/src/main/java/io/leangen/geantyref/TypeVisitor.java @@ -25,8 +25,12 @@ protected AnnotatedType visitParameterizedType(AnnotatedParameterizedType type) AnnotatedType[] params = Arrays.stream(type.getAnnotatedActualTypeArguments()) .map(param -> transform(param, this)) .toArray(AnnotatedType[]::new); + AnnotatedType owner = type.getAnnotatedOwnerType(); + if (owner != null) { + owner = transform(owner, this); + } - return GenericTypeReflector.replaceParameters(type, params); + return GenericTypeReflector.replaceParameters(type, params, owner); } protected AnnotatedType visitWildcardType(AnnotatedWildcardType type) { diff --git a/src/main/java/io/leangen/geantyref/VarMap.java b/src/main/java/io/leangen/geantyref/VarMap.java index 0e4095e..a8b91df 100644 --- a/src/main/java/io/leangen/geantyref/VarMap.java +++ b/src/main/java/io/leangen/geantyref/VarMap.java @@ -7,7 +7,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.*; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -56,8 +55,8 @@ class VarMap { add(typeParameters[i], arguments[i]); } - Type owner = ((ParameterizedType) type.getType()).getOwnerType(); - type = (owner instanceof ParameterizedType) ? (AnnotatedParameterizedType) annotate(owner) : null; + AnnotatedType owner = type.getAnnotatedOwnerType(); + type = (owner instanceof AnnotatedParameterizedType) ? (AnnotatedParameterizedType) owner : null; } while (type != null); } @@ -119,9 +118,9 @@ AnnotatedType map(AnnotatedType type, MappingMode mappingMode) { typeParameters[i] = updateAnnotations(typeParameter, raw.getTypeParameters()[i].getAnnotations()); } Type[] rawArgs = stream(typeParameters).map(AnnotatedType::getType).toArray(Type[]::new); - Type innerOwnerType = inner.getOwnerType() == null ? null : map(annotate(inner.getOwnerType()), mappingMode).getType(); - ParameterizedType newInner = new ParameterizedTypeImpl((Class) inner.getRawType(), rawArgs, innerOwnerType); - return new AnnotatedParameterizedTypeImpl(newInner, merge(pType.getAnnotations(), raw.getAnnotations()), typeParameters); + AnnotatedType ownerType = pType.getAnnotatedOwnerType() == null ? null : map(pType.getAnnotatedOwnerType(), mappingMode); + ParameterizedType newInner = new ParameterizedTypeImpl((Class) inner.getRawType(), rawArgs, ownerType != null ? ownerType.getType() : null); + return new AnnotatedParameterizedTypeImpl(newInner, merge(pType.getAnnotations(), raw.getAnnotations()), typeParameters, ownerType); } else if (type instanceof AnnotatedWildcardType) { AnnotatedWildcardType wType = (AnnotatedWildcardType) type; AnnotatedType[] up = map(wType.getAnnotatedUpperBounds(), mappingMode); @@ -154,8 +153,8 @@ AnnotatedType[] map(AnnotatedType[] types, MappingMode mappingMode) { } Type[] map(Type[] types) { - AnnotatedType[] result = map(Arrays.stream(types).map(GenericTypeReflector::annotate).toArray(AnnotatedType[]::new)); - return Arrays.stream(result).map(AnnotatedType::getType).toArray(Type[]::new); + AnnotatedType[] result = map(stream(types).map(GenericTypeReflector::annotate).toArray(AnnotatedType[]::new)); + return stream(result).map(AnnotatedType::getType).toArray(Type[]::new); } Type map(Type type) { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7a45cb6..2f13f75 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,9 +4,8 @@ */ /** - * GenTyRef - Type reflection library for Java + * GeantyRef - Type reflection library for Java */ module io.leangen.geantyref { - requires static java.desktop; exports io.leangen.geantyref; } diff --git a/src/test/java/io/leangen/geantyref/AbstractGenericsReflectorTest.java b/src/test/java/io/leangen/geantyref/AbstractGenericsReflectorTest.java index 9bff356..5f36a3a 100644 --- a/src/test/java/io/leangen/geantyref/AbstractGenericsReflectorTest.java +++ b/src/test/java/io/leangen/geantyref/AbstractGenericsReflectorTest.java @@ -5,21 +5,17 @@ package io.leangen.geantyref; -import static org.junit.Assert.assertNotEquals; - import junit.framework.TestCase; import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.RandomAccess; +import static org.junit.Assert.assertNotEquals; + public abstract class AbstractGenericsReflectorTest extends TestCase { /** * A constant that's false, to use in an if() block for code that's only there to show that it @@ -100,7 +96,7 @@ private void testMutualSupertypes(TypeToken... types) { * Tests that the types given are not equal, but they are eachother's supertype. */ private void checkedTestMutualSupertypes(TypeToken type1, TypeToken type2) { - assertFalse(type1.equals(type2)); + assertNotEquals(type1, type2); assertTrue(isSupertype(type1, type2)); assertTrue(isSupertype(type2, type1)); } diff --git a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java index 266f14c..d773edc 100644 --- a/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java +++ b/src/test/java/io/leangen/geantyref/GenericTypeReflectorTest.java @@ -5,10 +5,8 @@ package io.leangen.geantyref; -import java.awt.*; import java.io.Serializable; import java.lang.reflect.*; -import java.util.List; import java.util.*; import static io.leangen.geantyref.Annotations.*; @@ -28,7 +26,6 @@ public GenericTypeReflectorTest() { } public void testGetTypeParameter() { - @SuppressWarnings("serial") class StringList extends ArrayList { } assertEquals(String.class, GenericTypeReflector.getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0])); @@ -62,10 +59,19 @@ public void testGetExactReturnTypeIllegalArgument() throws SecurityException, No /** * Same as {@link #testGetExactReturnTypeIllegalArgument()} for getExactFieldType */ + public void testGetExactFieldTypeShadowedFieldIllegalArgument() throws SecurityException, NoSuchFieldException { + Field field = GummyWorm.class.getField("size"); + try { + GenericTypeReflector.getExactFieldType(field, Gummy.class); + fail("expected exception"); + } catch (IllegalArgumentException e) { // expected + } + } + public void testGetExactFieldTypeIllegalArgument() throws SecurityException, NoSuchFieldException { - Field field = Dimension.class.getField("width"); + Field field = Pen.class.getField("size"); try { - GenericTypeReflector.getExactFieldType(field, List.class); + GenericTypeReflector.getExactFieldType(field, GummyWorm.class); fail("expected exception"); } catch (IllegalArgumentException e) { // expected } @@ -222,6 +228,48 @@ public void testReduceBounded() { assertEquals(Long.class, reduced.getAnnotatedActualTypeArguments()[1].getType()); } + public void testOwnerTypeResolution() throws NoSuchMethodException { + AnnotatedType type = new TypeToken.Lock<@A5 Double>>() {}.getAnnotatedType(); + Method echo = Box.Lock.class.getDeclaredMethod("echo"); + AnnotatedType exactReturnType = GenericTypeReflector.getExactReturnType(echo, type); + assertTrue(exactReturnType instanceof AnnotatedParameterizedType); + AnnotatedParameterizedType parameterized = (AnnotatedParameterizedType) exactReturnType; + assertEquals(Integer.class, parameterized.getAnnotatedActualTypeArguments()[0].getType()); + assertAnnotationsPresent(parameterized.getAnnotatedActualTypeArguments()[0], A4.class, A1.class, A2.class); + AnnotatedParameterizedType owner = (AnnotatedParameterizedType) parameterized.getAnnotatedOwnerType(); + assertEquals(Integer.class, owner.getAnnotatedActualTypeArguments()[0].getType()); + assertAnnotationsPresent(owner.getAnnotatedActualTypeArguments()[0], A4.class, A1.class); + } + + public void testOwnerTypeResolution2() throws NoSuchMethodException { + AnnotatedType type = new TypeToken.Lock<@A5 Double>>() {}.getAnnotatedType(); + Method echo = Box.Lock.class.getDeclaredMethod("echo2"); + AnnotatedType exactReturnType = GenericTypeReflector.getExactReturnType(echo, type); + assertTrue(exactReturnType instanceof AnnotatedParameterizedType); + AnnotatedParameterizedType parameterized = (AnnotatedParameterizedType) exactReturnType; + assertEquals(Double.class, parameterized.getAnnotatedActualTypeArguments()[0].getType()); + assertAnnotationsPresent(parameterized.getAnnotatedActualTypeArguments()[0], A5.class, A2.class); + AnnotatedParameterizedType owner = (AnnotatedParameterizedType) parameterized.getAnnotatedOwnerType(); + assertEquals(Integer.class, owner.getAnnotatedActualTypeArguments()[0].getType()); + assertAnnotationsPresent(owner.getAnnotatedActualTypeArguments()[0], A4.class, A1.class); + } + + public void testOwnerTypeResolution3() throws NoSuchMethodException { + AnnotatedType type = new TypeToken.Shackle>() {}.getAnnotatedType(); + Method echo = Box.Shackle.class.getDeclaredMethod("echo2"); + AnnotatedType exactReturnType = GenericTypeReflector.resolveExactType(echo.getAnnotatedReturnType(), type); + System.out.println(exactReturnType); + assertTrue(exactReturnType instanceof AnnotatedParameterizedType); + AnnotatedParameterizedType parameterized = (AnnotatedParameterizedType) exactReturnType; + //Only parameterized because its owner is. No actual parameters of its own. + assertEquals(0, parameterized.getAnnotatedActualTypeArguments().length); + assertEquals(Box.Shackle.class, GenericTypeReflector.erase(parameterized.getType())); + AnnotatedParameterizedType annotatedOwnerType = (AnnotatedParameterizedType) parameterized.getAnnotatedOwnerType(); + assertEquals(Box.class, GenericTypeReflector.erase(annotatedOwnerType.getType())); + assertEquals(Integer.class, annotatedOwnerType.getAnnotatedActualTypeArguments()[0].getType()); + assertAnnotationsPresent(annotatedOwnerType.getAnnotatedActualTypeArguments()[0], A4.class, A1.class); + } + private class N {} private class P extends N {} private class L extends P, List>> {} @@ -243,4 +291,30 @@ private static class ComplexBounds private static AnnotatedType t2 = new TypeToken<@A5 Optional<@A4 Map<@A2 String, @A3 Integer @A1 []>>>(){}.getAnnotatedType(); private class Outer { @A1 class Inner { @A2 class Innermost {}}} + + public static class Pen { + public int size; + } + public static class Gummy { + public int size; + } + public static class GummyWorm extends Gummy { + public int size; + } + + class Box<@A1 T> { + class Lock<@A2 S> { + Lock echo() { + return null; + } + Lock echo2() { + return null; + } + } + class Shackle extends Lock { + Shackle echo2() { + return this; + } + } + } }