|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.reflect.annotation; |
|
|
|
import java.lang.annotation.*; |
|
import java.lang.reflect.*; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Objects; |
|
|
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.access.JavaLangAccess; |
|
import jdk.internal.reflect.ReflectionFactory; |
|
|
|
public final class AnnotationSupport { |
|
private static final JavaLangAccess LANG_ACCESS = SharedSecrets.getJavaLangAccess(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent( |
|
Map<Class<? extends Annotation>, Annotation> annotations, |
|
Class<A> annoClass) { |
|
List<A> result = new ArrayList<>(); |
|
|
|
@SuppressWarnings("unchecked") |
|
A direct = (A) annotations.get(annoClass); |
|
if (direct != null) |
|
result.add(direct); |
|
|
|
A[] indirect = getIndirectlyPresent(annotations, annoClass); |
|
if (indirect != null && indirect.length != 0) { |
|
boolean indirectFirst = direct == null || |
|
containerBeforeContainee(annotations, annoClass); |
|
|
|
result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect)); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
A[] arr = (A[]) Array.newInstance(annoClass, result.size()); |
|
return result.toArray(arr); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static <A extends Annotation> A[] getIndirectlyPresent( |
|
Map<Class<? extends Annotation>, Annotation> annotations, |
|
Class<A> annoClass) { |
|
|
|
Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class); |
|
if (repeatable == null) |
|
return null; |
|
|
|
Class<? extends Annotation> containerClass = repeatable.value(); |
|
|
|
Annotation container = annotations.get(containerClass); |
|
if (container == null) |
|
return null; |
|
|
|
|
|
A[] valueArray = getValueArray(container); |
|
checkTypes(valueArray, container, annoClass); |
|
|
|
return valueArray; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static <A extends Annotation> boolean containerBeforeContainee( |
|
Map<Class<? extends Annotation>, Annotation> annotations, |
|
Class<A> annoClass) { |
|
|
|
Class<? extends Annotation> containerClass = |
|
annoClass.getDeclaredAnnotation(Repeatable.class).value(); |
|
|
|
for (Class<? extends Annotation> c : annotations.keySet()) { |
|
if (c == containerClass) return true; |
|
if (c == annoClass) return false; |
|
} |
|
|
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static <A extends Annotation> A[] getAssociatedAnnotations( |
|
Map<Class<? extends Annotation>, Annotation> declaredAnnotations, |
|
Class<?> decl, |
|
Class<A> annoClass) { |
|
Objects.requireNonNull(decl); |
|
|
|
|
|
A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass); |
|
|
|
|
|
if(AnnotationType.getInstance(annoClass).isInherited()) { |
|
Class<?> superDecl = decl.getSuperclass(); |
|
while (result.length == 0 && superDecl != null) { |
|
result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass); |
|
superDecl = superDecl.getSuperclass(); |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
private static <A extends Annotation> A[] getValueArray(Annotation container) { |
|
try { |
|
// According to JLS the container must have an array-valued value |
|
// method. Get the AnnotationType, get the "value" method and invoke |
|
// it to get the content. |
|
|
|
Class<? extends Annotation> containerClass = container.annotationType(); |
|
AnnotationType annoType = AnnotationType.getInstance(containerClass); |
|
if (annoType == null) |
|
throw invalidContainerException(container, null); |
|
Method m = annoType.members().get("value"); |
|
if (m == null) |
|
throw invalidContainerException(container, null); |
|
|
|
if (Proxy.isProxyClass(container.getClass())) { |
|
|
|
InvocationHandler handler = Proxy.getInvocationHandler(container); |
|
|
|
try { |
|
// This will erase to (Annotation[]) but we do a runtime cast on the |
|
|
|
@SuppressWarnings("unchecked") |
|
A[] values = (A[]) handler.invoke(container, m, null); |
|
return values; |
|
} catch (Throwable t) { |
|
throw invalidContainerException(container, t); |
|
} |
|
} else { |
|
// In theory there might be instances of Annotations that are not |
|
// implemented using Proxies. Try to invoke the "value" element with |
|
// reflection. |
|
|
|
|
|
Class<?> iface = m.getDeclaringClass(); |
|
if (!iface.isAnnotation()) |
|
throw new UnsupportedOperationException("Unsupported container annotation type."); |
|
|
|
if (!Modifier.isPublic(m.getModifiers())) |
|
throw new UnsupportedOperationException("Unsupported value member."); |
|
|
|
|
|
final Method toInvoke; |
|
if (!Modifier.isPublic(iface.getModifiers())) { |
|
if (System.getSecurityManager() != null) { |
|
toInvoke = AccessController.doPrivileged(new PrivilegedAction<Method>() { |
|
@Override |
|
public Method run() { |
|
Method res = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); |
|
res.setAccessible(true); |
|
return res; |
|
} |
|
}); |
|
} else { |
|
toInvoke = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); |
|
toInvoke.setAccessible(true); |
|
} |
|
} else { |
|
toInvoke = m; |
|
} |
|
|
|
// This will erase to (Annotation[]) but we do a runtime cast on the |
|
|
|
@SuppressWarnings("unchecked") |
|
A[] values = (A[]) toInvoke.invoke(container); |
|
|
|
return values; |
|
} |
|
} catch (IllegalAccessException | |
|
IllegalArgumentException | |
|
InvocationTargetException | |
|
ClassCastException e) { |
|
throw invalidContainerException(container, e); |
|
} |
|
} |
|
|
|
|
|
private static AnnotationFormatError invalidContainerException(Annotation anno, |
|
Throwable cause) { |
|
return new AnnotationFormatError( |
|
anno + " is an invalid container for repeating annotations", |
|
cause); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static <A extends Annotation> void checkTypes(A[] annotations, |
|
Annotation container, |
|
Class<A> annoClass) { |
|
for (A a : annotations) { |
|
if (!annoClass.isInstance(a)) { |
|
throw new AnnotationFormatError( |
|
String.format("%s is an invalid container for " + |
|
"repeating annotations of type: %s", |
|
container, annoClass)); |
|
} |
|
} |
|
} |
|
} |