8057804: AnnotatedType interfaces provide no way to get annotations on owner type

Reviewed-by: darcy, redestad
This commit is contained in:
Joel Borggrén-Franck 2015-12-16 20:00:03 +01:00
parent 570ebc4176
commit 5c29ca104e
10 changed files with 525 additions and 48 deletions

View File

@ -42,4 +42,19 @@ public interface AnnotatedArrayType extends AnnotatedType {
* @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();
}

View File

@ -41,4 +41,26 @@ public interface AnnotatedParameterizedType extends AnnotatedType {
* @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();
}

View File

@ -35,6 +35,37 @@ package java.lang.reflect;
*/
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.
*

View File

@ -43,4 +43,19 @@ public interface AnnotatedTypeVariable extends AnnotatedType {
* @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();
}

View File

@ -54,4 +54,19 @@ public interface AnnotatedWildcardType extends AnnotatedType {
* @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();
}

View File

@ -62,7 +62,7 @@ public final class AnnotatedTypeFactory {
decl);
if (type instanceof Class) {
return new AnnotatedTypeBaseImpl(type,
addNesting(type, currentLoc),
currentLoc,
actualTypeAnnos,
allOnSameTarget,
decl);
@ -74,7 +74,7 @@ public final class AnnotatedTypeFactory {
decl);
} else if (type instanceof ParameterizedType) {
return new AnnotatedParameterizedTypeImpl((ParameterizedType)type,
addNesting(type, currentLoc),
currentLoc,
actualTypeAnnos,
allOnSameTarget,
decl);
@ -88,7 +88,7 @@ public final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
@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 @@ public final class AnnotatedTypeFactory {
return getTypeVariable().getAnnotatedBounds();
}
@Override
public AnnotatedType getAnnotatedOwnerType() {
return null;
}
private TypeVariable<?> getTypeVariable() {
return (TypeVariable)getType();
}
@ -248,19 +284,35 @@ public final class AnnotatedTypeFactory {
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 final class AnnotatedTypeFactory {
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 @@ public final class AnnotatedTypeFactory {
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;
}

View File

@ -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 @@ public final class TypeAnnotation {
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) {

View File

@ -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.nio.BufferUnderflowException;
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 @@ public final class TypeAnnotationParser {
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 @@ public final class TypeAnnotationParser {
}
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 @@ public final class TypeAnnotationParser {
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 @@ public final class TypeAnnotationParser {
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 @@ public final class TypeAnnotationParser {
}
}
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);

View File

@ -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 @@ public class TypeAnnotationReflection {
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 TestWildcardType {
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> {

View File

@ -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> {}
}