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 }