001: /*******************************************************************************************
002: * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
003: * http://backport175.codehaus.org *
004: * --------------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of Apache License Version 2.0 *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: *******************************************************************************************/package com.tc.backport175.proxy;
008:
009: import com.tc.backport175.bytecode.AnnotationElement;
010: import com.tc.backport175.bytecode.AnnotationReader;
011:
012: import com.tc.asm.Type;
013:
014: import java.lang.reflect.InvocationHandler;
015: import java.lang.reflect.Method;
016: import java.lang.reflect.Array;
017: import java.lang.reflect.Field;
018: import java.io.Serializable;
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.ArrayList;
022:
023: /**
024: * Implements a strongly typed reader handler for JavaDoc annotations.
025: *
026: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér</a>
027: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
028: */
029: public class JavaDocAnnotationInvocationHander implements
030: InvocationHandler, Serializable {
031: static final long serialVersionUID = 1584167345753299421L;
032:
033: private static final String TO_STRING_METHOD_NAME = "toString";
034: private static final String ANNOTATION_TYPE_METHOD_NAME = "annotationType";
035:
036: private final Class m_annotationInterface;
037: private final AnnotationElement.Annotation m_annotation;
038: private final String m_annotationName;
039:
040: /**
041: * The annotated class classloader. Strong ref is ok since we use a proxy handler
042: * and that one will be referenced by this classloader precisely
043: */
044: private final ClassLoader m_annotatedClassClassLoader;
045:
046: private ClassLoader getAnnotatedClassClassLoader() {
047: return m_annotatedClassClassLoader;
048: }
049:
050: /**
051: * Constructor that will trigger the parsing if required
052: *
053: * @param annotationInterface
054: * @param annotation
055: * @param annotatedClassClassLoader classloader of the annotated class from which we can safely load all values
056: */
057: public JavaDocAnnotationInvocationHander(
058: final Class annotationInterface,
059: final AnnotationElement.Annotation annotation,
060: final ClassLoader annotatedClassClassLoader) {
061: m_annotationInterface = annotationInterface;
062: m_annotation = annotation;
063: m_annotationName = annotationInterface.getName().replace('/',
064: '.');
065: m_annotatedClassClassLoader = annotatedClassClassLoader != null ? annotatedClassClassLoader
066: : ClassLoader.getSystemClassLoader();
067: }
068:
069: /**
070: * The proxy invoke method, dispatches to the target method being invoked.
071: *
072: * @param proxy
073: * @param method
074: * @param args
075: * @return
076: * @throws Throwable
077: */
078: public Object invoke(final Object proxy, final Method method,
079: final Object[] args) throws Throwable {
080: final String methodName = method.getName();
081: if (methodName.equals(ANNOTATION_TYPE_METHOD_NAME)) {
082: return m_annotationInterface;
083: } else if (methodName.equals(TO_STRING_METHOD_NAME)) {
084: return invokeToString();
085: } else {
086: return invokeAnnotationValue(method);
087: }
088: }
089:
090: /**
091: * Handle invocation of an annotation value method.
092: *
093: * @param method
094: * @return
095: */
096: private Object invokeAnnotationValue(final Method method) {
097: Object returnValue = null;
098: for (Iterator it = m_annotation.getElements().iterator(); it
099: .hasNext();) {
100: AnnotationElement.NamedValue namedValue = (AnnotationElement.NamedValue) it
101: .next();
102: if (namedValue.getName().equals(method.getName())) {
103: returnValue = resolveValue(namedValue, method
104: .getReturnType());
105: break;
106: }
107: }
108: return returnValue;
109: }
110:
111: /**
112: * Handles invocation of the <code>toString</code> method.
113: *
114: * @return the string representation for the annotation
115: */
116: private Object invokeToString() {
117: StringBuffer sb = new StringBuffer();
118: sb.append('@');
119: sb.append(m_annotationName);
120: sb.append('(');
121: sb.append(m_annotation.toString());
122: sb.append(')');
123: return sb.toString();
124: }
125:
126: /**
127: * Returns the resolved value for the annotation element.
128: *
129: * @param namedValue
130: * @param valueType
131: * @return
132: */
133: private Object resolveValue(
134: final AnnotationElement.NamedValue namedValue,
135: final Class valueType) {
136: if (namedValue.isResolved()) {
137: return namedValue.getResolvedValue();
138: }
139: AnnotationElement.Type type = namedValue.getType();
140: final Object value;
141: if (type.equals(AnnotationElement.Type.ANNOTATION)) {
142: AnnotationElement.Annotation annotation = (AnnotationElement.Annotation) namedValue
143: .getValue();
144: value = ProxyFactory.newAnnotationProxy(annotation,
145: getAnnotatedClassClassLoader());
146:
147: } else if (type.equals(AnnotationElement.Type.ARRAY)) {
148: value = resolveArray(namedValue, valueType);
149:
150: } else if (type.equals(AnnotationElement.Type.ENUM)) {
151: value = resolveEnum(namedValue);
152:
153: } else if (type.equals(AnnotationElement.Type.TYPE)) {
154: value = resolveType(namedValue);
155:
156: } else {
157: value = namedValue.getValue();
158: }
159: namedValue.setResolvedValue(value);
160: return value;
161: }
162:
163: /**
164: * Returns the class of an unresolved type.
165: *
166: * @param namedValue
167: * @return
168: */
169: private Object resolveType(
170: final AnnotationElement.NamedValue namedValue) {
171: final Object value = namedValue.getValue();
172: if (value instanceof Type) {
173: // type
174: final Type type = (Type) value;
175: final Class resolvedType;
176: try {
177: if (type.getClassName().endsWith("[]")) {
178: //if (type.getDimensions() > 0) { // Note: Bugs in ASM prevents me from using this check, first: if type is primitive -> NPE, second: dimension is wrong for non-array types (1)
179: int dimensions = type.getDimensions();
180: Type elementType = type.getElementType();
181: Class componentType = resolveType(elementType);
182: resolvedType = Array.newInstance(componentType,
183: new int[dimensions]).getClass();
184: } else {
185: resolvedType = resolveType(type);
186: }
187: } catch (ClassNotFoundException cnfe) {
188: throw new ResolveAnnotationException(
189: "class ["
190: + type.getClassName()
191: + "] defined in annotation can not be found in class loader ["
192: + m_annotatedClassClassLoader + "]",
193: cnfe);
194: }
195: return resolvedType;
196: } else {
197: // primitive value
198: return value;
199: }
200: }
201:
202: /**
203: * Resolves a type.
204: *
205: * @param type
206: * @return
207: * @throws ClassNotFoundException
208: */
209: private Class resolveType(final Type type)
210: throws ClassNotFoundException {
211: Class resolvedType;
212: if (Type.LONG_TYPE.equals(type)) {
213: resolvedType = long.class;
214: } else if (Type.INT_TYPE.equals(type)) {
215: resolvedType = int.class;
216: } else if (Type.SHORT_TYPE.equals(type)) {
217: resolvedType = short.class;
218: } else if (Type.DOUBLE_TYPE.equals(type)) {
219: resolvedType = double.class;
220: } else if (Type.FLOAT_TYPE.equals(type)) {
221: resolvedType = float.class;
222: } else if (Type.BOOLEAN_TYPE.equals(type)) {
223: resolvedType = boolean.class;
224: } else if (Type.BYTE_TYPE.equals(type)) {
225: resolvedType = byte.class;
226: } else if (Type.CHAR_TYPE.equals(type)) {
227: resolvedType = char.class;
228: } else {
229: resolvedType = Class.forName(type.getClassName(), false,
230: getAnnotatedClassClassLoader());
231: }
232: return resolvedType;
233: }
234:
235: /**
236: * Retuns the value of an enum (static field reference).
237: *
238: * @param namedValue
239: * @return
240: */
241: private Object resolveEnum(
242: final AnnotationElement.NamedValue namedValue) {
243: AnnotationElement.Enum enumElement = (AnnotationElement.Enum) namedValue
244: .getValue();
245: String className = AnnotationReader.toJavaName(enumElement
246: .getDesc());
247: String value = enumElement.getValue();
248:
249: try {
250: Class clazz = Class.forName(className, false,
251: getAnnotatedClassClassLoader());
252: Field field = clazz.getDeclaredField(value);
253: try {
254: return field.get(null);
255: } catch (IllegalAccessException e) {
256: throw new ResolveAnnotationException(
257: "can not access static reference field due to: "
258: + e.toString(), e);
259: }
260: } catch (Exception e) {
261: throw new ResolveAnnotationException(
262: "could not retrieve static reference to field (enum) ["
263: + className + "." + namedValue.getName()
264: + "] due to: " + e.toString(), e);
265: }
266: }
267:
268: /**
269: * Resolves the array type and returns an array instance of the correct type (including primitive types).
270: *
271: * @param namedValue
272: * @param valueType
273: * @return
274: */
275: private Object resolveArray(
276: final AnnotationElement.NamedValue namedValue,
277: final Class valueType) {
278: if (!valueType.isArray()) {
279: throw new IllegalArgumentException(
280: "annotation interface method ["
281: + namedValue.getName() + "] in interface ["
282: + m_annotationName
283: + "] needs to return an ARRAY type");
284: }
285: AnnotationElement.Array array = (AnnotationElement.Array) namedValue
286: .getValue();
287: Class componentType = valueType.getComponentType();
288:
289: List arrayElements = array.getElements();
290: List elementList = new ArrayList();
291: for (Iterator it2 = arrayElements.iterator(); it2.hasNext();) {
292: AnnotationElement.NamedValue arrayValue = (AnnotationElement.NamedValue) it2
293: .next();
294:
295: // recursive call to resolveValue(..)
296: elementList.add(resolveValue(arrayValue, componentType));
297: }
298:
299: if (componentType.isPrimitive()) {
300: if (componentType.equals(int.class)) {
301: int[] arrayInstance = (int[]) Array.newInstance(
302: componentType, arrayElements.size());
303: int i = 0;
304: for (Iterator it = elementList.iterator(); it.hasNext();) {
305: Integer primitive = (Integer) it.next();
306: arrayInstance[i++] = primitive.intValue();
307: }
308: return arrayInstance;
309: } else if (componentType.equals(long.class)) {
310: long[] arrayInstance = (long[]) Array.newInstance(
311: componentType, arrayElements.size());
312: int i = 0;
313: for (Iterator it = elementList.iterator(); it.hasNext();) {
314: Long primitive = (Long) it.next();
315: arrayInstance[i++] = primitive.longValue();
316: }
317: return arrayInstance;
318: } else if (componentType.equals(short.class)) {
319: short[] arrayInstance = (short[]) Array.newInstance(
320: componentType, arrayElements.size());
321: int i = 0;
322: for (Iterator it = elementList.iterator(); it.hasNext();) {
323: Short primitive = (Short) it.next();
324: arrayInstance[i++] = primitive.shortValue();
325: }
326: return arrayInstance;
327: } else if (componentType.equals(float.class)) {
328: float[] arrayInstance = (float[]) Array.newInstance(
329: componentType, arrayElements.size());
330: int i = 0;
331: for (Iterator it = elementList.iterator(); it.hasNext();) {
332: Float primitive = (Float) it.next();
333: arrayInstance[i++] = primitive.floatValue();
334: }
335: return arrayInstance;
336: } else if (componentType.equals(double.class)) {
337: double[] arrayInstance = (double[]) Array.newInstance(
338: componentType, arrayElements.size());
339: int i = 0;
340: for (Iterator it = elementList.iterator(); it.hasNext();) {
341: Double primitive = (Double) it.next();
342: arrayInstance[i++] = primitive.doubleValue();
343: }
344: return arrayInstance;
345: } else if (componentType.equals(char.class)) {
346: char[] arrayInstance = (char[]) Array.newInstance(
347: componentType, arrayElements.size());
348: int i = 0;
349: for (Iterator it = elementList.iterator(); it.hasNext();) {
350: Character primitive = (Character) it.next();
351: arrayInstance[i++] = primitive.charValue();
352: }
353: return arrayInstance;
354: } else if (componentType.equals(boolean.class)) {
355: boolean[] arrayInstance = (boolean[]) Array
356: .newInstance(componentType, arrayElements
357: .size());
358: int i = 0;
359: for (Iterator it = elementList.iterator(); it.hasNext();) {
360: Boolean primitive = (Boolean) it.next();
361: arrayInstance[i++] = primitive.booleanValue();
362: }
363: return arrayInstance;
364: } else if (componentType.equals(byte.class)) {
365: byte[] arrayInstance = (byte[]) Array.newInstance(
366: componentType, arrayElements.size());
367: int i = 0;
368: for (Iterator it = elementList.iterator(); it.hasNext();) {
369: Byte primitive = (Byte) it.next();
370: arrayInstance[i++] = primitive.byteValue();
371: }
372: return arrayInstance;
373: }
374: } else {
375: Object[] arrayInstance = (Object[]) Array.newInstance(
376: componentType, arrayElements.size());
377: int i = 0;
378: for (Iterator it = elementList.iterator(); it.hasNext();) {
379: Object element = it.next();
380: arrayInstance[i++] = element;
381: }
382: return arrayInstance;
383: }
384: return null;
385: }
386:
387: }
|