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 import java.util.function.IntFunction; 38 39 import jdk.internal.misc.SharedSecrets; 40 import jdk.internal.misc.JavaLangAccess; 41 import jdk.internal.reflect.ReflectionFactory; 42 43 public final class AnnotationSupport { 44 private static final JavaLangAccess LANG_ACCESS = SharedSecrets.getJavaLangAccess(); 45 46 /** 47 * Finds and returns all annotations in {@code annotations} matching 48 * the given {@code annoClass}. 49 * 50 * Apart from annotations directly present in {@code annotations} this 51 * method searches for annotations inside containers i.e. indirectly 52 * present annotations. 53 * 54 * The order of the elements in the array returned depends on the iteration 55 * order of the provided map. Specifically, the directly present annotations 56 * come before the indirectly present annotations if and only if the 57 * directly present annotations come before the indirectly present 58 * annotations in the map. 59 * 60 * @param annotations the {@code Map} in which to search for annotations 61 * @param annoClass the type of annotation to search for 62 * 63 * @return an array of instances of {@code annoClass} or an empty 64 * array if none were found 65 */ 66 public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent( 67 Map<Class<? extends Annotation>, Annotation> annotations, 68 Class<A> annoClass) { 69 List<A> result = new ArrayList<>(); 70 71 @SuppressWarnings("unchecked") 72 A direct = (A) annotations.get(annoClass); 73 if (direct != null) 74 result.add(direct); 75 76 A[] indirect = getIndirectlyPresent(annotations, annoClass); 77 if (indirect != null && indirect.length != 0) { 78 boolean indirectFirst = direct == null || 79 containerBeforeContainee(annotations, annoClass); 80 81 result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect)); 82 } 83 84 @SuppressWarnings("unchecked") 85 A[] arr = (A[]) Array.newInstance(annoClass, result.size()); 86 return result.toArray(arr); 87 } 88 89 /** 90 * Finds and returns all annotations matching the given {@code annoClass} 91 * indirectly present in {@code annotations}. 92 * 93 * @param annotations annotations to search indexed by their types 94 * @param annoClass the type of annotation to search for 95 * 96 * @return an array of instances of {@code annoClass} or an empty array if no 97 * indirectly present annotations were found 98 */ 99 private static <A extends Annotation> A[] getIndirectlyPresent( 100 Map<Class<? extends Annotation>, Annotation> annotations, 101 Class<A> annoClass) { 102 103 Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class); 104 if (repeatable == null) 105 return null; // Not repeatable -> no indirectly present annotations 106 107 Class<? extends Annotation> containerClass = repeatable.value(); 108 109 Annotation container = annotations.get(containerClass); 110 if (container == null) 111 return null; 112 113 // Unpack container 114 A[] valueArray = getValueArray(container); 115 checkTypes(valueArray, container, annoClass); 116 117 return valueArray; 118 } 119 120 121 /** 122 * Figures out if container class comes before containee class among the 123 * keys of the given map. 124 * 125 * @return true if container class is found before containee class when 126 * iterating over annotations.keySet(). 127 */ 128 private static <A extends Annotation> boolean containerBeforeContainee( 129 Map<Class<? extends Annotation>, Annotation> annotations, 130 Class<A> annoClass) { 131 132 Class<? extends Annotation> containerClass = 133 annoClass.getDeclaredAnnotation(Repeatable.class).value(); 134 135 for (Class<? extends Annotation> c : annotations.keySet()) { 136 if (c == containerClass) return true; 137 if (c == annoClass) return false; 138 } 139 140 // Neither containee nor container present 141 return false; 142 } 143 144 145 /** 146 * Finds and returns all associated annotations matching the given class. 147 * 148 * The order of the elements in the array returned depends on the iteration 149 * order of the provided maps. Specifically, the directly present annotations 150 * come before the indirectly present annotations if and only if the 151 * directly present annotations come before the indirectly present 152 * annotations in the relevant map. 153 * 154 * @param declaredAnnotations the declared annotations indexed by their types 155 * @param decl the class declaration on which to search for annotations 156 * @param annoClass the type of annotation to search for 157 * 158 * @return an array of instances of {@code annoClass} or an empty array if none were found. 159 */ 160 public static <A extends Annotation> A[] getAssociatedAnnotations( 161 Map<Class<? extends Annotation>, Annotation> declaredAnnotations, 162 Class<?> decl, 163 Class<A> annoClass) { 164 Objects.requireNonNull(decl); 165 166 // Search declared 167 A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass); 168 169 // Search inherited 170 if(AnnotationType.getInstance(annoClass).isInherited()) { 171 Class<?> superDecl = decl.getSuperclass(); 172 while (result.length == 0 && superDecl != null) { 173 result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass); 174 superDecl = superDecl.getSuperclass(); 175 } 176 } 177 178 return result; 179 } 180 181 182 /** 183 * There are cases where the number of parameters present for a constructor 184 * in source code and the number of parameters present for that constructor 185 * in the class file differ. Constructors of enum classes, anonymous classes, 186 * local classes and member classes may have additional parameters generated 187 * by compiler. This method fixes an array of "annotations collections" 188 * by prefixing and/or suffixing it with "empty collection" elements 189 * for parameters added by compiler. 190 * 191 * @param constructor the constructor 192 * @param annColls an array of annotation collections for parameters in source code 193 * @param emptyColl an empty collection replicated for compiler generated parameters 194 * @param arrayFactory a factory for arrays of collections 195 * @param <A> the type of annotation collection 196 * @return new array of annotation collections for parameters in class file 197 */ 198 public static <A> A[] fixConstructorParameterAnnotations( 199 Constructor<?> constructor, A[] annColls, A emptyColl, IntFunction<A[]> arrayFactory) 200 { 201 int prefixLength = 0; 202 boolean mayHaveSuffix = false; 203 Class<?> declaringClass = constructor.getDeclaringClass(); 204 if (declaringClass.isEnum() || 205 (declaringClass.isAnonymousClass() && declaringClass.getSuperclass().isEnum())) { 206 // enum class or enum constant anonymous subclass 207 prefixLength = 2; 208 mayHaveSuffix = true; 209 } else if (declaringClass.isLocalClass() || declaringClass.isAnonymousClass()) { 210 // local or anonymous class 211 if (!Modifier.isStatic(declaringClass.getModifiers())) { 212 prefixLength = 1; 213 } 214 mayHaveSuffix = true; 215 } else if (declaringClass.isMemberClass()) { 216 // member class 217 if (!Modifier.isStatic(declaringClass.getModifiers())) { 218 prefixLength = 1; 219 } 220 } 221 // compute total length of array of arrays 222 int len = annColls.length + prefixLength; 223 if (len < constructor.getParameterCount() && mayHaveSuffix) { 224 len = constructor.getParameterCount(); 225 } 226 // prepend empty annotations prefix and/or append suffix if needed 227 if (len > annColls.length) { 228 A[] newAnnColls = arrayFactory.apply(len); 229 Arrays.fill(newAnnColls, 0, prefixLength, emptyColl); 230 System.arraycopy(annColls, 0, 231 newAnnColls, prefixLength, 232 annColls.length); 233 Arrays.fill(newAnnColls, 234 prefixLength + annColls.length, newAnnColls.length, 235 emptyColl); 236 annColls = newAnnColls; 237 } 238 return annColls; 239 } 240 241 /* Reflectively invoke the values-method of the given annotation 242 * (container), cast it to an array of annotations and return the result. 243 */ 244 private static <A extends Annotation> A[] getValueArray(Annotation container) { 245 try { 246 // According to JLS the container must have an array-valued value 247 // method. Get the AnnotationType, get the "value" method and invoke 248 // it to get the content. 249 250 Class<? extends Annotation> containerClass = container.annotationType(); 251 AnnotationType annoType = AnnotationType.getInstance(containerClass); 252 if (annoType == null) 253 throw invalidContainerException(container, null); 254 Method m = annoType.members().get("value"); 255 if (m == null) 256 throw invalidContainerException(container, null); 257 258 if (Proxy.isProxyClass(container.getClass())) { 259 // Invoke by invocation handler 260 InvocationHandler handler = Proxy.getInvocationHandler(container); 261 262 try { 263 // This will erase to (Annotation[]) but we do a runtime cast on the 264 // return-value in the method that call this method. 265 @SuppressWarnings("unchecked") 266 A[] values = (A[]) handler.invoke(container, m, null); 267 return values; 268 } catch (Throwable t) { // from InvocationHandler::invoke 269 throw invalidContainerException(container, t); 270 } 271 } else { 272 // In theory there might be instances of Annotations that are not 273 // implemented using Proxies. Try to invoke the "value" element with 274 // reflection. 275 276 // Declaring class should be an annotation type 277 Class<?> iface = m.getDeclaringClass(); 278 if (!iface.isAnnotation()) 279 throw new UnsupportedOperationException("Unsupported container annotation type."); 280 // Method must be public 281 if (!Modifier.isPublic(m.getModifiers())) 282 throw new UnsupportedOperationException("Unsupported value member."); 283 284 // Interface might not be public though 285 final Method toInvoke; 286 if (!Modifier.isPublic(iface.getModifiers())) { 287 if (System.getSecurityManager() != null) { 288 toInvoke = AccessController.doPrivileged(new PrivilegedAction<Method>() { 289 @Override 290 public Method run() { 291 Method res = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); 292 res.setAccessible(true); 293 return res; 294 } 295 }); 296 } else { 297 toInvoke = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); 298 toInvoke.setAccessible(true); 299 } 300 } else { 301 toInvoke = m; 302 } 303 304 // This will erase to (Annotation[]) but we do a runtime cast on the 305 // return-value in the method that call this method. 306 @SuppressWarnings("unchecked") 307 A[] values = (A[]) toInvoke.invoke(container); 308 309 return values; 310 } 311 } catch (IllegalAccessException | // couldn't loosen security 312 IllegalArgumentException | // parameters doesn't match 313 InvocationTargetException | // the value method threw an exception 314 ClassCastException e) { 315 throw invalidContainerException(container, e); 316 } 317 } 318 319 320 private static AnnotationFormatError invalidContainerException(Annotation anno, 321 Throwable cause) { 322 return new AnnotationFormatError( 323 anno + " is an invalid container for repeating annotations", 324 cause); 325 } 326 327 328 /* Sanity check type of all the annotation instances of type {@code annoClass} 329 * from {@code container}. 330 */ 331 private static <A extends Annotation> void checkTypes(A[] annotations, 332 Annotation container, 333 Class<A> annoClass) { 334 for (A a : annotations) { 335 if (!annoClass.isInstance(a)) { 336 throw new AnnotationFormatError( 337 String.format("%s is an invalid container for " + 338 "repeating annotations of type: %s", 339 container, annoClass)); 340 } 341 } 342 } 343 }