1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.reflect.annotation; 27 28 import java.lang.annotation.*; 29 import java.lang.reflect.*; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Objects; 37 38 import jdk.internal.misc.SharedSecrets; 39 import jdk.internal.misc.JavaLangAccess; 40 import sun.reflect.ReflectionFactory; 41 42 public final class AnnotationSupport { 43 private static final JavaLangAccess LANG_ACCESS = SharedSecrets.getJavaLangAccess(); 44 45 /** 46 * Finds and returns all annotations in {@code annotations} matching 47 * the given {@code annoClass}. 48 * 49 * Apart from annotations directly present in {@code annotations} this 50 * method searches for annotations inside containers i.e. indirectly 51 * present annotations. 52 * 53 * The order of the elements in the array returned depends on the iteration 54 * order of the provided map. Specifically, the directly present annotations 55 * come before the indirectly present annotations if and only if the 56 * directly present annotations come before the indirectly present 57 * annotations in the map. 58 * 59 * @param annotations the {@code Map} in which to search for annotations 60 * @param annoClass the type of annotation to search for 61 * 62 * @return an array of instances of {@code annoClass} or an empty 63 * array if none were found 64 */ 65 public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent( 66 Map<Class<? extends Annotation>, Annotation> annotations, 67 Class<A> annoClass) { 68 List<A> result = new ArrayList<>(); 69 70 @SuppressWarnings("unchecked") 71 A direct = (A) annotations.get(annoClass); 72 if (direct != null) 73 result.add(direct); 74 75 A[] indirect = getIndirectlyPresent(annotations, annoClass); 76 if (indirect != null && indirect.length != 0) { 77 boolean indirectFirst = direct == null || 78 containerBeforeContainee(annotations, annoClass); 79 80 result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect)); 81 } 82 83 @SuppressWarnings("unchecked") 84 A[] arr = (A[]) Array.newInstance(annoClass, result.size()); 85 return result.toArray(arr); 86 } 87 88 /** 89 * Finds and returns all annotations matching the given {@code annoClass} 90 * indirectly present in {@code annotations}. 91 * 92 * @param annotations annotations to search indexed by their types 93 * @param annoClass the type of annotation to search for 94 * 95 * @return an array of instances of {@code annoClass} or an empty array if no 96 * indirectly present annotations were found 97 */ 98 private static <A extends Annotation> A[] getIndirectlyPresent( 99 Map<Class<? extends Annotation>, Annotation> annotations, 100 Class<A> annoClass) { 101 102 Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class); 103 if (repeatable == null) 104 return null; // Not repeatable -> no indirectly present annotations 105 106 Class<? extends Annotation> containerClass = repeatable.value(); 107 108 Annotation container = annotations.get(containerClass); 109 if (container == null) 110 return null; 111 112 // Unpack container 113 A[] valueArray = getValueArray(container); 114 checkTypes(valueArray, container, annoClass); 115 116 return valueArray; 117 } 118 119 120 /** 121 * Figures out if container class comes before containee class among the 122 * keys of the given map. 123 * 124 * @return true if container class is found before containee class when 125 * iterating over annotations.keySet(). 126 */ 127 private static <A extends Annotation> boolean containerBeforeContainee( 128 Map<Class<? extends Annotation>, Annotation> annotations, 129 Class<A> annoClass) { 130 131 Class<? extends Annotation> containerClass = 132 annoClass.getDeclaredAnnotation(Repeatable.class).value(); 133 134 for (Class<? extends Annotation> c : annotations.keySet()) { 135 if (c == containerClass) return true; 136 if (c == annoClass) return false; 137 } 138 139 // Neither containee nor container present 140 return false; 141 } 142 143 144 /** 145 * Finds and returns all associated annotations matching the given class. 146 * 147 * The order of the elements in the array returned depends on the iteration 148 * order of the provided maps. Specifically, the directly present annotations 149 * come before the indirectly present annotations if and only if the 150 * directly present annotations come before the indirectly present 151 * annotations in the relevant map. 152 * 153 * @param declaredAnnotations the declared annotations indexed by their types 154 * @param decl the class declaration on which to search for annotations 155 * @param annoClass the type of annotation to search for 156 * 157 * @return an array of instances of {@code annoClass} or an empty array if none were found. 158 */ 159 public static <A extends Annotation> A[] getAssociatedAnnotations( 160 Map<Class<? extends Annotation>, Annotation> declaredAnnotations, 161 Class<?> decl, 162 Class<A> annoClass) { 163 Objects.requireNonNull(decl); 164 165 // Search declared 166 A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass); 167 168 // Search inherited 169 if(AnnotationType.getInstance(annoClass).isInherited()) { 170 Class<?> superDecl = decl.getSuperclass(); 171 while (result.length == 0 && superDecl != null) { 172 result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass); 173 superDecl = superDecl.getSuperclass(); 174 } 175 } 176 177 return result; 178 } 179 180 181 /* Reflectively invoke the values-method of the given annotation 182 * (container), cast it to an array of annotations and return the result. 183 */ 184 private static <A extends Annotation> A[] getValueArray(Annotation container) { 185 try { 186 // According to JLS the container must have an array-valued value 187 // method. Get the AnnotationType, get the "value" method and invoke 188 // it to get the content. 189 190 Class<? extends Annotation> containerClass = container.annotationType(); 191 AnnotationType annoType = AnnotationType.getInstance(containerClass); 192 if (annoType == null) 193 throw invalidContainerException(container, null); 194 Method m = annoType.members().get("value"); 195 if (m == null) 196 throw invalidContainerException(container, null); 197 198 if (Proxy.isProxyClass(container.getClass())) { 199 // Invoke by invocation handler 200 InvocationHandler handler = Proxy.getInvocationHandler(container); 201 202 try { 203 // This will erase to (Annotation[]) but we do a runtime cast on the 204 // return-value in the method that call this method. 205 @SuppressWarnings("unchecked") 206 A[] values = (A[]) handler.invoke(container, m, null); 207 return values; 208 } catch (Throwable t) { // from InvocationHandler::invoke 209 throw invalidContainerException(container, t); 210 } 211 } else { 212 // In theory there might be instances of Annotations that are not 213 // implemented using Proxies. Try to invoke the "value" element with 214 // reflection. 215 216 // Declaring class should be an annotation type 217 Class<?> iface = m.getDeclaringClass(); 218 if (!iface.isAnnotation()) 219 throw new UnsupportedOperationException("Unsupported container annotation type."); 220 // Method must be public 221 if (!Modifier.isPublic(m.getModifiers())) 222 throw new UnsupportedOperationException("Unsupported value member."); 223 224 // Interface might not be public though 225 final Method toInvoke; 226 if (!Modifier.isPublic(iface.getModifiers())) { 227 if (System.getSecurityManager() != null) { 228 toInvoke = AccessController.doPrivileged(new PrivilegedAction<Method>() { 229 @Override 230 public Method run() { 231 Method res = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); 232 res.setAccessible(true); 233 return res; 234 } 235 }); 236 } else { 237 toInvoke = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); 238 toInvoke.setAccessible(true); 239 } 240 } else { 241 toInvoke = m; 242 } 243 244 // This will erase to (Annotation[]) but we do a runtime cast on the 245 // return-value in the method that call this method. 246 @SuppressWarnings("unchecked") 247 A[] values = (A[]) toInvoke.invoke(container); 248 249 return values; 250 } 251 } catch (IllegalAccessException | // couldn't loosen security 252 IllegalArgumentException | // parameters doesn't match 253 InvocationTargetException | // the value method threw an exception 254 ClassCastException e) { 255 throw invalidContainerException(container, e); 256 } 257 } 258 259 260 private static AnnotationFormatError invalidContainerException(Annotation anno, 261 Throwable cause) { 262 return new AnnotationFormatError( 263 anno + " is an invalid container for repeating annotations", 264 cause); 265 } 266 267 268 /* Sanity check type of all the annotation instances of type {@code annoClass} 269 * from {@code container}. 270 */ 271 private static <A extends Annotation> void checkTypes(A[] annotations, 272 Annotation container, 273 Class<A> annoClass) { 274 for (A a : annotations) { 275 if (!annoClass.isInstance(a)) { 276 throw new AnnotationFormatError( 277 String.format("%s is an invalid container for " + 278 "repeating annotations of type: %s", 279 container, annoClass)); 280 } 281 } 282 } 283 }