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.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 import java.util.Map; 34 35 public final class AnnotationSupport { 36 /** 37 * Finds and returns all annotation of the type indicated by 38 * {@code annotationClass} from the {@code Map} {@code 39 * annotationMap}. Looks into containers of the {@code 40 * annotationClass} (as specified by an the {@code 41 * annotationClass} type being meta-annotated with an {@code 42 * Repeatable} annotation). 43 * 44 * @param annotationMap the {@code Map} used to store annotations indexed by their type 45 * @param annotationClass the type of annotation to search for 46 * 47 * @return an array of instances of {@code annotationClass} or an empty array if none were found 48 */ 49 public static <A extends Annotation> A[] getMultipleAnnotations( 50 final Map<Class<? extends Annotation>, Annotation> annotationMap, 51 final Class<A> annotationClass) { 52 final List<A> res = new ArrayList<A>(); 53 54 @SuppressWarnings("unchecked") 55 final A candidate = (A)annotationMap.get(annotationClass); 56 if (candidate != null) { 57 res.add(candidate); 58 } 59 60 final Class<? extends Annotation> containerClass = getContainer(annotationClass); 61 if (containerClass != null) { 62 res.addAll(unpackAll(annotationMap.get(containerClass), annotationClass)); 63 } 64 65 @SuppressWarnings("unchecked") // should be safe annotationClass is a token for A 66 final A[] emptyTemplateArray = (A[])Array.newInstance(annotationClass, 0); 67 return res.isEmpty() ? emptyTemplateArray : res.toArray(emptyTemplateArray); 68 } 69 70 /** Helper to get the container, or null if none, of an annotation. */ 71 private static <A extends Annotation> Class<? extends Annotation> getContainer(Class<A> annotationClass) { 72 Repeatable containingAnnotation = annotationClass.getDeclaredAnnotation(Repeatable.class); 73 return (containingAnnotation == null) ? null : containingAnnotation.value(); 74 } 75 76 /** Reflectively look up and get the returned array from the the 77 * invocation of the value() element on an instance of an 78 * Annotation. 79 */ 80 private static <A extends Annotation> A[] getValueArray(Annotation containerInstance) { 81 try { 82 // the spec tells us the container must have an array-valued 83 // value element. Get the AnnotationType, get the "value" element 84 // and invoke it to get the contents. 85 86 Class<? extends Annotation> containerClass = containerInstance.annotationType(); 87 AnnotationType annoType = AnnotationType.getInstance(containerClass); 88 if (annoType == null) 89 throw new InvalidContainerAnnotationError(containerInstance + " is an invalid container for repeating annotations"); 90 91 Method m = annoType.members().get("value"); 92 if (m == null) 93 throw new InvalidContainerAnnotationError(containerInstance + 94 " is an invalid container for repeating annotations"); 95 m.setAccessible(true); 96 97 @SuppressWarnings("unchecked") // not provably safe, but we catch the ClassCastException 98 A[] a = (A[])m.invoke(containerInstance); // this will erase to (Annotation[]) but we 99 // do a runtime cast on the return-value 100 // in the methods that call this method 101 return a; 102 } catch (IllegalAccessException | // couldnt loosen security 103 IllegalArgumentException | // parameters doesn't match 104 InvocationTargetException | // the value method threw an exception 105 ClassCastException e) { // well, a cast failed ... 106 throw new InvalidContainerAnnotationError( 107 containerInstance + " is an invalid container for repeating annotations", 108 e, 109 containerInstance, 110 null); 111 } 112 } 113 114 /* Sanity check type of and return a list of all the annotation 115 * instances of type {@code annotationClass} from {@code 116 * containerInstance}. 117 */ 118 private static <A extends Annotation> List<A> unpackAll(Annotation containerInstance, 119 Class<A> annotationClass) { 120 if (containerInstance == null) { 121 return Collections.emptyList(); // container not present 122 } 123 124 try { 125 A[] a = getValueArray(containerInstance); 126 List<A> l = new ArrayList<>(a.length); 127 for (int i = 0; i < a.length; i++) 128 l.add(annotationClass.cast(a[i])); 129 return l; 130 } catch (ClassCastException | 131 NullPointerException e) { 132 throw new InvalidContainerAnnotationError( 133 String.format("%s is an invalid container for repeating annotations of type: %s", 134 containerInstance, annotationClass), 135 e, 136 containerInstance, 137 annotationClass); 138 } 139 } 140 }