KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > apt > core > internal > env > AnnotationInvocationHandler


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 BEA Systems, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * tyeung@bea.com - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.apt.core.internal.env;
13
14 import com.sun.mirror.type.MirroredTypeException;
15 import com.sun.mirror.type.MirroredTypesException;
16 import com.sun.mirror.type.TypeMirror;
17
18 import java.lang.reflect.Array JavaDoc;
19 import java.lang.reflect.Field JavaDoc;
20 import java.lang.reflect.InvocationHandler JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Proxy JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Collections JavaDoc;
26 import org.eclipse.jdt.apt.core.internal.declaration.AnnotationMirrorImpl;
27 import org.eclipse.jdt.apt.core.internal.util.Factory;
28 import org.eclipse.jdt.core.dom.IMethodBinding;
29 import org.eclipse.jdt.core.dom.IAnnotationBinding;
30 import org.eclipse.jdt.core.dom.ITypeBinding;
31 import org.eclipse.jdt.core.dom.IVariableBinding;
32
33 public class AnnotationInvocationHandler implements InvocationHandler JavaDoc
34 {
35     private static final String JavaDoc JAVA_LANG_CLASS = "java.lang.Class"; //$NON-NLS-1$
36
private final AnnotationMirrorImpl _instance;
37     private final Class JavaDoc<?> _clazz;
38
39     public AnnotationInvocationHandler(final AnnotationMirrorImpl annotation,
40                                        final Class JavaDoc<?> clazz)
41     {
42         _instance = annotation;
43         _clazz = clazz;
44     }
45
46     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc
47     {
48         final String JavaDoc methodName = method.getName();
49         if( args == null || args.length == 0 )
50         {
51             if( methodName.equals("hashCode") ) //$NON-NLS-1$
52
return new Integer JavaDoc( _instance.hashCode() );
53             if( methodName.equals("toString") ) //$NON-NLS-1$
54
return _instance.toString();
55             if( methodName.equals("annotationType")) //$NON-NLS-1$
56
return _clazz;
57         }
58         else if( args.length == 1 && methodName.equals("equals") ) //$NON-NLS-1$
59
{
60             return new Boolean JavaDoc( _instance.equals( args[0] ) );
61         }
62         if( args != null && args.length != 0 )
63             throw new NoSuchMethodException JavaDoc("method " + method.getName() + formatArgs(args) + " does not exists"); //$NON-NLS-1$ //$NON-NLS-2$
64
final String JavaDoc c_methodName = method.getName();
65         final IMethodBinding methodBinding = _instance.getMethodBinding(c_methodName);
66         if( methodBinding == null )
67             throw new NoSuchMethodException JavaDoc("method " + method.getName() + "() does not exists"); //$NON-NLS-1$ //$NON-NLS-2$
68

69         final ITypeBinding retType = methodBinding.getReturnType();
70         if( retType == null ) return null;
71         
72         final String JavaDoc qName = retType.getTypeDeclaration().getQualifiedName();
73         // type of annotation member is java.lang.Class
74
if( retType.isClass() && JAVA_LANG_CLASS.equals(qName) ){
75             // need to figure out the class that's being accessed
76
final ITypeBinding[] classTypes = _instance.getMemberValueTypeBinding(c_methodName);
77             TypeMirror mirrorType = null;
78             if( classTypes != null && classTypes.length > 0 ){
79                 mirrorType = Factory.createTypeMirror(classTypes[0], _instance.getEnvironment() );
80             }
81             if( mirrorType == null )
82                 mirrorType = Factory.createErrorClassType(classTypes[0]);
83             throw new MirroredTypeException(mirrorType);
84         }
85         else if( retType.isArray() ){
86             final ITypeBinding leafType = retType.getElementType();
87             final String JavaDoc leafQName = leafType.getTypeDeclaration().getQualifiedName();
88             // type of annotation member is java.lang.Class[]
89
if( leafType.isClass() && JAVA_LANG_CLASS.equals(leafQName) ){
90                 final ITypeBinding[] classTypes = _instance.getMemberValueTypeBinding(c_methodName);
91                 final Collection JavaDoc<TypeMirror> mirrorTypes;
92                 if( classTypes == null || classTypes.length == 0 )
93                     mirrorTypes = Collections.emptyList();
94                 else{
95                     mirrorTypes = new ArrayList JavaDoc<TypeMirror>(classTypes.length);
96                     for( ITypeBinding type : classTypes ){
97                         TypeMirror mirror = Factory.createTypeMirror(type, _instance.getEnvironment() );
98                         if( mirror == null )
99                             mirrorTypes.add(Factory.createErrorClassType(type));
100                         else
101                             mirrorTypes.add(mirror);
102                     }
103                 }
104
105                 throw new MirroredTypesException(mirrorTypes);
106             }
107         }
108         final Object JavaDoc sourceValue = _instance.getValue(c_methodName);
109         return getReflectionValueWithTypeConversion(sourceValue, method.getReturnType());
110     }
111     
112     private Object JavaDoc getReflectionValueWithTypeConversion(
113             final Object JavaDoc domValue,
114             final Class JavaDoc<?> expectedType )
115     {
116         
117         final Object JavaDoc actualValue = _getReflectionValue(domValue, expectedType);
118         return performNecessaryTypeConversion(expectedType, actualValue);
119     }
120
121     private Object JavaDoc _getReflectionValue(final Object JavaDoc domValue, final Class JavaDoc<?> expectedType)
122     {
123         if( expectedType == null || domValue == null )
124             return null;
125     
126         if( domValue instanceof IVariableBinding )
127         {
128             final IVariableBinding varBinding = (IVariableBinding)domValue;
129             final ITypeBinding declaringClass = varBinding.getDeclaringClass();
130             if( declaringClass != null ){
131                 try {
132                     final Field JavaDoc returnedField = expectedType.getField( varBinding.getName() );
133                     return returnedField == null ? null : returnedField.get(null);
134                 }
135                 catch (NoSuchFieldException JavaDoc nsfe) {
136                     return null;
137                 }
138                 catch (IllegalAccessException JavaDoc iae) {
139                     return null;
140                 }
141             }
142             return null;
143         }
144         else if (domValue instanceof Object JavaDoc[])
145         {
146             final Object JavaDoc[] elements = (Object JavaDoc[])domValue;
147             if(!expectedType.isArray())
148                 return null; // bad user source
149
final Class JavaDoc<?> componentType = expectedType.getComponentType();
150             final int length = elements.length;
151             final Object JavaDoc array = Array.newInstance(componentType, length);
152     
153             for( int i=0; i<length; i++ ){
154                 final Object JavaDoc returnObj =
155                     getReflectionValueWithTypeConversion( elements[i], componentType );
156                 // fill in the array.
157
// If it is an array of some primitive type, we will need to unwrap it.
158
if( componentType.isPrimitive() ){
159                     if( componentType == boolean.class ){
160                         final Boolean JavaDoc bool = (Boolean JavaDoc)returnObj;
161                         Array.setBoolean( array, i, bool.booleanValue());
162                     }
163                     else if( componentType == byte.class ){
164                         final Byte JavaDoc b = (Byte JavaDoc)returnObj;
165                         Array.setByte( array, i, b.byteValue() );
166                     }
167                     else if( componentType == char.class ){
168                         final Character JavaDoc c = (Character JavaDoc)returnObj;
169                         Array.setChar( array, i, c.charValue() );
170                     }
171                     else if( componentType == double.class ){
172                         final Double JavaDoc d = (Double JavaDoc)returnObj;
173                         Array.setDouble( array, i, d.doubleValue() );
174                     }
175                     else if( componentType == float.class ){
176                         final Float JavaDoc f = (Float JavaDoc)returnObj;
177                         Array.setFloat( array, i, f.floatValue() );
178                     }
179                     else if( componentType == int.class ){
180                         final Integer JavaDoc integer = (Integer JavaDoc)returnObj;
181                         Array.setInt( array, i, integer.intValue() );
182                     }
183                     else if( componentType == long.class ){
184                         final Long JavaDoc l = (Long JavaDoc)returnObj;
185                         Array.setLong( array, i, l.longValue() );
186                     }
187                     else if( componentType == short.class ){
188                         final Short JavaDoc s = (Short JavaDoc)returnObj;
189                         Array.setShort( array, i, s.shortValue() );
190                     }
191                     else {
192                         throw new IllegalStateException JavaDoc("unrecognized primitive type: " + componentType ); //$NON-NLS-1$
193
}
194                 }
195                 else{
196                     Array.set( array, i, returnObj );
197                 }
198             }
199             return array;
200         }
201         // caller should have caught this case.
202
else if( domValue instanceof ITypeBinding )
203             throw new IllegalStateException JavaDoc("sourceValue is a type binding."); //$NON-NLS-1$
204

205         else if( domValue instanceof IAnnotationBinding )
206         {
207             // We cannot convert an annotation into anything else
208
if (!expectedType.isAnnotation()) {
209                 return null;
210             }
211             
212             final AnnotationMirrorImpl annoMirror =
213                 (AnnotationMirrorImpl)Factory.createAnnotationMirror(
214                     (IAnnotationBinding)domValue,
215                     _instance.getAnnotatedDeclaration(),
216                     _instance.getEnvironment());
217             final AnnotationInvocationHandler handler = new AnnotationInvocationHandler(annoMirror, expectedType);
218             return Proxy.newProxyInstance(expectedType.getClassLoader(),
219                                           new Class JavaDoc[]{ expectedType }, handler );
220         }
221         // primitive wrapper or String.
222
else
223             return domValue;
224     }
225     
226     private Object JavaDoc performNecessaryTypeConversion(Class JavaDoc<?> expectedType, Object JavaDoc actualValue){
227         if( actualValue == null )
228             return Factory.getMatchingDummyValue(expectedType);
229         else if( expectedType.isPrimitive() )
230             return Factory.performNecessaryPrimitiveTypeConversion( expectedType, actualValue, true);
231         else if( expectedType.isAssignableFrom(actualValue.getClass()))
232             return actualValue;
233         else if( expectedType.isArray() ){
234             // the above assignableFrom test failed which leave up with
235
// the array-ificiation problem.
236
// arrays are always type corrected.
237
actualValue = performNecessaryTypeConversion(expectedType.getComponentType(), actualValue);
238             return arrayify(expectedType, actualValue);
239         }
240         // type conversion cannot be performed and expected type is not a primitive
241
// Returning null so that we don't get a ClassCastException.
242
else return null;
243     }
244     
245     private Object JavaDoc arrayify(final Class JavaDoc<?> expectedType, Object JavaDoc actualValue){
246         assert expectedType.isArray() : "expected type must be an array"; //$NON-NLS-1$
247
assert ( !(actualValue instanceof Object JavaDoc[]) ) :
248             "actual value cannot be of type Object[]"; //$NON-NLS-1$
249
final Class JavaDoc<?> componentType = expectedType.getComponentType();
250         final Object JavaDoc array = Array.newInstance(componentType, 1);
251         
252         if( componentType.isPrimitive() ){
253             if( componentType == boolean.class ){
254                 final Boolean JavaDoc bool = (Boolean JavaDoc)actualValue;
255                 Array.setBoolean( array, 0, bool.booleanValue());
256             }
257             else if( componentType == byte.class ){
258                 final Byte JavaDoc b = (Byte JavaDoc)actualValue;
259                 Array.setByte( array, 0, b.byteValue() );
260             }
261             else if( componentType == char.class ){
262                 final Character JavaDoc c = (Character JavaDoc)actualValue;
263                 Array.setChar( array, 0, c.charValue() );
264             }
265             else if( componentType == double.class ){
266                 final Double JavaDoc d = (Double JavaDoc)actualValue;
267                 Array.setDouble( array, 0, d.doubleValue() );
268             }
269             else if( componentType == float.class ){
270                 final Float JavaDoc f = (Float JavaDoc)actualValue;
271                 Array.setFloat( array, 0, f.floatValue() );
272             }
273             else if( componentType == int.class ){
274                 final Integer JavaDoc integer = (Integer JavaDoc)actualValue;
275                 Array.setInt( array, 0, integer.intValue() );
276             }
277             else if( componentType == long.class ){
278                 final Long JavaDoc l = (Long JavaDoc)actualValue;
279                 Array.setLong( array, 0, l.longValue() );
280             }
281             else if( componentType == short.class ){
282                 final Short JavaDoc s = (Short JavaDoc)actualValue;
283                 Array.setShort( array, 0, s.shortValue() );
284             }
285             else {
286                 throw new IllegalStateException JavaDoc("unrecognized primitive type: " + componentType ); //$NON-NLS-1$
287
}
288         }
289         else{
290             Array.set( array, 0, actualValue );
291         }
292         return array;
293     }
294
295     private String JavaDoc formatArgs(final Object JavaDoc[] args)
296     {
297         // estimate that each class name (plus the separators) is 10 characters long plus 2 for "()".
298
final StringBuilder JavaDoc builder = new StringBuilder JavaDoc(args.length * 8 + 2 );
299         builder.append('(');
300         for( int i=0; i<args.length; i++ )
301         {
302             if( i > 0 ) builder.append(", "); //$NON-NLS-1$
303
builder.append(args[i].getClass().getName());
304         }
305
306         builder.append(')');
307
308         return builder.toString();
309     }
310 }
311
Popular Tags