KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > annotation > Java5AnnotationInvocationHandler


1 /**************************************************************************************
2  * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

8 package org.codehaus.aspectwerkz.annotation;
9
10 import org.codehaus.aspectwerkz.reflect.MethodInfo;
11 import org.codehaus.aspectwerkz.reflect.ClassInfo;
12 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
13 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
14 import org.objectweb.asm.attrs.*;
15 import org.objectweb.asm.attrs.Annotation;
16 import org.objectweb.asm.Type;
17
18 import java.lang.reflect.InvocationHandler JavaDoc;
19 import java.lang.reflect.Method JavaDoc;
20 import java.lang.reflect.Proxy JavaDoc;
21 import java.lang.reflect.Field JavaDoc;
22 import java.lang.reflect.Array JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Arrays JavaDoc;
30 import java.io.Serializable JavaDoc;
31
32 /**
33  * Dynamic proxy handler for ASM Annotations we extract
34  * The handler resolve the LazyClass to a concrete Class so that the proxy creation does not trigger
35  * any class loading.
36  * <p/>
37  *
38  * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
39  */

40 public class Java5AnnotationInvocationHandler implements InvocationHandler JavaDoc {
41
42     /**
43      * The annotation class name
44      */

45     private final String JavaDoc m_annotationClassName;
46
47     /**
48      * A list of AnnotationElement containing the annotation instance element values
49      * (including the defaulted value)
50      */

51     private final List JavaDoc m_annotationElements;
52
53     /**
54      * private ctor - see getAnnotationProxy()
55      *
56      * @param annotationClassName
57      * @param annotationElements
58      */

59     private Java5AnnotationInvocationHandler(String JavaDoc annotationClassName, Collection JavaDoc annotationElements) {
60         m_annotationClassName = annotationClassName;
61         m_annotationElements = new ArrayList JavaDoc(annotationElements.size());
62         for (Iterator JavaDoc iterator = annotationElements.iterator(); iterator.hasNext();) {
63             m_annotationElements.add(iterator.next());
64         }
65     }
66
67     /**
68      * Dynamic proxy based implementation
69      * toString(), annotationType() and value() have a specific behavior
70      *
71      * @param proxy
72      * @param method
73      * @param args
74      * @return
75      * @throws Throwable
76      */

77     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
78         String JavaDoc name = method.getName();
79         if ("toString".equals(name)) {
80             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
81             sb.append('@').append(m_annotationClassName);
82             sb.append("(");
83             String JavaDoc sep = "";
84             for (Iterator JavaDoc iterator = m_annotationElements.iterator(); iterator.hasNext();) {
85                 AnnotationElement annotationElement = (AnnotationElement) iterator.next();
86                 sb.append(sep).append(annotationElement.name + "=" + annotationElement.toString());
87                 sep = ", ";
88             }
89             sb.append(")");
90             return sb.toString();
91         } else if ("annotationType".equals(name)) {
92             // funny, may explain why 1.5 Annotation intf has annotationType + getClass
93
// since a dynamic proxy handler cannot hijack getClass() ..
94
return Class.forName(m_annotationClassName, false, proxy.getClass().getClassLoader());
95         } else if ("value".equals(name)) {
96             if (m_annotationElements.isEmpty()) {
97                 return null;
98             } else {
99                 //FIXME !!value can be there with other elements !
100
// we could check that we don't have more than one element
101
return ((AnnotationElement) m_annotationElements.get(0)).resolveValueHolderFrom(
102                         proxy.getClass().getClassLoader()
103                 );
104             }
105         } else {
106             for (Iterator JavaDoc iterator = m_annotationElements.iterator(); iterator.hasNext();) {
107                 AnnotationElement annotationElement = (AnnotationElement) iterator.next();
108                 if (name.equals(annotationElement.name)) {
109                     return annotationElement.resolveValueHolderFrom(proxy.getClass().getClassLoader());
110                 }
111             }
112             // element not found for such a name
113
throw new RuntimeException JavaDoc("No such element on Annotation @" + m_annotationClassName + " : " + name);
114         }
115     }
116
117     /**
118      * Build and return a dynamic proxy representing the given ASM Annotation.
119      * The proxy implements the AspectWerkz Annotation interface, as well as the user type Annotation.
120      * Each elements of the annotation is proxied if needed or agressively created unless Class types to not trigger
121      * any nested loading.
122      *
123      * Note: JSR-175 does not support Annotation value of N-dimensional array. At most 1 dimension is supported and
124      * only for a subset of Java types.
125      *
126      * @param annotation
127      * @param loader the classloader of the annotatED component (can be different from the one of the annotation class)
128      * @return
129      */

130     public static org.codehaus.aspectwerkz.annotation.Annotation getAnnotationProxy(org.objectweb.asm.attrs.Annotation annotation, ClassLoader JavaDoc loader) {
131         String JavaDoc annotationClassName = Type.getType(annotation.type).getClassName();
132
133         // get the ClassInfo for the annoation class to populate the assigned element values
134
// with lazy value holders from the setted value or the default value if defaulted element
135
// has been used in the annotation
136
ClassInfo annotationClassInfo = AsmClassInfo.getClassInfo(annotationClassName, loader);
137         Map JavaDoc annotationElementValueHoldersByName = new HashMap JavaDoc();
138
139         // populate with the default values (might be then overriden by setted values)
140
MethodInfo[] annotationMethods = annotationClassInfo.getMethods();
141         for (int i = 0; i < annotationMethods.length; i++) {
142             MethodInfo annotationMethod = annotationMethods[i];
143             for (Iterator JavaDoc iterator = annotationMethod.getAnnotations().iterator(); iterator.hasNext();) {
144                 AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();
145                 // handles AnnotationDefault attribute that we have wrapped. See AnnotationDefault.
146
if (annotationInfo.getName().equals(AnnotationDefault.NAME)) {
147                     Object JavaDoc value = ((AnnotationDefault)annotationInfo.getAnnotation()).value();
148                     Object JavaDoc valueHolder = getAnnotationValueHolder(value, loader);
149                     annotationElementValueHoldersByName.put(annotationMethod.getName(),
150                                                             new AnnotationElement(annotationMethod.getName(),
151                                                                                   valueHolder)
152                     );
153                 }
154             }
155         }
156
157         // override and populate with the setted values
158
List JavaDoc settedElementValues = annotation.elementValues;
159         for (int i = 0; i < settedElementValues.size(); i++) {
160             Object JavaDoc[] element = (Object JavaDoc[]) settedElementValues.get(i);
161             String JavaDoc name = (String JavaDoc) element[0];
162             Object JavaDoc valueHolder = getAnnotationValueHolder(element[1], loader);
163             annotationElementValueHoldersByName.put(name, new AnnotationElement(name, valueHolder));
164         }
165
166         // create a dynamic proxy to embody the annotation instance
167
try {
168             Class JavaDoc typeClass = Class.forName(annotationClassName, false, loader);
169             Object JavaDoc proxy = Proxy.newProxyInstance(
170                     loader,
171                     new Class JavaDoc[]{org.codehaus.aspectwerkz.annotation.Annotation.class, typeClass},
172                     new Java5AnnotationInvocationHandler(annotationClassName,
173                                                          annotationElementValueHoldersByName.values()
174                     )
175             );
176             return (org.codehaus.aspectwerkz.annotation.Annotation) proxy;
177         } catch (ClassNotFoundException JavaDoc e) {
178             throw new WrappedRuntimeException(e);
179         }
180     }
181
182     /**
183      * Turn an ASM Annotation value into a concrete Java value holder, unless the value is of type
184      * Class, in which case we wrap it behind a LazyClass() object so that actual loading of the class
185      * will be done lazily
186      *
187      * @param value
188      * @param loader
189      * @return
190      */

191     private static Object JavaDoc getAnnotationValueHolder(Object JavaDoc value, ClassLoader JavaDoc loader) {
192         if (value instanceof Annotation.EnumConstValue) {
193             Annotation.EnumConstValue enumAsmValue = (Annotation.EnumConstValue) value;
194             try {
195                 Class JavaDoc enumClass = Class.forName(Type.getType(enumAsmValue.typeName).getClassName(), false, loader);
196                 Field JavaDoc enumConstValue = enumClass.getField(enumAsmValue.constName);
197                 return enumConstValue.get(null);
198             } catch (Exception JavaDoc e) {
199                 throw new WrappedRuntimeException(e);
200             }
201         } else if (value instanceof Type) {
202             // TODO may require additional filtering ?
203
return new AnnotationElement.LazyClass(((Type) value).getClassName());
204         } else if (value instanceof Annotation) {
205             return getAnnotationProxy(((Annotation) value), loader);
206         } else if (value instanceof Object JavaDoc[]) {
207             Object JavaDoc[] values = (Object JavaDoc[]) value;
208             Object JavaDoc[] holders = new Object JavaDoc[values.length];
209             boolean isLazyClass = false;
210             for (int i = 0; i < values.length; i++) {
211                 holders[i] = getAnnotationValueHolder(values[i], loader);
212                 if (!isLazyClass && holders[i] instanceof AnnotationElement.LazyClass) {
213                     isLazyClass = true;
214                 }
215             }
216             if (isLazyClass) {
217                 // retype the array
218
AnnotationElement.LazyClass[] typedHolders = (AnnotationElement.LazyClass[]) Array.newInstance(AnnotationElement.LazyClass.class, values.length);
219                 for (int i = 0; i < holders.length; i++) {
220                     typedHolders[i] = (AnnotationElement.LazyClass) holders[i];
221                 }
222                 return typedHolders;
223             } else {
224                 return holders;
225             }
226         }
227         return value;
228     }
229
230 }
231
232
Popular Tags