123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.bytecode.annotation;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
- import javassist.ClassPool;
- import javassist.CtClass;
- import javassist.NotFoundException;
- import javassist.bytecode.AnnotationDefaultAttribute;
- import javassist.bytecode.ClassFile;
- import javassist.bytecode.MethodInfo;
-
- /**
- * Internal-use only. This is a helper class internally used for implementing
- * <code>toAnnotationType()</code> in <code>Annotation</code>.
- *
- * @author Shigeru Chiba
- * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
- * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
- */
- public class AnnotationImpl implements InvocationHandler {
- private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
- private static Method JDK_ANNOTATION_TYPE_METHOD = null;
-
- private Annotation annotation;
- private ClassPool pool;
- private ClassLoader classLoader;
- private transient Class<?> annotationType;
- private transient int cachedHashCode = Integer.MIN_VALUE;
-
- static {
- // Try to resolve the JDK annotation type method
- try {
- Class<?> clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
- JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null);
- }
- catch (Exception ignored) {
- // Probably not JDK5+
- }
- }
-
- /**
- * Constructs an annotation object.
- *
- * @param cl class loader for obtaining annotation types.
- * @param clazz the annotation type.
- * @param cp class pool for containing an annotation
- * type (or null).
- * @param anon the annotation.
- * @return the annotation
- */
- public static Object make(ClassLoader cl, Class<?> clazz, ClassPool cp,
- Annotation anon)
- throws IllegalArgumentException
- {
- AnnotationImpl handler = new AnnotationImpl(anon, cp, cl);
- return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler);
- }
-
- private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) {
- annotation = a;
- pool = cp;
- classLoader = loader;
- }
-
- /**
- * Obtains the name of the annotation type.
- *
- * @return the type name
- */
- public String getTypeName() {
- return annotation.getTypeName();
- }
-
- /**
- * Get the annotation type
- *
- * @return the annotation class
- * @throws NoClassDefFoundError when the class could not loaded
- */
- private Class<?> getAnnotationType() {
- if (annotationType == null) {
- String typeName = annotation.getTypeName();
- try {
- annotationType = classLoader.loadClass(typeName);
- }
- catch (ClassNotFoundException e) {
- NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName);
- error.setStackTrace(e.getStackTrace());
- throw error;
- }
- }
- return annotationType;
- }
-
- /**
- * Obtains the internal data structure representing the annotation.
- *
- * @return the annotation
- */
- public Annotation getAnnotation() {
- return annotation;
- }
-
- /**
- * Executes a method invocation on a proxy instance.
- * The implementations of <code>toString()</code>, <code>equals()</code>,
- * and <code>hashCode()</code> are directly supplied by the
- * <code>AnnotationImpl</code>. The <code>annotationType()</code> method
- * is also available on the proxy instance.
- */
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable
- {
- String name = method.getName();
- if (Object.class == method.getDeclaringClass()) {
- if ("equals".equals(name)) {
- Object obj = args[0];
- return Boolean.valueOf(checkEquals(obj));
- }
- else if ("toString".equals(name))
- return annotation.toString();
- else if ("hashCode".equals(name))
- return Integer.valueOf(hashCode());
- }
- else if ("annotationType".equals(name)
- && method.getParameterTypes().length == 0)
- return getAnnotationType();
-
- MemberValue mv = annotation.getMemberValue(name);
- if (mv == null)
- return getDefault(name, method);
- return mv.getValue(classLoader, pool, method);
- }
-
- private Object getDefault(String name, Method method)
- throws ClassNotFoundException, RuntimeException
- {
- String classname = annotation.getTypeName();
- if (pool != null) {
- try {
- CtClass cc = pool.get(classname);
- ClassFile cf = cc.getClassFile2();
- MethodInfo minfo = cf.getMethod(name);
- if (minfo != null) {
- AnnotationDefaultAttribute ainfo
- = (AnnotationDefaultAttribute)
- minfo.getAttribute(AnnotationDefaultAttribute.tag);
- if (ainfo != null) {
- MemberValue mv = ainfo.getDefaultValue();
- return mv.getValue(classLoader, pool, method);
- }
- }
- }
- catch (NotFoundException e) {
- throw new RuntimeException("cannot find a class file: "
- + classname);
- }
- }
-
- throw new RuntimeException("no default value: " + classname + "."
- + name + "()");
- }
-
- /**
- * Returns a hash code value for this object.
- */
- @Override
- public int hashCode() {
- if (cachedHashCode == Integer.MIN_VALUE) {
- int hashCode = 0;
-
- // Load the annotation class
- getAnnotationType();
-
- Method[] methods = annotationType.getDeclaredMethods();
- for (int i = 0; i < methods.length; ++ i) {
- String name = methods[i].getName();
- int valueHashCode = 0;
-
- // Get the value
- MemberValue mv = annotation.getMemberValue(name);
- Object value = null;
- try {
- if (mv != null)
- value = mv.getValue(classLoader, pool, methods[i]);
- if (value == null)
- value = getDefault(name, methods[i]);
- }
- catch (RuntimeException e) {
- throw e;
- }
- catch (Exception e) {
- throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
- }
-
- // Calculate the hash code
- if (value != null) {
- if (value.getClass().isArray())
- valueHashCode = arrayHashCode(value);
- else
- valueHashCode = value.hashCode();
- }
- hashCode += 127 * name.hashCode() ^ valueHashCode;
- }
-
- cachedHashCode = hashCode;
- }
- return cachedHashCode;
- }
-
- /**
- * Check that another annotation equals ourselves.
- *
- * @param obj the other annotation
- * @return the true when equals false otherwise
- * @throws Exception for any problem
- */
- private boolean checkEquals(Object obj) throws Exception {
- if (obj == null)
- return false;
-
- // Optimization when the other is one of ourselves
- if (obj instanceof Proxy) {
- InvocationHandler ih = Proxy.getInvocationHandler(obj);
- if (ih instanceof AnnotationImpl) {
- AnnotationImpl other = (AnnotationImpl) ih;
- return annotation.equals(other.annotation);
- }
- }
-
- Class<?> otherAnnotationType = (Class<?>) JDK_ANNOTATION_TYPE_METHOD.invoke(obj);
- if (getAnnotationType().equals(otherAnnotationType) == false)
- return false;
-
- Method[] methods = annotationType.getDeclaredMethods();
- for (int i = 0; i < methods.length; ++ i) {
- String name = methods[i].getName();
-
- // Get the value
- MemberValue mv = annotation.getMemberValue(name);
- Object value = null;
- Object otherValue = null;
- try {
- if (mv != null)
- value = mv.getValue(classLoader, pool, methods[i]);
- if (value == null)
- value = getDefault(name, methods[i]);
- otherValue = methods[i].invoke(obj);
- }
- catch (RuntimeException e) {
- throw e;
- }
- catch (Exception e) {
- throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
- }
-
- if (value == null && otherValue != null)
- return false;
- if (value != null && value.equals(otherValue) == false)
- return false;
- }
-
- return true;
- }
-
- /**
- * Calculates the hashCode of an array using the same
- * algorithm as java.util.Arrays.hashCode()
- *
- * @param object the object
- * @return the hashCode
- */
- private static int arrayHashCode(Object object)
- {
- if (object == null)
- return 0;
-
- int result = 1;
-
- Object[] array = (Object[]) object;
- for (int i = 0; i < array.length; ++i) {
- int elementHashCode = 0;
- if (array[i] != null)
- elementHashCode = array[i].hashCode();
- result = 31 * result + elementHashCode;
- }
- return result;
- }
- }
|