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
--- a/jdk/src/share/classes/java/lang/Class.java Wed Oct 23 15:37:40 2013 +0400
+++ b/jdk/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/jdk/src/share/classes/java/lang/System.java Wed Oct 23 15:37:40 2013 +0400
+++ b/jdk/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/jdk/src/share/classes/sun/misc/JavaLangAccess.java Wed Oct 23 15:37:40 2013 +0400
+++ b/jdk/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/jdk/src/share/classes/sun/reflect/annotation/AnnotationSupport.java Wed Oct 23 15:37:40 2013 +0400
+++ b/jdk/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);
-
- if (indirect != null) {
+ A[] indirect = getIndirectlyPresent(annotations, annoClass);
+ if (indirect != null && indirect.length != 0) {
+ boolean indirectFirst = direct == null ||
+ containerBeforeContainee(annotations, annoClass);
- 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/jdk/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 {}