diff --git a/src/share/classes/java/lang/Class.java b/src/share/classes/java/lang/Class.java index e11ee1e65e8bd1e240ac0b59cab954f26d143b23..45c00913f6cdebcfbc8f2d3703c5cd9a194114f2 100644 --- a/src/share/classes/java/lang/Class.java +++ b/src/share/classes/java/lang/Class.java @@ -3316,7 +3316,7 @@ public final class Class implements java.io.Serializable, AnnotationData annotationData = annotationData(); return AnnotationSupport.getAssociatedAnnotations(annotationData.declaredAnnotations, - annotationData.annotations, + this, annotationClass); } @@ -3442,6 +3442,10 @@ public final class Class implements java.io.Serializable, return annotationType; } + Map, Annotation> getDeclaredAnnotationMap() { + return annotationData().declaredAnnotations; + } + /* Backing store of user-defined values pertaining to this class. * Maintained by the ClassValue class. */ diff --git a/src/share/classes/java/lang/System.java b/src/share/classes/java/lang/System.java index c5b5e9e890bc966d31cedb050a2a9d95d6eab218..f68a5401704324d5e58cfcf9c83994f599890305 100644 --- a/src/share/classes/java/lang/System.java +++ b/src/share/classes/java/lang/System.java @@ -26,10 +26,12 @@ package java.lang; 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 final class System { public AnnotationType getAnnotationType(Class klass) { return klass.getAnnotationType(); } + public Map, Annotation> getDeclaredAnnotationMap(Class klass) { + return klass.getDeclaredAnnotationMap(); + } public byte[] getRawClassAnnotations(Class klass) { return klass.getRawAnnotations(); } diff --git a/src/share/classes/sun/misc/JavaLangAccess.java b/src/share/classes/sun/misc/JavaLangAccess.java index fa844d4e5efc96b18667fab67255992b0c9b96cf..77126470586a9d4985fd032221291e279dffd040 100644 --- a/src/share/classes/sun/misc/JavaLangAccess.java +++ b/src/share/classes/sun/misc/JavaLangAccess.java @@ -28,6 +28,7 @@ package sun.misc; 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; @@ -49,6 +50,11 @@ public interface JavaLangAccess { */ AnnotationType getAnnotationType(Class klass); + /** + * Get the declared annotations for a given class, indexed by their types. + */ + Map, Annotation> getDeclaredAnnotationMap(Class klass); + /** * Get the array of bytes that is the class-file representation * of this Class' annotations. diff --git a/src/share/classes/sun/reflect/annotation/AnnotationSupport.java b/src/share/classes/sun/reflect/annotation/AnnotationSupport.java index 4bb33d6495febc9db77f4fa8ef82017abae41c9a..604e19574f991e6eef530d9c64f6612389c2da59 100644 --- a/src/share/classes/sun/reflect/annotation/AnnotationSupport.java +++ b/src/share/classes/sun/reflect/annotation/AnnotationSupport.java @@ -32,8 +32,12 @@ import java.util.Arrays; 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 @@ public final class AnnotationSupport { * * @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[] getDirectlyAndIndirectlyPresent( + public static A[] getDirectlyAndIndirectlyPresent( Map, Annotation> annotations, - Class annoClass, - boolean includeNonInheritedContainees) { - + Class annoClass) { List result = new ArrayList(); @SuppressWarnings("unchecked") @@ -69,17 +69,12 @@ public final class AnnotationSupport { 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 @@ public final class AnnotationSupport { return result.toArray(arr); } - - /** - * Equivalent to calling {@code getDirectlyAndIndirectlyPresentAnnotations( - * annotations, annoClass, true)}. - */ - public static A[] getDirectlyAndIndirectlyPresent( - Map, Annotation> annotations, - Class 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 @@ public final class AnnotationSupport { * 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[] getAssociatedAnnotations( Map, Annotation> declaredAnnotations, - Map, Annotation> allAnnotations, + Class decl, Class 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; } diff --git a/test/java/lang/annotation/repeatingAnnotations/InheritedAssociatedAnnotations.java b/test/java/lang/annotation/repeatingAnnotations/InheritedAssociatedAnnotations.java new file mode 100644 index 0000000000000000000000000000000000000000..5b190c0eca6424a783ee0323877b9b87d1f53d2a --- /dev/null +++ b/test/java/lang/annotation/repeatingAnnotations/InheritedAssociatedAnnotations.java @@ -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 {}