8057804: AnnotatedType interfaces provide no way to get annotations on owner type
authorjfranck
Wed, 16 Dec 2015 20:00:03 +0100
changeset 34708 4a1e3728135c
parent 34707 5866a10ac337
child 34709 931408264ee8
8057804: AnnotatedType interfaces provide no way to get annotations on owner type Reviewed-by: darcy, redestad
jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedArrayType.java
jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedParameterizedType.java
jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedType.java
jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedTypeVariable.java
jdk/src/java.base/share/classes/java/lang/reflect/AnnotatedWildcardType.java
jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java
jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotation.java
jdk/src/java.base/share/classes/sun/reflect/annotation/TypeAnnotationParser.java
jdk/test/java/lang/annotation/TypeAnnotationReflection.java
jdk/test/java/lang/annotation/typeAnnotations/GetAnnotatedOwnerType.java
--- 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> {}
+}