8057804: AnnotatedType interfaces provide no way to get annotations on owner type
Reviewed-by: darcy, redestad
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedArrayType.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedArrayType.java Wed Dec 16 20:00:03 2015 +0100
@@ -42,4 +42,19 @@
* @see GenericArrayType#getGenericComponentType()
*/
AnnotatedType getAnnotatedGenericComponentType();
+
+ /**
+ * Returns the potentially annotated type that this type is a member of, if
+ * this type represents a nested type. For example, if this type is
+ * {@code @TA O<T>.I<S>}, return a representation of {@code @TA O<T>}.
+ *
+ * <p>Returns {@code null} for an {@code AnnotatedType} that is an instance
+ * of {@code AnnotatedArrayType}.
+ *
+ * @return {@code null}
+ *
+ * @since 1.9
+ */
+ @Override
+ AnnotatedType getAnnotatedOwnerType();
}
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedParameterizedType.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedParameterizedType.java Wed Dec 16 20:00:03 2015 +0100
@@ -41,4 +41,26 @@
* @see ParameterizedType#getActualTypeArguments()
*/
AnnotatedType[] getAnnotatedActualTypeArguments();
+
+ /**
+ * Returns the potentially annotated type that this type is a member of, if
+ * this type represents a nested type. For example, if this type is
+ * {@code @TA O<T>.I<S>}, return a representation of {@code @TA O<T>}.
+ *
+ * <p>Returns {@code null} if this {@code AnnotatedType} represents a
+ * top-level type, or a local or anonymous class, or a primitive type, or
+ * void.
+ *
+ * @return an {@code AnnotatedType} object representing the potentially
+ * annotated type that this type is a member of, or {@code null}
+ * @throws TypeNotPresentException if the owner type
+ * refers to a non-existent type declaration
+ * @throws MalformedParameterizedTypeException if the owner type
+ * refers to a parameterized type that cannot be instantiated
+ * for any reason
+ *
+ * @since 1.9
+ */
+ @Override
+ AnnotatedType getAnnotatedOwnerType();
}
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedType.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedType.java Wed Dec 16 20:00:03 2015 +0100
@@ -36,6 +36,37 @@
public interface AnnotatedType extends AnnotatedElement {
/**
+ * Returns the potentially annotated type that this type is a member of, if
+ * this type represents a nested type. For example, if this type is
+ * {@code @TA O<T>.I<S>}, return a representation of {@code @TA O<T>}.
+ *
+ * <p>Returns {@code null} if this {@code AnnotatedType} represents a
+ * top-level type, or a local or anonymous class, or a primitive type, or
+ * void.
+ *
+ * <p>Returns {@code null} if this {@code AnnotatedType} is an instance of
+ * {@code AnnotatedArrayType}, {@code AnnotatedTypeVariable}, or
+ * {@code AnnotatedWildcardType}.
+ *
+ * @implSpec
+ * This default implementation returns {@code null} and performs no other
+ * action.
+ *
+ * @return an {@code AnnotatedType} object representing the potentially
+ * annotated type that this type is a member of, or {@code null}
+ * @throws TypeNotPresentException if the owner type
+ * refers to a non-existent type declaration
+ * @throws MalformedParameterizedTypeException if the owner type
+ * refers to a parameterized type that cannot be instantiated
+ * for any reason
+ *
+ * @since 1.9
+ */
+ default AnnotatedType getAnnotatedOwnerType() {
+ return null;
+ }
+
+ /**
* Returns the underlying type that this annotated type represents.
*
* @return the type this annotated type represents
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedTypeVariable.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedTypeVariable.java Wed Dec 16 20:00:03 2015 +0100
@@ -43,4 +43,19 @@
* @see TypeVariable#getBounds()
*/
AnnotatedType[] getAnnotatedBounds();
+
+ /**
+ * Returns the potentially annotated type that this type is a member of, if
+ * this type represents a nested type. For example, if this type is
+ * {@code @TA O<T>.I<S>}, return a representation of {@code @TA O<T>}.
+ *
+ * <p>Returns {@code null} for an {@code AnnotatedType} that is an instance
+ * of {@code AnnotatedTypeVariable}.
+ *
+ * @return {@code null}
+ *
+ * @since 1.9
+ */
+ @Override
+ AnnotatedType getAnnotatedOwnerType();
}
--- a/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedWildcardType.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedWildcardType.java Wed Dec 16 20:00:03 2015 +0100
@@ -54,4 +54,19 @@
* @see WildcardType#getUpperBounds()
*/
AnnotatedType[] getAnnotatedUpperBounds();
+
+ /**
+ * Returns the potentially annotated type that this type is a member of, if
+ * this type represents a nested type. For example, if this type is
+ * {@code @TA O<T>.I<S>}, return a representation of {@code @TA O<T>}.
+ *
+ * <p>Returns {@code null} for an {@code AnnotatedType} that is an instance
+ * of {@code AnnotatedWildcardType}.
+ *
+ * @return {@code null}
+ *
+ * @since 1.9
+ */
+ @Override
+ AnnotatedType getAnnotatedOwnerType();
}
--- a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java Wed Dec 16 20:00:03 2015 +0100
@@ -62,7 +62,7 @@
decl);
if (type instanceof Class) {
return new AnnotatedTypeBaseImpl(type,
- addNesting(type, currentLoc),
+ currentLoc,
actualTypeAnnos,
allOnSameTarget,
decl);
@@ -74,7 +74,7 @@
decl);
} else if (type instanceof ParameterizedType) {
return new AnnotatedParameterizedTypeImpl((ParameterizedType)type,
- addNesting(type, currentLoc),
+ currentLoc,
actualTypeAnnos,
allOnSameTarget,
decl);
@@ -88,7 +88,7 @@
throw new AssertionError("Unknown instance of Type: " + type + "\nThis should not happen.");
}
- private static LocationInfo addNesting(Type type, LocationInfo addTo) {
+ public static LocationInfo nestingForType(Type type, LocationInfo addTo) {
if (isArray(type))
return addTo;
if (type instanceof Class) {
@@ -96,13 +96,13 @@
if (clz.getEnclosingClass() == null)
return addTo;
if (Modifier.isStatic(clz.getModifiers()))
- return addNesting(clz.getEnclosingClass(), addTo);
- return addNesting(clz.getEnclosingClass(), addTo.pushInner());
+ return nestingForType(clz.getEnclosingClass(), addTo);
+ return nestingForType(clz.getEnclosingClass(), addTo.pushInner());
} else if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType)type;
if (t.getOwnerType() == null)
return addTo;
- return addNesting(t.getOwnerType(), addTo.pushInner());
+ return nestingForType(t.getOwnerType(), addTo.pushInner());
}
return addTo;
}
@@ -118,8 +118,9 @@
return false;
}
+ static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0];
static final AnnotatedType EMPTY_ANNOTATED_TYPE = new AnnotatedTypeBaseImpl(null, LocationInfo.BASE_LOCATION,
- new TypeAnnotation[0], new TypeAnnotation[0], null);
+ EMPTY_TYPE_ANNOTATION_ARRAY, EMPTY_TYPE_ANNOTATION_ARRAY, null);
static final AnnotatedType[] EMPTY_ANNOTATED_TYPE_ARRAY = new AnnotatedType[0];
private static class AnnotatedTypeBaseImpl implements AnnotatedType {
@@ -177,6 +178,30 @@
return type;
}
+ @Override
+ public AnnotatedType getAnnotatedOwnerType() {
+ if (!(type instanceof Class<?>))
+ throw new IllegalStateException("Can't compute owner");
+
+ Class<?> inner = (Class<?>)type;
+ Class<?> owner = inner.getDeclaringClass();
+ if (owner == null) // top-level, local or anonymous
+ return null;
+ if (inner.isPrimitive() || inner == Void.TYPE)
+ return null;
+
+ LocationInfo outerLoc = nestingForType(owner, getLocation().popAllLocations((byte)1));
+ TypeAnnotation[]all = getTypeAnnotations();
+ List<TypeAnnotation> l = new ArrayList<>(all.length);
+
+ for (TypeAnnotation t : all)
+ if (t.getLocationInfo().isSameLocationInfo(outerLoc))
+ l.add(t);
+
+ return buildAnnotatedType(owner, outerLoc, l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), all, getDecl());
+
+ }
+
// Implementation details
final LocationInfo getLocation() {
return location;
@@ -198,11 +223,17 @@
@Override
public AnnotatedType getAnnotatedGenericComponentType() {
- return AnnotatedTypeFactory.buildAnnotatedType(getComponentType(),
- getLocation().pushArray(),
- getTypeAnnotations(),
- getTypeAnnotations(),
- getDecl());
+ Type t = getComponentType();
+ return AnnotatedTypeFactory.buildAnnotatedType(t,
+ nestingForType(t, getLocation().pushArray()),
+ getTypeAnnotations(),
+ getTypeAnnotations(),
+ getDecl());
+ }
+
+ @Override
+ public AnnotatedType getAnnotatedOwnerType() {
+ return null;
}
private Type getComponentType() {
@@ -227,6 +258,11 @@
return getTypeVariable().getAnnotatedBounds();
}
+ @Override
+ public AnnotatedType getAnnotatedOwnerType() {
+ return null;
+ }
+
private TypeVariable<?> getTypeVariable() {
return (TypeVariable)getType();
}
@@ -248,19 +284,35 @@
int initialCapacity = getTypeAnnotations().length;
for (int i = 0; i < res.length; i++) {
List<TypeAnnotation> l = new ArrayList<>(initialCapacity);
- LocationInfo newLoc = getLocation().pushTypeArg((byte)i);
+ LocationInfo newLoc = nestingForType(arguments[i], getLocation().pushTypeArg((byte)i));
for (TypeAnnotation t : getTypeAnnotations())
if (t.getLocationInfo().isSameLocationInfo(newLoc))
l.add(t);
res[i] = buildAnnotatedType(arguments[i],
- newLoc,
- l.toArray(new TypeAnnotation[0]),
- getTypeAnnotations(),
- getDecl());
+ newLoc,
+ l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY),
+ getTypeAnnotations(),
+ getDecl());
}
return res;
}
+ @Override
+ public AnnotatedType getAnnotatedOwnerType() {
+ Type owner = getParameterizedType().getOwnerType();
+ if (owner == null)
+ return null;
+ LocationInfo outerLoc = nestingForType(owner, getLocation().popAllLocations((byte)1));
+ TypeAnnotation[]all = getTypeAnnotations();
+ List<TypeAnnotation> l = new ArrayList<>(all.length);
+
+ for (TypeAnnotation t : all)
+ if (t.getLocationInfo().isSameLocationInfo(outerLoc))
+ l.add(t);
+
+ return buildAnnotatedType(owner, outerLoc, l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), all, getDecl());
+ }
+
private ParameterizedType getParameterizedType() {
return (ParameterizedType)getType();
}
@@ -279,11 +331,11 @@
public AnnotatedType[] getAnnotatedUpperBounds() {
if (!hasUpperBounds()) {
return new AnnotatedType[] { buildAnnotatedType(Object.class,
- LocationInfo.BASE_LOCATION,
- new TypeAnnotation[0],
- new TypeAnnotation[0],
- null)
- };
+ LocationInfo.BASE_LOCATION,
+ EMPTY_TYPE_ANNOTATION_ARRAY,
+ EMPTY_TYPE_ANNOTATION_ARRAY,
+ null)
+ };
}
return getAnnotatedBounds(getWildcardType().getUpperBounds());
}
@@ -295,21 +347,26 @@
return getAnnotatedBounds(getWildcardType().getLowerBounds());
}
+ @Override
+ public AnnotatedType getAnnotatedOwnerType() {
+ return null;
+ }
+
private AnnotatedType[] getAnnotatedBounds(Type[] bounds) {
AnnotatedType[] res = new AnnotatedType[bounds.length];
Arrays.fill(res, EMPTY_ANNOTATED_TYPE);
- LocationInfo newLoc = getLocation().pushWildcard();
int initialCapacity = getTypeAnnotations().length;
for (int i = 0; i < res.length; i++) {
+ LocationInfo newLoc = nestingForType(bounds[i], getLocation().pushWildcard());
List<TypeAnnotation> l = new ArrayList<>(initialCapacity);
for (TypeAnnotation t : getTypeAnnotations())
if (t.getLocationInfo().isSameLocationInfo(newLoc))
l.add(t);
res[i] = buildAnnotatedType(bounds[i],
- newLoc,
- l.toArray(new TypeAnnotation[0]),
- getTypeAnnotations(),
- getDecl());
+ newLoc,
+ l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY),
+ getTypeAnnotations(),
+ getDecl());
}
return res;
}
--- a/jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java Wed Dec 16 20:00:03 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -187,13 +187,28 @@
return new LocationInfo(newDepth, res);
}
+ /** Pop a series of locations matching {@code tag}. Stop poping as soon as a non-matching tag is found. */
+ public LocationInfo popAllLocations(byte tag) {
+ LocationInfo l = this;
+ int newDepth = l.depth;
+ while(newDepth > 0 && l.locations[newDepth - 1].tag == tag) {
+ newDepth--;
+ }
+ if (newDepth != l.depth) {
+ Location[] res = new Location[newDepth];
+ System.arraycopy(this.locations, 0, res, 0, newDepth);
+ return new LocationInfo(newDepth, res);
+ } else
+ return l;
+ }
+
public TypeAnnotation[] filter(TypeAnnotation[] ta) {
ArrayList<TypeAnnotation> l = new ArrayList<>(ta.length);
for (TypeAnnotation t : ta) {
if (isSameLocationInfo(t.getLocationInfo()))
l.add(t);
}
- return l.toArray(new TypeAnnotation[0]);
+ return l.toArray(AnnotatedTypeFactory.EMPTY_TYPE_ANNOTATION_ARRAY);
}
boolean isSameLocationInfo(LocationInfo other) {
--- a/jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java Wed Dec 16 20:00:03 2015 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import jdk.internal.misc.SharedSecrets;
@@ -67,9 +66,8 @@
Type type,
TypeAnnotationTarget filter) {
TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations,
- cp,
- decl,
- container);
+ cp, decl, container);
+
List<TypeAnnotation> l = new ArrayList<>(tas.length);
for (TypeAnnotation t : tas) {
TypeAnnotationTargetInfo ti = t.getTargetInfo();
@@ -78,10 +76,10 @@
}
TypeAnnotation[] typeAnnotations = l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY);
return AnnotatedTypeFactory.buildAnnotatedType(type,
- LocationInfo.BASE_LOCATION,
- typeAnnotations,
- typeAnnotations,
- decl);
+ AnnotatedTypeFactory.nestingForType(type, LocationInfo.BASE_LOCATION),
+ typeAnnotations,
+ typeAnnotations,
+ decl);
}
/**
@@ -110,9 +108,8 @@
ArrayList[] l = new ArrayList[size]; // array of ArrayList<TypeAnnotation>
TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations,
- cp,
- decl,
- container);
+ cp, decl, container);
+
for (TypeAnnotation t : tas) {
TypeAnnotationTargetInfo ti = t.getTargetInfo();
if (ti.getTarget() == filter) {
@@ -136,10 +133,10 @@
typeAnnotations = EMPTY_TYPE_ANNOTATION_ARRAY;
}
result[i] = AnnotatedTypeFactory.buildAnnotatedType(types[i],
- LocationInfo.BASE_LOCATION,
- typeAnnotations,
- typeAnnotations,
- decl);
+ AnnotatedTypeFactory.nestingForType(types[i], LocationInfo.BASE_LOCATION),
+ typeAnnotations,
+ typeAnnotations,
+ decl);
}
return result;
@@ -278,7 +275,7 @@
}
}
res[i] = AnnotatedTypeFactory.buildAnnotatedType(bounds[i],
- loc,
+ AnnotatedTypeFactory.nestingForType(bounds[i], loc),
l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY),
candidates.toArray(EMPTY_TYPE_ANNOTATION_ARRAY),
(AnnotatedElement)decl);
--- a/jdk/test/java/lang/annotation/TypeAnnotationReflection.java Wed Dec 16 13:00:29 2015 -0500
+++ b/jdk/test/java/lang/annotation/TypeAnnotationReflection.java Wed Dec 16 20:00:03 2015 +0100
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8004698 8007073 8022343 8054304 8058595
+ * @bug 8004698 8007073 8022343 8054304 8057804 8058595
* @summary Unit test for type annotations
*/
@@ -358,6 +358,31 @@
check(annos.length == 2);
check(((TypeAnno)annos[0]).value().equals("I1"));
check(args[0].getAnnotation(TypeAnno2.class).value().equals("I2"));
+
+ // check type args
+ Field f = TestParameterizedType.class.getDeclaredField("theField");
+ AnnotatedParameterizedType fType = (AnnotatedParameterizedType)f.getAnnotatedType();
+ args = fType.getAnnotatedActualTypeArguments();
+ check(args.length == 1);
+ annos = args[0].getAnnotations();
+ check(annos.length == 1);
+ check(((TypeAnno2)annos[0]).value().equals("Map Arg"));
+ check(args[0].getAnnotation(TypeAnno2.class).value().equals("Map Arg"));
+
+ // check outer type type args
+ fType = (AnnotatedParameterizedType)fType.getAnnotatedOwnerType();
+ args = fType.getAnnotatedActualTypeArguments();
+ check(args.length == 1);
+ annos = args[0].getAnnotations();
+ check(annos.length == 1);
+ check(((TypeAnno2)annos[0]).value().equals("String Arg"));
+ check(args[0].getAnnotation(TypeAnno2.class).value().equals("String Arg"));
+
+ // check outer type normal type annotations
+ annos = fType.getAnnotations();
+ check(annos.length == 1);
+ check(((TypeAnno)annos[0]).value().equals("FieldOuter"));
+ check(fType.getAnnotation(TypeAnno.class).value().equals("FieldOuter"));
}
private static void testWildcardType() throws Exception {
@@ -563,9 +588,12 @@
abstract class TestParameterizedType implements @TypeAnno("M") Map<@TypeAnno("S")String, @TypeAnno("I") @TypeAnno2("I2")Integer> {
public ParameterizedOuter<String>.ParameterizedInner<Integer> foo() {return null;}
public @TypeAnno("O") ParameterizedOuter<@TypeAnno("S1") @TypeAnno2("S2") String>.
- @TypeAnno("I") ParameterizedInner<@TypeAnno("I1") @TypeAnno2("I2")Integer> foo2() {
+ @TypeAnno("I") ParameterizedInner<@TypeAnno("I1") @TypeAnno2("I2")Integer> foo2() {
return null;
}
+
+ public @TypeAnno("FieldOuter") ParameterizedOuter<@TypeAnno2("String Arg") String>.
+ @TypeAnno("FieldInner")ParameterizedInner<@TypeAnno2("Map Arg")Map> theField;
}
class ParameterizedOuter <T> {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java Wed Dec 16 20:00:03 2015 +0100
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058595
+ * @summary Test that AnnotatedType.getAnnotatedOwnerType() works as expected
+ *
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.Asserts
+ * @run main GetAnnotatedOwnerType
+ */
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+
+import jdk.testlibrary.Asserts;
+
+public class GetAnnotatedOwnerType<Dummy> {
+ public @TA("generic") GetAnnotatedOwnerType<String> . @TB("generic") Nested<Integer> genericField;
+ public @TA("raw") GetAnnotatedOwnerType . @TB("raw") Nested rawField;
+ public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("non-generic") Inner nonGeneric;
+ public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("generic") InnerGeneric<String> innerGeneric;
+ public @TA("non-generic") GetAnnotatedOwnerTypeAuxilliary . @TB("raw") InnerGeneric innerRaw;
+ public Object anonymous = new Object() {};
+ public @TA("array") Dummy[] dummy;
+ public @TA("wildcard") GetAnnotatedOwnerType<?> wildcard;
+ public @TA("typevariable") Dummy tv;
+ public @TA("bad") GetAnnotatedOwnerType<@TA("good") GetAnnotatedOwnerType<String> . @TB("tb") Nested<Integer> > typeArgument;
+ public GetAnnotatedOwnerType< GetAnnotatedOwnerType<String> .
+ B .
+ C<Class<?>, ? extends @TA("complicated") Exception> .
+ D<Number> > [] complicated;
+
+ public static void main(String[] args) throws Exception {
+ testGeneric();
+ testRaw();
+ testNonGeneric();
+ testInnerGeneric();
+ testInnerRaw();
+
+ testLocalClass();
+ testAnonymousClass();
+
+ testArray();
+ testWildcard();
+ testTypeParameter();
+
+ testTypeArgument();
+ testComplicated();
+ }
+
+ public static void testGeneric() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("genericField");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "generic");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((ParameterizedType) f.getGenericType()).getOwnerType());
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "generic");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testRaw() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("rawField");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "raw");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((Class<?>)f.getGenericType()).getEnclosingClass());
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "raw");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testNonGeneric() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("nonGeneric");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "non-generic");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((Class<?>)f.getGenericType()).getEnclosingClass());
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "non-generic");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testInnerGeneric() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("innerGeneric");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "generic");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((ParameterizedType) f.getGenericType()).getOwnerType());
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "non-generic");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testInnerRaw() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("innerRaw");
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = f.getAnnotatedType();
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "raw");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated, on the correct type
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getType(), ((Class<?>)f.getGenericType()).getEnclosingClass());
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "non-generic");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testLocalClass() throws Exception {
+ class ALocalClass {}
+ class OneMore {
+ public @TA("null") ALocalClass c;
+ }
+ testNegative(OneMore.class.getField("c").getAnnotatedType(), "Local class should return null");
+ }
+
+ public static void testAnonymousClass() throws Exception {
+ testNegative(GetAnnotatedOwnerType.class.getField("anonymous").getAnnotatedType(),
+ "Anonymous class should return null");
+ }
+
+ public static void testArray() throws Exception {
+ AnnotatedType t = GetAnnotatedOwnerType.class.getField("dummy").getAnnotatedType();
+ Asserts.assertTrue((t instanceof AnnotatedArrayType),
+ "Was expecting an AnnotatedArrayType " + t);
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ }
+
+ public static void testWildcard() throws Exception {
+ AnnotatedType tt = GetAnnotatedOwnerType.class.getField("wildcard").getAnnotatedType();
+ AnnotatedType t = ((AnnotatedParameterizedType)tt).getAnnotatedActualTypeArguments()[0];
+ Asserts.assertTrue((t instanceof AnnotatedWildcardType),
+ "Was expecting an AnnotatedWildcardType " + t);
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ }
+
+ public static void testTypeParameter() throws Exception {
+ AnnotatedType t = GetAnnotatedOwnerType.class.getField("tv").getAnnotatedType();
+ Asserts.assertTrue((t instanceof AnnotatedTypeVariable),
+ "Was expecting an AnnotatedTypeVariable " + t);
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ }
+
+ public static void testTypeArgument() throws Exception {
+ AnnotatedType tt = GetAnnotatedOwnerType.class.getField("typeArgument").getAnnotatedType();
+ Asserts.assertEquals(tt.getAnnotation(TA.class).value(), "bad");
+ Asserts.assertTrue(tt.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + tt.getAnnotations().length);
+
+ // make sure inner is correctly annotated
+ AnnotatedType inner = ((AnnotatedParameterizedType)tt).getAnnotatedActualTypeArguments()[0];
+ Asserts.assertEquals(inner.getAnnotation(TB.class).value(), "tb");
+ Asserts.assertTrue(inner.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + inner.getAnnotations().length);
+
+ // make sure owner is correctly annotated
+ AnnotatedType outer = inner.getAnnotatedOwnerType();
+ Asserts.assertEquals(outer.getAnnotation(TA.class).value(), "good");
+ Asserts.assertTrue(outer.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + outer.getAnnotations().length);
+ }
+
+ public static void testComplicated() throws Exception {
+ Field f = GetAnnotatedOwnerType.class.getField("complicated");
+
+ // Outermost level
+ AnnotatedType t = f.getAnnotatedType();
+ Asserts.assertTrue((t instanceof AnnotatedArrayType),
+ "Was expecting an AnnotatedArrayType " + t);
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: "
+ + t.getAnnotations().length);
+
+ // Component type
+ t = ((AnnotatedArrayType)t).getAnnotatedGenericComponentType();
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: "
+ + t.getAnnotations().length);
+
+ // Type arg GetAnnotatedOwnerType<String>...D<Number>
+ t = ((AnnotatedParameterizedType)t).getAnnotatedActualTypeArguments()[0];
+ Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: "
+ + t.getAnnotations().length);
+
+ // C<Class<?>, ? extends ...>
+ t = t.getAnnotatedOwnerType();
+ Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: "
+ + t.getAnnotations().length);
+
+ // ? extends
+ t = ((AnnotatedParameterizedType)t).getAnnotatedActualTypeArguments()[1];
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: "
+ + t.getAnnotations().length);
+
+ // @TA("complicated") Exception
+ t = ((AnnotatedWildcardType)t).getAnnotatedUpperBounds()[0];
+ testNegative(t, "" + t + " should not have an annotated owner type");
+ Asserts.assertEquals(t.getAnnotation(TA.class).value(), "complicated");
+ Asserts.assertTrue(t.getAnnotations().length == 1, "expecting one (1) annotation, got: "
+ + t.getAnnotations().length);
+ }
+
+ private static void testNegative(AnnotatedType t, String msg) {
+ Asserts.assertNull(t.getAnnotatedOwnerType(), msg);
+ }
+
+ public class Nested<AlsoDummy> {}
+ public class B {
+ public class C<R, S> {
+ public class D<T> {
+ }
+ }
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TA {
+ String value();
+ }
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TB {
+ String value();
+ }
+}
+
+class GetAnnotatedOwnerTypeAuxilliary {
+ class Inner {}
+
+ class InnerGeneric<Dummy> {}
+}