--- old/src/java.base/share/classes/java/lang/reflect/AnnotatedType.java 2015-10-24 09:55:15.000000000 +0200 +++ new/src/java.base/share/classes/java/lang/reflect/AnnotatedType.java 2015-10-24 09:55:15.000000000 +0200 @@ -36,6 +36,35 @@ 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.I}, return a representation of {@code @TA O}. + * + *

Returns {@code null} if this {@code AnnotatedType} represents a + * top-level type, or a local or anonymous class, or a primitive type, or + * void. + * + *

Returns {@code null} if this {@code AnnotatedType} is an instance of + * {@code AnnotatedArrayType}, {@code AnnotatedTypeVariable}, or + * {@code AnnotatedWildcardType}. + * + * @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 + * @throws UnsupportedOperationException if the {@code getAnnotatedOwnerType} + * operation is not supported by the implementation of this interface + * + * @since 1.9 + */ + default AnnotatedType getAnnotatedOwnerType() { + throw new UnsupportedOperationException("getAnnotatedOwnerType() is not supported by this implementation"); + } + + /** * Returns the underlying type that this annotated type represents. * * @return the type this annotated type represents --- old/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java 2015-10-24 09:55:16.000000000 +0200 +++ new/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java 2015-10-24 09:55:16.000000000 +0200 @@ -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; } @@ -121,6 +121,7 @@ static final AnnotatedType EMPTY_ANNOTATED_TYPE = new AnnotatedTypeBaseImpl(null, LocationInfo.BASE_LOCATION, new TypeAnnotation[0], new TypeAnnotation[0], null); static final AnnotatedType[] EMPTY_ANNOTATED_TYPE_ARRAY = new AnnotatedType[0]; + static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0]; private static class AnnotatedTypeBaseImpl implements AnnotatedType { private final Type type; @@ -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 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 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(new TypeAnnotation[0]), + 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 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, + new TypeAnnotation[0], + new TypeAnnotation[0], + null) + }; } return getAnnotatedBounds(getWildcardType().getUpperBounds()); } @@ -295,12 +347,17 @@ 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 l = new ArrayList<>(initialCapacity); for (TypeAnnotation t : getTypeAnnotations()) if (t.getLocationInfo().isSameLocationInfo(newLoc)) --- old/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java 2015-10-24 09:55:17.000000000 +0200 +++ new/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java 2015-10-24 09:55:16.000000000 +0200 @@ -187,6 +187,28 @@ return new LocationInfo(newDepth, res); } + public LocationInfo popLocation(byte tag) { + if (locations[depth-1].tag != tag) + throw new IllegalStateException("Tags not matching"); + return popLocation(); + } + + public LocationInfo popLocation() { + int newDepth = this.depth - 1; + Location[] res = new Location[newDepth]; + System.arraycopy(this.locations, 0, res, 0, newDepth); + 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; + while(l.depth > 0 && l.locations[l.depth - 1].tag == tag) { + l = l.popLocation(); + } + return l; + } + public TypeAnnotation[] filter(TypeAnnotation[] ta) { ArrayList l = new ArrayList<>(ta.length); for (TypeAnnotation t : ta) { --- old/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java 2015-10-24 09:55:17.000000000 +0200 +++ new/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java 2015-10-24 09:55:17.000000000 +0200 @@ -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 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[] 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); --- old/test/java/lang/annotation/TypeAnnotationReflection.java 2015-10-24 09:55:18.000000000 +0200 +++ new/test/java/lang/annotation/TypeAnnotationReflection.java 2015-10-24 09:55:18.000000000 +0200 @@ -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.ParameterizedInner 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 { --- /dev/null 2015-10-24 09:55:19.000000000 +0200 +++ new/test/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java 2015-10-24 09:55:19.000000000 +0200 @@ -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 { + public @TA("generic") GetAnnotatedOwnerType . @TB("generic") Nested 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 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 . @TB("tb") Nested > typeArgument; + public GetAnnotatedOwnerType< GetAnnotatedOwnerType . + B . + C, ? extends @TA("complicated") Exception> . + D > [] 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...D + t = ((AnnotatedParameterizedType)t).getAnnotatedActualTypeArguments()[0]; + Asserts.assertTrue(t.getAnnotations().length == 0, "expecting zero annotation, got: " + + t.getAnnotations().length); + + // C, ? 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 {} + public class B { + public class C { + public class D { + } + } + } + + @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 {} +}