/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.reflect.annotation;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public final class AnnotationSupport {
/**
* Finds and returns all annotation of the type indicated by
* {@code annotationClass} from the {@code Map} {@code
* annotationMap}. Looks into containers of the {@code
* annotationClass} (as specified by an the {@code
* annotationClass} type being meta-annotated with an {@code
* Repeatable} annotation).
*
* @param annotationMap the {@code Map} used to store annotations indexed by their type
* @param annotationClass the type of annotation to search for
*
* @return an array of instances of {@code annotationClass} or an empty array if none were found
*/
public static <A extends Annotation> A[] getMultipleAnnotations(
final Map<Class<? extends Annotation>, Annotation> annotationMap,
final Class<A> annotationClass) {
final List<A> res = new ArrayList<A>();
@SuppressWarnings("unchecked")
final A candidate = (A)annotationMap.get(annotationClass);
if (candidate != null) {
res.add(candidate);
}
final Class<? extends Annotation> containerClass = getContainer(annotationClass);
if (containerClass != null) {
res.addAll(unpackAll(annotationMap.get(containerClass), annotationClass));
}
@SuppressWarnings("unchecked") // should be safe annotationClass is a token for A
final A[] emptyTemplateArray = (A[])Array.newInstance(annotationClass, 0);
return res.isEmpty() ? emptyTemplateArray : res.toArray(emptyTemplateArray);
}
/** Helper to get the container, or null if none, of an annotation. */
private static <A extends Annotation> Class<? extends Annotation> getContainer(Class<A> annotationClass) {
Repeatable containingAnnotation = annotationClass.getDeclaredAnnotation(Repeatable.class);
return (containingAnnotation == null) ? null : containingAnnotation.value();
}
/** Reflectively look up and get the returned array from the the
* invocation of the value() element on an instance of an
* Annotation.
*/
private static <A extends Annotation> A[] getValueArray(Annotation containerInstance) {
try {
// the spec tells us the container must have an array-valued
// value element. Get the AnnotationType, get the "value" element
// and invoke it to get the contents.
Class<? extends Annotation> containerClass = containerInstance.annotationType();
AnnotationType annoType = AnnotationType.getInstance(containerClass);
if (annoType == null)
throw new AnnotationFormatError(containerInstance + " is an invalid container for repeating annotations");
Method m = annoType.members().get("value");
if (m == null)
throw new AnnotationFormatError(containerInstance +
" is an invalid container for repeating annotations");
m.setAccessible(true);
@SuppressWarnings("unchecked") // not provably safe, but we catch the ClassCastException
A[] a = (A[])m.invoke(containerInstance); // this will erase to (Annotation[]) but we
// do a runtime cast on the return-value
// in the methods that call this method
return a;
} catch (IllegalAccessException | // couldnt loosen security
IllegalArgumentException | // parameters doesn't match
InvocationTargetException | // the value method threw an exception
ClassCastException e) { // well, a cast failed ...
throw new AnnotationFormatError(
containerInstance + " is an invalid container for repeating annotations",
e);
}
}
/* Sanity check type of and return a list of all the annotation
* instances of type {@code annotationClass} from {@code
* containerInstance}.
*/
private static <A extends Annotation> List<A> unpackAll(Annotation containerInstance,
Class<A> annotationClass) {
if (containerInstance == null) {
return Collections.emptyList(); // container not present
}
try {
A[] a = getValueArray(containerInstance);
List<A> l = new ArrayList<>(a.length);
for (int i = 0; i < a.length; i++)
l.add(annotationClass.cast(a[i]));
return l;
} catch (ClassCastException |
NullPointerException e) {
throw new AnnotationFormatError(
String.format("%s is an invalid container for repeating annotations of type: %s",
containerInstance, annotationClass),
e);
}
}
}