< prev index next >
src/java.base/share/classes/sun/reflect/annotation/AnnotationType.java
Print this page
@@ -30,10 +30,11 @@
import java.util.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import jdk.internal.misc.SharedSecrets;
import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.reflect.ReflectionFactory;
/**
* Represents an annotation type at run time. Used to type-check annotations
* and apply member defaults.
*
@@ -71,11 +72,11 @@
private final boolean inherited;
/**
* Returns an AnnotationType instance for the specified annotation type.
*
- * @throws IllegalArgumentException if the specified class object
+ * @throws AnnotationFormatError if the specified class object
* does not represent a valid annotation type
*/
public static AnnotationType getInstance(
Class<? extends Annotation> annotationClass)
{
@@ -96,41 +97,40 @@
/**
* Sole constructor.
*
* @param annotationClass the class object for the annotation type
- * @throws IllegalArgumentException if the specified class object for
+ * @throws AnnotationFormatError if the specified class object
* does not represent a valid annotation type
*/
private AnnotationType(final Class<? extends Annotation> annotationClass) {
- if (!annotationClass.isAnnotation())
- throw new IllegalArgumentException("Not an annotation type");
+ Class<?>[] superInterfaces = annotationClass.getInterfaces();
+ if (!annotationClass.isAnnotation() ||
+ superInterfaces.length != 1 ||
+ superInterfaces[0] != Annotation.class) {
+ throw new AnnotationFormatError("Not an annotation type.");
+ }
Method[] methods =
AccessController.doPrivileged(new PrivilegedAction<>() {
public Method[] run() {
- // Initialize memberTypes and defaultValues
return annotationClass.getDeclaredMethods();
}
});
memberTypes = new HashMap<>(methods.length+1, 1.0f);
memberDefaults = new HashMap<>(0);
members = new HashMap<>(methods.length+1, 1.0f);
for (Method method : methods) {
- if (Modifier.isPublic(method.getModifiers()) &&
- Modifier.isAbstract(method.getModifiers()) &&
- !method.isSynthetic()) {
- if (method.getParameterTypes().length != 0) {
- throw new IllegalArgumentException(method + " has params");
- }
+ // skip synthetic methods as would be created for lambdas for example
+ if (!method.isSynthetic()) {
+ validateAnnotationMethod(method);
String name = method.getName();
Class<?> type = method.getReturnType();
memberTypes.put(name, invocationHandlerReturnType(type));
members.put(name, method);
-
Object defaultValue = method.getDefaultValue();
if (defaultValue != null) {
memberDefaults.put(name, defaultValue);
}
}
@@ -201,10 +201,38 @@
*/
public Map<String, Method> members() {
return members;
}
+ private volatile Map<String, Method> accessibleMembers;
+
+ /**
+ * Returns members of this annotation type
+ * (member name {@literal ->} associated Method object mapping)
+ * which are made {@link Method#setAccessible(boolean) accessible}.
+ */
+ Map<String, Method> accessibleMembers() {
+ Map<String, Method> accMembers = accessibleMembers;
+ if (accMembers == null) {
+ accMembers = AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public Map<String, Method> run() {
+ Map<String, Method> ams = new HashMap<>(members.size()+1, 1.0f);
+ ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
+ for (Method m : members.values()) {
+ Method am = rf.leafCopyMethod(m);
+ am.setAccessible(true);
+ ams.put(am.getName(), am);
+ }
+ return ams;
+ }
+ });
+ accessibleMembers = accMembers;
+ }
+ return accMembers;
+ }
+
/**
* Returns the default values for this annotation type
* (Member name {@literal ->} default value mapping).
*/
public Map<String, Object> memberDefaults() {
@@ -233,6 +261,97 @@
" Member types: " + memberTypes + "\n" +
" Member defaults: " + memberDefaults + "\n" +
" Retention policy: " + retention + "\n" +
" Inherited: " + inherited;
}
+
+ /**
+ * Validates that specified method is structurally appropriate for an
+ * annotation type. As of Java SE 8, annotation types cannot
+ * contain static methods and the declared methods of an
+ * annotation type must take zero arguments and there are
+ * restrictions on the return type.
+ * @throws AnnotationFormatError if specified method is
+ * inappropriate method for an annotation type.
+ */
+ private static void validateAnnotationMethod(Method method) {
+ /*
+ * Specification citations below are from JLS
+ * 9.6.1. Annotation Type Elements
+ */
+ boolean valid = true;
+ block: {
+ /*
+ * "By virtue of the AnnotationTypeElementDeclaration
+ * production, a method declaration in an annotation type
+ * declaration cannot have formal parameters, type
+ * parameters, or a throws clause.
+ *
+ * "By virtue of the AnnotationTypeElementModifier
+ * production, a method declaration in an annotation type
+ * declaration cannot be default or static."
+ */
+ if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||
+ method.isDefault() ||
+ method.getParameterCount() != 0 ||
+ method.getExceptionTypes().length != 0) {
+ valid = false;
+ break block;
+ }
+
+ /*
+ * "It is a compile-time error if the return type of a
+ * method declared in an annotation type is not one of the
+ * following: a primitive type, String, Class, any
+ * parameterized invocation of Class, an enum type
+ * (section 8.9), an annotation type, or an array type
+ * (chapter 10) whose element type is one of the preceding
+ * types."
+ */
+ Class<?> returnType = method.getReturnType();
+ if (returnType.isArray()) {
+ returnType = returnType.getComponentType();
+ if (returnType.isArray()) { // Only single dimensional arrays
+ valid = false;
+ break block;
+ }
+ }
+
+ if (!((returnType.isPrimitive() && returnType != void.class) ||
+ returnType == java.lang.String.class ||
+ returnType == java.lang.Class.class ||
+ returnType.isEnum() ||
+ returnType.isAnnotation())) {
+ valid = false;
+ break block;
+ }
+
+ /*
+ * "It is a compile-time error if any method declared in an
+ * annotation type has a signature that is
+ * override-equivalent to that of any public or protected
+ * method declared in class Object or in the interface
+ * java.lang.annotation.Annotation."
+ *
+ * The methods in Object or Annotation meeting the other
+ * criteria (no arguments, contrained return type, etc.)
+ * above are:
+ *
+ * String toString()
+ * int hashCode()
+ * Class<? extends Annotation> annotationType()
+ */
+ String methodName = method.getName();
+ if ((methodName.equals("toString") && returnType == java.lang.String.class) ||
+ (methodName.equals("hashCode") && returnType == int.class) ||
+ (methodName.equals("annotationType") && returnType == java.lang.Class.class)) {
+ valid = false;
+ break block;
+ }
+ }
+
+ if (!valid) {
+ throw new AnnotationFormatError(
+ "Malformed method on an annotation type: " + method);
+ }
+ }
}
< prev index next >