Path: blob/master/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java
41159 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.reflect.annotation;2627import java.lang.annotation.*;28import java.lang.reflect.*;29import java.security.AccessController;30import java.security.PrivilegedAction;31import java.util.ArrayList;32import java.util.Arrays;33import java.util.List;34import java.util.Map;35import java.util.Objects;3637import jdk.internal.access.SharedSecrets;38import jdk.internal.access.JavaLangAccess;39import jdk.internal.reflect.ReflectionFactory;4041public final class AnnotationSupport {42private static final JavaLangAccess LANG_ACCESS = SharedSecrets.getJavaLangAccess();4344/**45* Finds and returns all annotations in {@code annotations} matching46* the given {@code annoClass}.47*48* Apart from annotations directly present in {@code annotations} this49* method searches for annotations inside containers i.e. indirectly50* present annotations.51*52* The order of the elements in the array returned depends on the iteration53* order of the provided map. Specifically, the directly present annotations54* come before the indirectly present annotations if and only if the55* directly present annotations come before the indirectly present56* annotations in the map.57*58* @param annotations the {@code Map} in which to search for annotations59* @param annoClass the type of annotation to search for60*61* @return an array of instances of {@code annoClass} or an empty62* array if none were found63*/64public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent(65Map<Class<? extends Annotation>, Annotation> annotations,66Class<A> annoClass) {67List<A> result = new ArrayList<>();6869@SuppressWarnings("unchecked")70A direct = (A) annotations.get(annoClass);71if (direct != null)72result.add(direct);7374A[] indirect = getIndirectlyPresent(annotations, annoClass);75if (indirect != null && indirect.length != 0) {76boolean indirectFirst = direct == null ||77containerBeforeContainee(annotations, annoClass);7879result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect));80}8182@SuppressWarnings("unchecked")83A[] arr = (A[]) Array.newInstance(annoClass, result.size());84return result.toArray(arr);85}8687/**88* Finds and returns all annotations matching the given {@code annoClass}89* indirectly present in {@code annotations}.90*91* @param annotations annotations to search indexed by their types92* @param annoClass the type of annotation to search for93*94* @return an array of instances of {@code annoClass} or an empty array if no95* indirectly present annotations were found96*/97private static <A extends Annotation> A[] getIndirectlyPresent(98Map<Class<? extends Annotation>, Annotation> annotations,99Class<A> annoClass) {100101Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class);102if (repeatable == null)103return null; // Not repeatable -> no indirectly present annotations104105Class<? extends Annotation> containerClass = repeatable.value();106107Annotation container = annotations.get(containerClass);108if (container == null)109return null;110111// Unpack container112A[] valueArray = getValueArray(container);113checkTypes(valueArray, container, annoClass);114115return valueArray;116}117118119/**120* Figures out if container class comes before containee class among the121* keys of the given map.122*123* @return true if container class is found before containee class when124* iterating over annotations.keySet().125*/126private static <A extends Annotation> boolean containerBeforeContainee(127Map<Class<? extends Annotation>, Annotation> annotations,128Class<A> annoClass) {129130Class<? extends Annotation> containerClass =131annoClass.getDeclaredAnnotation(Repeatable.class).value();132133for (Class<? extends Annotation> c : annotations.keySet()) {134if (c == containerClass) return true;135if (c == annoClass) return false;136}137138// Neither containee nor container present139return false;140}141142143/**144* Finds and returns all associated annotations matching the given class.145*146* The order of the elements in the array returned depends on the iteration147* order of the provided maps. Specifically, the directly present annotations148* come before the indirectly present annotations if and only if the149* directly present annotations come before the indirectly present150* annotations in the relevant map.151*152* @param declaredAnnotations the declared annotations indexed by their types153* @param decl the class declaration on which to search for annotations154* @param annoClass the type of annotation to search for155*156* @return an array of instances of {@code annoClass} or an empty array if none were found.157*/158public static <A extends Annotation> A[] getAssociatedAnnotations(159Map<Class<? extends Annotation>, Annotation> declaredAnnotations,160Class<?> decl,161Class<A> annoClass) {162Objects.requireNonNull(decl);163164// Search declared165A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass);166167// Search inherited168if(AnnotationType.getInstance(annoClass).isInherited()) {169Class<?> superDecl = decl.getSuperclass();170while (result.length == 0 && superDecl != null) {171result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass);172superDecl = superDecl.getSuperclass();173}174}175176return result;177}178179180/* Reflectively invoke the values-method of the given annotation181* (container), cast it to an array of annotations and return the result.182*/183@SuppressWarnings("removal")184private static <A extends Annotation> A[] getValueArray(Annotation container) {185try {186// According to JLS the container must have an array-valued value187// method. Get the AnnotationType, get the "value" method and invoke188// it to get the content.189190Class<? extends Annotation> containerClass = container.annotationType();191AnnotationType annoType = AnnotationType.getInstance(containerClass);192if (annoType == null)193throw invalidContainerException(container, null);194Method m = annoType.members().get("value");195if (m == null)196throw invalidContainerException(container, null);197198if (Proxy.isProxyClass(container.getClass())) {199// Invoke by invocation handler200InvocationHandler handler = Proxy.getInvocationHandler(container);201202try {203// This will erase to (Annotation[]) but we do a runtime cast on the204// return-value in the method that call this method.205@SuppressWarnings("unchecked")206A[] values = (A[]) handler.invoke(container, m, null);207return values;208} catch (Throwable t) { // from InvocationHandler::invoke209throw invalidContainerException(container, t);210}211} else {212// In theory there might be instances of Annotations that are not213// implemented using Proxies. Try to invoke the "value" element with214// reflection.215216// Declaring class should be an annotation type217Class<?> iface = m.getDeclaringClass();218if (!iface.isAnnotation())219throw new UnsupportedOperationException("Unsupported container annotation type.");220// Method must be public221if (!Modifier.isPublic(m.getModifiers()))222throw new UnsupportedOperationException("Unsupported value member.");223224// Interface might not be public though225final Method toInvoke;226if (!Modifier.isPublic(iface.getModifiers())) {227if (System.getSecurityManager() != null) {228toInvoke = AccessController.doPrivileged(new PrivilegedAction<Method>() {229@Override230public Method run() {231Method res = ReflectionFactory.getReflectionFactory().leafCopyMethod(m);232res.setAccessible(true);233return res;234}235});236} else {237toInvoke = ReflectionFactory.getReflectionFactory().leafCopyMethod(m);238toInvoke.setAccessible(true);239}240} else {241toInvoke = m;242}243244// This will erase to (Annotation[]) but we do a runtime cast on the245// return-value in the method that call this method.246@SuppressWarnings("unchecked")247A[] values = (A[]) toInvoke.invoke(container);248249return values;250}251} catch (IllegalAccessException | // couldn't loosen security252IllegalArgumentException | // parameters doesn't match253InvocationTargetException | // the value method threw an exception254ClassCastException e) {255throw invalidContainerException(container, e);256}257}258259260private static AnnotationFormatError invalidContainerException(Annotation anno,261Throwable cause) {262return new AnnotationFormatError(263anno + " is an invalid container for repeating annotations",264cause);265}266267268/* Sanity check type of all the annotation instances of type {@code annoClass}269* from {@code container}.270*/271private static <A extends Annotation> void checkTypes(A[] annotations,272Annotation container,273Class<A> annoClass) {274for (A a : annotations) {275if (!annoClass.isInstance(a)) {276throw new AnnotationFormatError(277String.format("%s is an invalid container for " +278"repeating annotations of type: %s",279container, annoClass));280}281}282}283}284285286