You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AnnotationImpl.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.bytecode.annotation;
  17. import java.lang.reflect.InvocationHandler;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Proxy;
  20. import javassist.ClassPool;
  21. import javassist.CtClass;
  22. import javassist.NotFoundException;
  23. import javassist.bytecode.AnnotationDefaultAttribute;
  24. import javassist.bytecode.ClassFile;
  25. import javassist.bytecode.MethodInfo;
  26. /**
  27. * Internal-use only. This is a helper class internally used for implementing
  28. * <code>toAnnotationType()</code> in <code>Annotation</code>.
  29. *
  30. * @author Shigeru Chiba
  31. * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
  32. * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
  33. */
  34. public class AnnotationImpl implements InvocationHandler {
  35. private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
  36. private static Method JDK_ANNOTATION_TYPE_METHOD = null;
  37. private Annotation annotation;
  38. private ClassPool pool;
  39. private ClassLoader classLoader;
  40. private transient Class<?> annotationType;
  41. private transient int cachedHashCode = Integer.MIN_VALUE;
  42. static {
  43. // Try to resolve the JDK annotation type method
  44. try {
  45. Class<?> clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
  46. JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null);
  47. }
  48. catch (Exception ignored) {
  49. // Probably not JDK5+
  50. }
  51. }
  52. /**
  53. * Constructs an annotation object.
  54. *
  55. * @param cl class loader for obtaining annotation types.
  56. * @param clazz the annotation type.
  57. * @param cp class pool for containing an annotation
  58. * type (or null).
  59. * @param anon the annotation.
  60. * @return the annotation
  61. */
  62. public static Object make(ClassLoader cl, Class<?> clazz, ClassPool cp,
  63. Annotation anon)
  64. throws IllegalArgumentException
  65. {
  66. AnnotationImpl handler = new AnnotationImpl(anon, cp, cl);
  67. return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler);
  68. }
  69. private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) {
  70. annotation = a;
  71. pool = cp;
  72. classLoader = loader;
  73. }
  74. /**
  75. * Obtains the name of the annotation type.
  76. *
  77. * @return the type name
  78. */
  79. public String getTypeName() {
  80. return annotation.getTypeName();
  81. }
  82. /**
  83. * Get the annotation type
  84. *
  85. * @return the annotation class
  86. * @throws NoClassDefFoundError when the class could not loaded
  87. */
  88. private Class<?> getAnnotationType() {
  89. if (annotationType == null) {
  90. String typeName = annotation.getTypeName();
  91. try {
  92. annotationType = classLoader.loadClass(typeName);
  93. }
  94. catch (ClassNotFoundException e) {
  95. NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName);
  96. error.setStackTrace(e.getStackTrace());
  97. throw error;
  98. }
  99. }
  100. return annotationType;
  101. }
  102. /**
  103. * Obtains the internal data structure representing the annotation.
  104. *
  105. * @return the annotation
  106. */
  107. public Annotation getAnnotation() {
  108. return annotation;
  109. }
  110. /**
  111. * Executes a method invocation on a proxy instance.
  112. * The implementations of <code>toString()</code>, <code>equals()</code>,
  113. * and <code>hashCode()</code> are directly supplied by the
  114. * <code>AnnotationImpl</code>. The <code>annotationType()</code> method
  115. * is also available on the proxy instance.
  116. */
  117. @Override
  118. public Object invoke(Object proxy, Method method, Object[] args)
  119. throws Throwable
  120. {
  121. String name = method.getName();
  122. if (Object.class == method.getDeclaringClass()) {
  123. if ("equals".equals(name)) {
  124. Object obj = args[0];
  125. return Boolean.valueOf(checkEquals(obj));
  126. }
  127. else if ("toString".equals(name))
  128. return annotation.toString();
  129. else if ("hashCode".equals(name))
  130. return Integer.valueOf(hashCode());
  131. }
  132. else if ("annotationType".equals(name)
  133. && method.getParameterTypes().length == 0)
  134. return getAnnotationType();
  135. MemberValue mv = annotation.getMemberValue(name);
  136. if (mv == null)
  137. return getDefault(name, method);
  138. return mv.getValue(classLoader, pool, method);
  139. }
  140. private Object getDefault(String name, Method method)
  141. throws ClassNotFoundException, RuntimeException
  142. {
  143. String classname = annotation.getTypeName();
  144. if (pool != null) {
  145. try {
  146. CtClass cc = pool.get(classname);
  147. ClassFile cf = cc.getClassFile2();
  148. MethodInfo minfo = cf.getMethod(name);
  149. if (minfo != null) {
  150. AnnotationDefaultAttribute ainfo
  151. = (AnnotationDefaultAttribute)
  152. minfo.getAttribute(AnnotationDefaultAttribute.tag);
  153. if (ainfo != null) {
  154. MemberValue mv = ainfo.getDefaultValue();
  155. return mv.getValue(classLoader, pool, method);
  156. }
  157. }
  158. }
  159. catch (NotFoundException e) {
  160. throw new RuntimeException("cannot find a class file: "
  161. + classname);
  162. }
  163. }
  164. throw new RuntimeException("no default value: " + classname + "."
  165. + name + "()");
  166. }
  167. /**
  168. * Returns a hash code value for this object.
  169. */
  170. @Override
  171. public int hashCode() {
  172. if (cachedHashCode == Integer.MIN_VALUE) {
  173. int hashCode = 0;
  174. // Load the annotation class
  175. getAnnotationType();
  176. Method[] methods = annotationType.getDeclaredMethods();
  177. for (int i = 0; i < methods.length; ++ i) {
  178. String name = methods[i].getName();
  179. int valueHashCode = 0;
  180. // Get the value
  181. MemberValue mv = annotation.getMemberValue(name);
  182. Object value = null;
  183. try {
  184. if (mv != null)
  185. value = mv.getValue(classLoader, pool, methods[i]);
  186. if (value == null)
  187. value = getDefault(name, methods[i]);
  188. }
  189. catch (RuntimeException e) {
  190. throw e;
  191. }
  192. catch (Exception e) {
  193. throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
  194. }
  195. // Calculate the hash code
  196. if (value != null) {
  197. if (value.getClass().isArray())
  198. valueHashCode = arrayHashCode(value);
  199. else
  200. valueHashCode = value.hashCode();
  201. }
  202. hashCode += 127 * name.hashCode() ^ valueHashCode;
  203. }
  204. cachedHashCode = hashCode;
  205. }
  206. return cachedHashCode;
  207. }
  208. /**
  209. * Check that another annotation equals ourselves.
  210. *
  211. * @param obj the other annotation
  212. * @return the true when equals false otherwise
  213. * @throws Exception for any problem
  214. */
  215. private boolean checkEquals(Object obj) throws Exception {
  216. if (obj == null)
  217. return false;
  218. // Optimization when the other is one of ourselves
  219. if (obj instanceof Proxy) {
  220. InvocationHandler ih = Proxy.getInvocationHandler(obj);
  221. if (ih instanceof AnnotationImpl) {
  222. AnnotationImpl other = (AnnotationImpl) ih;
  223. return annotation.equals(other.annotation);
  224. }
  225. }
  226. Class<?> otherAnnotationType = (Class<?>) JDK_ANNOTATION_TYPE_METHOD.invoke(obj);
  227. if (getAnnotationType().equals(otherAnnotationType) == false)
  228. return false;
  229. Method[] methods = annotationType.getDeclaredMethods();
  230. for (int i = 0; i < methods.length; ++ i) {
  231. String name = methods[i].getName();
  232. // Get the value
  233. MemberValue mv = annotation.getMemberValue(name);
  234. Object value = null;
  235. Object otherValue = null;
  236. try {
  237. if (mv != null)
  238. value = mv.getValue(classLoader, pool, methods[i]);
  239. if (value == null)
  240. value = getDefault(name, methods[i]);
  241. otherValue = methods[i].invoke(obj);
  242. }
  243. catch (RuntimeException e) {
  244. throw e;
  245. }
  246. catch (Exception e) {
  247. throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
  248. }
  249. if (value == null && otherValue != null)
  250. return false;
  251. if (value != null && value.equals(otherValue) == false)
  252. return false;
  253. }
  254. return true;
  255. }
  256. /**
  257. * Calculates the hashCode of an array using the same
  258. * algorithm as java.util.Arrays.hashCode()
  259. *
  260. * @param object the object
  261. * @return the hashCode
  262. */
  263. private static int arrayHashCode(Object object)
  264. {
  265. if (object == null)
  266. return 0;
  267. int result = 1;
  268. Object[] array = (Object[]) object;
  269. for (int i = 0; i < array.length; ++i) {
  270. int elementHashCode = 0;
  271. if (array[i] != null)
  272. elementHashCode = array[i].hashCode();
  273. result = 31 * result + elementHashCode;
  274. }
  275. return result;
  276. }
  277. }