changeset 10387:b8927cbfb893

8027170: Annotations declared on super-super-class should be overridden by super-class. Reviewed-by: jfranck Contributed-by: andreas.lundblad@oracle.com, peter.levart@gmail.com
author alundblad
date Thu, 24 Oct 2013 18:52:13 +0200
parents e2ec05b2ed94
children 808b9ef4f667
files src/share/classes/java/lang/Class.java src/share/classes/java/lang/System.java src/share/classes/sun/misc/JavaLangAccess.java src/share/classes/sun/reflect/annotation/AnnotationSupport.java test/java/lang/annotation/repeatingAnnotations/InheritedAssociatedAnnotations.java
diffstat 5 files changed, 157 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/Class.java	Wed Oct 23 15:37:40 2013 +0400
+++ b/src/share/classes/java/lang/Class.java	Thu Oct 24 18:52:13 2013 +0200
@@ -3316,7 +3316,7 @@
 
         AnnotationData annotationData = annotationData();
         return AnnotationSupport.getAssociatedAnnotations(annotationData.declaredAnnotations,
-                                                          annotationData.annotations,
+                                                          this,
                                                           annotationClass);
     }
 
@@ -3442,6 +3442,10 @@
         return annotationType;
     }
 
+    Map<Class<? extends Annotation>, Annotation> getDeclaredAnnotationMap() {
+        return annotationData().declaredAnnotations;
+    }
+
     /* Backing store of user-defined values pertaining to this class.
      * Maintained by the ClassValue class.
      */
--- a/src/share/classes/java/lang/System.java	Wed Oct 23 15:37:40 2013 +0400
+++ b/src/share/classes/java/lang/System.java	Thu Oct 24 18:52:13 2013 +0200
@@ -26,10 +26,12 @@
 
 import java.io.*;
 import java.lang.reflect.Executable;
+import java.lang.annotation.Annotation;
 import java.security.AccessControlContext;
 import java.util.Properties;
 import java.util.PropertyPermission;
 import java.util.StringTokenizer;
+import java.util.Map;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.AllPermission;
@@ -1227,6 +1229,9 @@
             public AnnotationType getAnnotationType(Class<?> klass) {
                 return klass.getAnnotationType();
             }
+            public Map<Class<? extends Annotation>, Annotation> getDeclaredAnnotationMap(Class<?> klass) {
+                return klass.getDeclaredAnnotationMap();
+            }
             public byte[] getRawClassAnnotations(Class<?> klass) {
                 return klass.getRawAnnotations();
             }
--- a/src/share/classes/sun/misc/JavaLangAccess.java	Wed Oct 23 15:37:40 2013 +0400
+++ b/src/share/classes/sun/misc/JavaLangAccess.java	Thu Oct 24 18:52:13 2013 +0200
@@ -28,6 +28,7 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Executable;
 import java.security.AccessControlContext;
+import java.util.Map;
 
 import sun.reflect.ConstantPool;
 import sun.reflect.annotation.AnnotationType;
@@ -50,6 +51,11 @@
     AnnotationType getAnnotationType(Class<?> klass);
 
     /**
+     * Get the declared annotations for a given class, indexed by their types.
+     */
+    Map<Class<? extends Annotation>, Annotation> getDeclaredAnnotationMap(Class<?> klass);
+
+    /**
      * Get the array of bytes that is the class-file representation
      * of this Class' annotations.
      */
--- a/src/share/classes/sun/reflect/annotation/AnnotationSupport.java	Wed Oct 23 15:37:40 2013 +0400
+++ b/src/share/classes/sun/reflect/annotation/AnnotationSupport.java	Thu Oct 24 18:52:13 2013 +0200
@@ -32,8 +32,12 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+
+import sun.misc.JavaLangAccess;
 
 public final class AnnotationSupport {
+    private static final JavaLangAccess LANG_ACCESS = sun.misc.SharedSecrets.getJavaLangAccess();
 
     /**
      * Finds and returns all annotations in {@code annotations} matching
@@ -51,17 +55,13 @@
      *
      * @param annotations the {@code Map} in which to search for annotations
      * @param annoClass the type of annotation to search for
-     * @param includeNonInheritedContainees if false, the annoClass must be
-     *        inheritable for the containers to be searched
      *
      * @return an array of instances of {@code annoClass} or an empty
      *         array if none were found
      */
-    private static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(
+    public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(
             Map<Class<? extends Annotation>, Annotation> annotations,
-            Class<A> annoClass,
-            boolean includeNonInheritedContainees) {
-
+            Class<A> annoClass) {
         List<A> result = new ArrayList<A>();
 
         @SuppressWarnings("unchecked")
@@ -69,17 +69,12 @@
         if (direct != null)
             result.add(direct);
 
-        if (includeNonInheritedContainees ||
-                AnnotationType.getInstance(annoClass).isInherited()) {
-            A[] indirect = getIndirectlyPresent(annotations, annoClass);
+        A[] indirect = getIndirectlyPresent(annotations, annoClass);
+        if (indirect != null && indirect.length != 0) {
+            boolean indirectFirst = direct == null ||
+                                    containerBeforeContainee(annotations, annoClass);
 
-            if (indirect != null) {
-
-                boolean indirectFirst = direct == null ||
-                                        containerBeforeContainee(annotations, annoClass);
-
-                result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect));
-            }
+            result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect));
         }
 
         @SuppressWarnings("unchecked")
@@ -87,19 +82,6 @@
         return result.toArray(arr);
     }
 
-
-    /**
-     * Equivalent to calling {@code getDirectlyAndIndirectlyPresentAnnotations(
-     * annotations, annoClass, true)}.
-     */
-    public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(
-            Map<Class<? extends Annotation>, Annotation> annotations,
-            Class<A> annoClass) {
-
-        return getDirectlyAndIndirectlyPresent(annotations, annoClass, true);
-    }
-
-
     /**
      * Finds and returns all annotations matching the given {@code annoClass}
      * indirectly present in {@code annotations}.
@@ -166,22 +148,28 @@
      * annotations in the relevant map.
      *
      * @param declaredAnnotations the declared annotations indexed by their types
-     * @param allAnnotations declared and inherited annotations indexed by their types
+     * @param decl the class declaration on which to search for annotations
      * @param annoClass the type of annotation to search for
      *
      * @return an array of instances of {@code annoClass} or an empty array if none were found.
      */
     public static <A extends Annotation> A[] getAssociatedAnnotations(
             Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
-            Map<Class<? extends Annotation>, Annotation> allAnnotations,
+            Class<?> decl,
             Class<A> annoClass) {
+        Objects.requireNonNull(decl);
 
         // Search declared
         A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass);
 
         // Search inherited
-        if (result.length == 0)
-            result = getDirectlyAndIndirectlyPresent(allAnnotations, annoClass, false);
+        if(AnnotationType.getInstance(annoClass).isInherited()) {
+            Class<?> superDecl = decl.getSuperclass();
+            while (result.length == 0 && superDecl != null) {
+                result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass);
+                superDecl = superDecl.getSuperclass();
+            }
+        }
 
         return result;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/annotation/repeatingAnnotations/InheritedAssociatedAnnotations.java	Thu Oct 24 18:52:13 2013 +0200
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2013, 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     8027170
+ * @summary getAnnotationsByType needs to take the class hierarchy into account
+ *          when determining which annotations are associated with a given
+ *          class.
+ * @run main InheritedAssociatedAnnotations
+ */
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.Arrays;
+
+public class InheritedAssociatedAnnotations {
+
+    public static void main(String[] args) {
+        checkAssociated(A3.class);
+        checkAssociated(B3.class);
+        checkAssociated(C3.class);
+        checkAssociated(D3.class);
+    }
+
+    private static void checkAssociated(AnnotatedElement ae) {
+        Ann[] actual = ae.getAnnotationsByType(Ann.class);
+        Ann[] expected = ae.getAnnotation(ExpectedAssociated.class).value();
+
+        if (!Arrays.equals(actual, expected)) {
+            throw new RuntimeException(String.format(
+                    "Test failed for %s: Expected %s but got %s.",
+                    ae,
+                    Arrays.toString(expected),
+                    Arrays.toString(actual)));
+        }
+    }
+
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface ExpectedAssociated {
+    Ann[] value();
+}
+
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Repeatable(AnnCont.class)
+@interface Ann {
+    int value();
+}
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@interface AnnCont {
+    Ann[] value();
+}
+
+
+@Ann(10)
+class A1 {}
+
+@Ann(20)
+class A2 extends A1 {}
+
+@ExpectedAssociated({@Ann(20)})
+class A3 extends A2 {}
+
+
+@Ann(10) @Ann(11)
+class B1 {}
+
+@Ann(20)
+class B2 extends B1 {}
+
+@ExpectedAssociated({@Ann(20)})
+class B3 extends B2 {}
+
+
+@Ann(10)
+class C1 {}
+
+@Ann(20) @Ann(21)
+class C2 extends C1 {}
+
+@ExpectedAssociated({@Ann(20), @Ann(21)})
+class C3 extends C2 {}
+
+
+@Ann(10) @Ann(11)
+class D1 {}
+
+@Ann(20) @Ann(21)
+class D2 extends D1 {}
+
+@ExpectedAssociated({@Ann(20), @Ann(21)})
+class D3 extends D2 {}