/* |
|
* Copyright (c) 2003, 2016, 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 sun.misc.JavaLangAccess; |
|
import java.lang.annotation.*; |
|
import java.lang.reflect.*; |
|
import java.util.*; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
/** |
|
* Represents an annotation type at run time. Used to type-check annotations |
|
* and apply member defaults. |
|
* |
|
* @author Josh Bloch |
|
* @since 1.5 |
|
*/ |
|
public class AnnotationType { |
|
/** |
|
* Member name -> type mapping. Note that primitive types |
|
* are represented by the class objects for the corresponding wrapper |
|
* types. This matches the return value that must be used for a |
|
* dynamic proxy, allowing for a simple isInstance test. |
|
*/ |
|
private final Map<String, Class<?>> memberTypes; |
|
/** |
|
* Member name -> default value mapping. |
|
*/ |
|
private final Map<String, Object> memberDefaults; |
|
/** |
|
* Member name -> Method object mapping. This (and its assoicated |
|
* accessor) are used only to generate AnnotationTypeMismatchExceptions. |
|
*/ |
|
private final Map<String, Method> members; |
|
/** |
|
* The retention policy for this annotation type. |
|
*/ |
|
private final RetentionPolicy retention; |
|
/** |
|
* Whether this annotation type is inherited. |
|
*/ |
|
private final boolean inherited; |
|
/** |
|
* Returns an AnnotationType instance for the specified annotation type. |
|
* |
|
* @throw IllegalArgumentException if the specified class object for |
|
* does not represent a valid annotation type |
|
*/ |
|
public static AnnotationType getInstance( |
|
Class<? extends Annotation> annotationClass) |
|
{ |
|
JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); |
|
AnnotationType result = jla.getAnnotationType(annotationClass); // volatile read |
|
if (result == null) { |
|
result = new AnnotationType(annotationClass); |
|
// try to CAS the AnnotationType: null -> result |
|
if (!jla.casAnnotationType(annotationClass, null, result)) { |
|
// somebody was quicker -> read it's result |
|
result = jla.getAnnotationType(annotationClass); |
|
assert result != null; |
|
} |
|
} |
|
return result; |
|
} |
|
/** |
|
* Sole constructor. |
|
* |
|
* @param annotationClass the class object for the annotation type |
|
* @throw IllegalArgumentException if the specified class object for |
|
* does not represent a valid annotation type |
|
*/ |
|
private AnnotationType(final Class<? extends Annotation> annotationClass) { |
|
if (!annotationClass.isAnnotation()) |
|
throw new IllegalArgumentException("Not an annotation type"); |
|
Method[] methods = |
|
AccessController.doPrivileged(new PrivilegedAction<Method[]>() { |
|
public Method[] run() { |
|
// Initialize memberTypes and defaultValues |
|
return annotationClass.getDeclaredMethods(); |
|
} |
|
}); |
|
memberTypes = new HashMap<String,Class<?>>(methods.length+1, 1.0f); |
|
memberDefaults = new HashMap<String, Object>(0); |
|
members = new HashMap<String, Method>(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"); |
|
} |
|
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); |
|
} |
|
} |
|
} |
|
// Initialize retention, & inherited fields. Special treatment |
|
// of the corresponding annotation types breaks infinite recursion. |
|
if (annotationClass != Retention.class && |
|
annotationClass != Inherited.class) { |
|
JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); |
|
Map<Class<? extends Annotation>, Annotation> metaAnnotations = |
|
AnnotationParser.parseSelectAnnotations( |
|
jla.getRawClassAnnotations(annotationClass), |
|
jla.getConstantPool(annotationClass), |
|
annotationClass, |
|
Retention.class, Inherited.class |
|
); |
|
Retention ret = (Retention) metaAnnotations.get(Retention.class); |
|
retention = (ret == null ? RetentionPolicy.CLASS : ret.value()); |
|
inherited = metaAnnotations.containsKey(Inherited.class); |
|
} |
|
else { |
|
retention = RetentionPolicy.RUNTIME; |
|
inherited = false; |
|
} |
|
} |
|
/** |
|
* Returns the type that must be returned by the invocation handler |
|
* of a dynamic proxy in order to have the dynamic proxy return |
|
* the specified type (which is assumed to be a legal member type |
|
* for an annotation). |
|
*/ |
|
public static Class<?> invocationHandlerReturnType(Class<?> type) { |
|
// Translate primitives to wrappers |
|
if (type == byte.class) |
|
return Byte.class; |
|
if (type == char.class) |
|
return Character.class; |
|
if (type == double.class) |
|
return Double.class; |
|
if (type == float.class) |
|
return Float.class; |
|
if (type == int.class) |
|
return Integer.class; |
|
if (type == long.class) |
|
return Long.class; |
|
if (type == short.class) |
|
return Short.class; |
|
if (type == boolean.class) |
|
return Boolean.class; |
|
// Otherwise, just return declared type |
|
return type; |
|
} |
|
/** |
|
* Returns member types for this annotation type |
|
* (member name -> type mapping). |
|
*/ |
|
public Map<String, Class<?>> memberTypes() { |
|
return memberTypes; |
|
} |
|
/** |
|
* Returns members of this annotation type |
|
* (member name -> associated Method object mapping). |
|
*/ |
|
public Map<String, Method> members() { |
|
return members; |
|
} |
|
/** |
|
* Returns the default values for this annotation type |
|
* (Member name -> default value mapping). |
|
*/ |
|
public Map<String, Object> memberDefaults() { |
|
return memberDefaults; |
|
} |
|
/** |
|
* Returns the retention policy for this annotation type. |
|
*/ |
|
public RetentionPolicy retention() { |
|
return retention; |
|
} |
|
/** |
|
* Returns true if this this annotation type is inherited. |
|
*/ |
|
public boolean isInherited() { |
|
return inherited; |
|
} |
|
/** |
|
* For debugging. |
|
*/ |
|
public String toString() { |
|
return "Annotation Type:\n" + |
|
" Member types: " + memberTypes + "\n" + |
|
" Member defaults: " + memberDefaults + "\n" + |
|
" Retention policy: " + retention + "\n" + |
|
" Inherited: " + inherited; |
|
} |
|
} |