001 package edu.rice.cs.cunit.subAnnot;
002
003 import edu.rice.cs.cunit.classFile.ClassFile;
004 import edu.rice.cs.cunit.classFile.ClassFileTools;
005 import edu.rice.cs.cunit.classFile.MethodInfo;
006 import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo;
007 import edu.rice.cs.cunit.classFile.constantPool.*;
008 import edu.rice.cs.cunit.classFile.constantPool.visitors.ADefaultPoolInfoVisitor;
009
010 import java.io.File;
011 import java.io.IOException;
012 import java.lang.annotation.Annotation;
013 import java.lang.reflect.Array;
014 import java.lang.reflect.InvocationHandler;
015 import java.lang.reflect.Method;
016 import java.lang.reflect.Proxy;
017 import java.util.*;
018
019 /**
020 * Proxy handler for annotations.
021 *
022 * @author Mathias Ricken
023 */
024 public class AnnotationDynamicProxyHandler implements InvocationHandler {
025 /**
026 * Annotation.
027 */
028 protected Class<? extends Annotation> _annotClass;
029
030 /**
031 * Map with key-value pairs.
032 */
033 protected Map<String, Object> _values;
034
035 /**
036 * List of class path entries.
037 */
038 protected static final ArrayList<String> _classPath = new ArrayList<String>();
039 static {
040 _classPath.addAll(Arrays.asList(System.getProperty("sun.boot.class.path").split(File.pathSeparator)));
041 _classPath.addAll(Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator)));
042 }
043
044 /**
045 * Constructor for the proxy handler.
046 * @param annotClass annotation class
047 * @param annot annotation information from the class file
048 */
049 public AnnotationDynamicProxyHandler(Class<? extends Annotation> annotClass, AAnnotationsAttributeInfo.Annotation annot) {
050 _annotClass = annotClass;
051 getDefaultValues();
052 for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
053 _values.put(nvp.getName().toString(), getMemberValue(nvp, _annotClass));
054 }
055 }
056
057 /**
058 * Load default values for members not specified.
059 */
060 protected void getDefaultValues() {
061 HashMap<String, Object> defaults = new HashMap<String, Object>();
062 Queue<Class> classesToProcess = new LinkedList<Class>();
063 classesToProcess.add(_annotClass);
064 while(classesToProcess.size()>0) {
065 Class nextClass = classesToProcess.remove();
066 Method[] methods = nextClass.getDeclaredMethods();
067 for(Method m: methods) {
068 if (m.getParameterTypes().length==0) {
069 Object v = m.getDefaultValue();
070 if (v!=null) {
071 defaults.put(m.getName(), v);
072 }
073 }
074 }
075 classesToProcess.addAll(Arrays.asList(nextClass.getInterfaces()));
076 }
077 _values = new LinkedHashMap<String, Object>(defaults);
078 }
079
080 /**
081 * Return the value specified in the
082 * @param nvp name-value pair
083 * @param annotClass the class of the annotation
084 * @return member value as an object
085 */
086 protected Object getMemberValue(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp,
087 final Class<? extends Annotation> annotClass) {
088 return nvp.getValue().execute(
089 new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Object, AAnnotationsAttributeInfo.Annotation.NameValuePair>() {
090 public Object constantMemberCase(final AAnnotationsAttributeInfo.Annotation.ConstantMemberValue outerHost,
091 AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
092 return outerHost.getConstValue().execute(new ADefaultPoolInfoVisitor<Object,Object>() {
093 public Object defaultCase(APoolInfo host, Object param) {
094 throw new ClassFormatError(
095 "Unexpected constant pool entry (" + host.getClass().getName() + "): " + host);
096 }
097
098 public Object stringCase(StringPoolInfo host, Object param) {
099 return host.getUtf().toString();
100 }
101
102 public Object intCase(IntegerPoolInfo host, Object param) {
103 switch(outerHost.getTag()) {
104 case 'B':
105 return (byte)host.getIntValue();
106 case 'C':
107 return (char)host.getIntValue();
108 case 'I':
109 return host.getIntValue();
110 case 'Z':
111 return (host.getIntValue() != 0);
112 default:
113 throw new ClassFormatError(
114 "Unexpected tag for integer constant pool entry (" + outerHost.getTag() + "): " + host);
115 }
116 }
117
118 public Object floatCase(FloatPoolInfo host, Object param) {
119 return host.getFloatValue();
120 }
121
122 public Object longCase(LongPoolInfo host, Object param) {
123 return host.getLongValue();
124 }
125
126 public Object doubleCase(DoublePoolInfo host, Object param) {
127 return host.getDoubleValue();
128 }
129
130 public Object asciizCase(ASCIIPoolInfo host, Object param) {
131 return host.toString();
132 }
133
134 public Object unicodeCase(UnicodePoolInfo host, Object param) {
135 return host.toString();
136 }
137 }, null);
138 }
139 public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host,
140 AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
141 String className = host.getTypeName().toString().replace('/','.').replace('$','.');
142 if ((className.length()==0) || (className.charAt(0)!='L')) {
143 throw new ClassFormatError("Unexpected type name for enum constant: "+host.getTypeName().toString());
144 }
145 className = className.substring(1,className.length()-1);
146 try {
147 Class enumClass = Class.forName(className, true, annotClass.getClassLoader());
148 String enumName = host.getConstValue().toString();
149 for(Object o: enumClass.getEnumConstants()) {
150 if (o.toString().equals(enumName)) { return o; }
151 }
152 throw new ClassFormatError("Unknown enum constant "+ enumName +" for class "+className);
153 }
154 catch(ClassNotFoundException e) {
155 throw new ClassFormatError("Could not find class for "+className);
156 }
157 }
158 public Object classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host,
159 AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
160 final String name = host.getClassName().toString();
161 try {
162 return ClassFileTools.getClassFromType(name, true, _annotClass.getClassLoader());
163 }
164 catch(ClassNotFoundException e) {
165 throw new ClassFormatError("Could not find class instance for "+ClassFileTools.getClassNameFromType(name));
166 }
167 }
168 @SuppressWarnings("unchecked")
169 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host,
170 AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
171 final String name = host.getAnnotation().getType();
172 try {
173 if ((name.length()==0) || (name.charAt(0)!='L')) {
174 throw new ClassFormatError("Unexpected type name for annotation: "+host.getAnnotation().getType());
175 }
176 Class a = Class.forName(name.substring(1, name.length() - 1).replace('/', '.').replace('$', '.'), true, annotClass.getClassLoader());
177 return Proxy.newProxyInstance(
178 a.getClassLoader(),
179 new Class[]{a},
180 new AnnotationDynamicProxyHandler(a, host.getAnnotation()));
181 }
182 catch (ClassNotFoundException e) {
183 throw new ClassFormatError("Could not find class instance for " + ClassFileTools.getClassNameFromType(name));
184 }
185 }
186 public Object arrayMemberCase(final AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arrHost,
187 final AAnnotationsAttributeInfo.Annotation.NameValuePair arrParam) {
188 return arrHost.getEntries()[0].execute(new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Object,Object>() {
189 public Object constantMemberCase(final AAnnotationsAttributeInfo.Annotation.ConstantMemberValue outerHost, Object param) {
190 return outerHost.getConstValue().execute(new ADefaultPoolInfoVisitor<Object, Object>() {
191 public Object defaultCase(APoolInfo host, Object param) {
192 throw new ClassFormatError("Unexpected constant pool entry (" +host.getClass().getName() + "): " + host);
193 }
194 public Object stringCase(StringPoolInfo host, Object param) {
195 String[] arr = new String[arrHost.getEntries().length];
196 int i = 0;
197 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
198 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
199 public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
200 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
201 }
202 public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
203 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
204 public String defaultCase(APoolInfo host, Object param) {
205 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
206 }
207 public String unicodeCase(UnicodePoolInfo host, Object param) {
208 return host.toString();
209 }
210 public String asciizCase(ASCIIPoolInfo host, Object param) {
211 return host.toString();
212 }
213 public String stringCase(StringPoolInfo host, Object param) {
214 return host.toString();
215 }
216 }, host);
217 }
218 }, null);
219 }
220 return arr;
221 }
222 public Object intCase(IntegerPoolInfo host, Object param) {
223 Object temp;
224 switch (outerHost.getTag()) {
225 case 'B':
226 temp = new byte[arrHost.getEntries().length];
227 break;
228 case 'C':
229 temp = new char[arrHost.getEntries().length];
230 break;
231 case 'I':
232 temp = new int[arrHost.getEntries().length];
233 break;
234 case 'Z':
235 temp = new boolean[arrHost.getEntries().length];
236 break;
237 default:
238 throw new ClassFormatError("Unexpected tag for integer constant pool entry ("+outerHost.getTag() + "): " + host);
239 }
240 final Object arr = temp;
241 int i = 0;
242 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
243 mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Integer>() {
244 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Integer param) {
245 throw new ClassFormatError("Expected integer constant as array element (was "+host.getClass().getName()+")");
246 }
247 public Object constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, final Integer index) {
248 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Object, Object>() {
249 public Object defaultCase(APoolInfo host, Object param) {
250 throw new ClassFormatError("Expected integer constant as array element (was "+host.getClass().getName()+")");
251 }
252 public Object intCase(IntegerPoolInfo host, Object param) {
253 switch (outerHost.getTag()) {
254 case 'B':
255 Array.setByte(arr, index, (byte)host.getIntValue());
256 return null;
257 case 'C':
258 Array.setChar(arr, index, (char)host.getIntValue());
259 return null;
260 case 'I':
261 Array.setInt(arr, index, host.getIntValue());
262 return null;
263 case 'Z':
264 Array.setBoolean(arr, index, (host.getIntValue() != 0));
265 return null;
266 default:
267 throw new ClassFormatError("Unexpected tag for integer constant pool entry (" +
268 outerHost.getTag() + "): " + host);
269 }
270 }
271 }, null);
272 }
273 }, i++);
274 }
275 return arr;
276 }
277 public Object floatCase(FloatPoolInfo host, Object param) {
278 float[] arr = new float[arrHost.getEntries().length];
279 int i = 0;
280 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
281 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Float, Object>() {
282 public Float defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
283 throw new ClassFormatError("Expected float as array element (was "+host.getClass().getName()+")");
284 }
285 public Float constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
286 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Float,Object>() {
287 public Float defaultCase(APoolInfo host, Object param) {
288 throw new ClassFormatError("Expected float as array element (was "+host.getClass().getName()+")");
289 }
290 public Float floatCase(FloatPoolInfo host, Object param) {
291 return host.getFloatValue();
292 }
293 }, host);
294 }
295 }, null);
296 }
297 return arr;
298 }
299 public Object longCase(LongPoolInfo host, Object param) {
300 long[] arr = new long[arrHost.getEntries().length];
301 int i = 0;
302 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
303 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Long, Object>() {
304 public Long defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
305 throw new ClassFormatError("Expected long as array element (was "+host.getClass().getName()+")");
306 }
307 public Long constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
308 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Long,Object>() {
309 public Long defaultCase(APoolInfo host, Object param) {
310 throw new ClassFormatError("Expected long as array element (was "+host.getClass().getName()+")");
311 }
312 public Long longCase(LongPoolInfo host, Object param) {
313 return host.getLongValue();
314 }
315 }, host);
316 }
317 }, null);
318 }
319 return arr;
320 }
321 public Object doubleCase(DoublePoolInfo host, Object param) {
322 double[] arr = new double[arrHost.getEntries().length];
323 int i = 0;
324 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
325 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Double, Object>() {
326 public Double defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
327 throw new ClassFormatError("Expected double as array element (was "+host.getClass().getName()+")");
328 }
329 public Double constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
330 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Double,Object>() {
331 public Double defaultCase(APoolInfo host, Object param) {
332 throw new ClassFormatError("Expected double as array element (was "+host.getClass().getName()+")");
333 }
334 public Double doubleCase(DoublePoolInfo host, Object param) {
335 return host.getDoubleValue();
336 }
337 }, host);
338 }
339 }, null);
340 }
341 return arr;
342 }
343 public Object asciizCase(ASCIIPoolInfo host, Object param) {
344 String[] arr = new String[arrHost.getEntries().length];
345 int i = 0;
346 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
347 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
348 public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
349 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
350 }
351 public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
352 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
353 public String defaultCase(APoolInfo host, Object param) {
354 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
355 }
356 public String unicodeCase(UnicodePoolInfo host, Object param) {
357 return host.toString();
358 }
359 public String asciizCase(ASCIIPoolInfo host, Object param) {
360 return host.toString();
361 }
362 public String stringCase(StringPoolInfo host, Object param) {
363 return host.toString();
364 }
365 }, host);
366 }
367 }, null);
368 }
369 return arr;
370 }
371 public Object unicodeCase(UnicodePoolInfo host, Object param) {
372 String[] arr = new String[arrHost.getEntries().length];
373 int i = 0;
374 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
375 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
376 public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
377 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
378 }
379 public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
380 return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
381 public String defaultCase(APoolInfo host, Object param) {
382 throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
383 }
384 public String unicodeCase(UnicodePoolInfo host, Object param) {
385 return host.toString();
386 }
387 public String asciizCase(ASCIIPoolInfo host, Object param) {
388 return host.toString();
389 }
390 public String stringCase(StringPoolInfo host, Object param) {
391 return host.toString();
392 }
393 }, host);
394 }
395 }, null);
396 }
397 return arr;
398 }
399 },null);
400 }
401
402 public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) {
403 String tn = host.getTypeName().toString();
404 if ((tn.length()==0) || (tn.charAt(0)!='L')) {
405 throw new ClassFormatError("Unexpected type name for enum constant: "+host.getTypeName().toString());
406 }
407 final String className = tn.replace('/', '.').replace('$', '.').substring(1, tn.length() - 1);
408 try {
409 final Class enumClass = Class.forName(className, true, annotClass.getClassLoader());
410 Object arr = Array.newInstance(enumClass, arrHost.getEntries().length);
411 int i = 0;
412 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
413 Array.set(arr, i++, mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
414 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
415 throw new ClassFormatError("Expected enum of type " + enumClass.getName() + " as array element");
416 }
417 public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) {
418 String enumName = host.getConstValue().toString();
419 for (Object o : enumClass.getEnumConstants()) {
420 if (o.toString().equals(enumName)) {
421 return o;
422 }
423 }
424 throw new ClassFormatError("Unknown enum constant " + enumName + " for class " + className);
425 }
426 }, null));
427 }
428 return arr;
429 }
430 catch (ClassNotFoundException e) {
431 throw new ClassFormatError("Could not find class for " + className);
432 }
433 }
434
435 public Object classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) {
436 Class[] arr = new Class[arrHost.getEntries().length];
437 int i = 0;
438 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
439 arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Class, Object>() {
440 public Class defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
441 throw new ClassFormatError("Expected Class object as array element");
442 }
443
444 public Class classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) {
445 String name = host.getClassName().toString();
446 try {
447 return ClassFileTools.getClassFromType(name);
448 } catch (ClassNotFoundException e) {
449 throw new ClassFormatError("Could not find class instance for "+name);
450 }
451 }
452 }, null);
453 }
454 return arr;
455 }
456
457 @SuppressWarnings("unchecked")
458 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
459 // create array based on member type in annotation definition, NOT based on first element
460 String cn = annotClass.getName();
461 ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(cn, _classPath);
462 if (cl==null) {
463 throw new ClassFormatError("Could not find class file for "+cn);
464 }
465 ClassFile cf = cl.getClassFile();
466 try {
467 cl.close();
468 }
469 catch (IOException e) { /* ignore */ }
470 String memberName = arrParam.getName().toString();
471 String retType = null;
472 for(MethodInfo mi: cf.getMethods()) {
473 if (mi.getName().toString().equals(memberName)) {
474 retType = mi.getDescriptor().toString();
475 retType = retType.substring(retType.lastIndexOf(')')+1);
476 }
477 }
478 if (retType==null) {
479 throw new ClassFormatError("Could not find member "+memberName+" in class file for "+cn);
480 }
481 if ((!retType.startsWith("[L")) || (!retType.endsWith(";"))) {
482 throw new ClassFormatError("Array of annotations return type expected for member "+memberName+" in class file for "+cn);
483 }
484
485 final String arrName = retType.replace('/', '.').replace('$', '.').substring(2, retType.length() - 1);
486 try {
487 // this is the array element class
488 final Class arrClass = Class.forName(arrName, true, annotClass.getClassLoader());
489 Object arr = Array.newInstance(arrClass, arrHost.getEntries().length);
490 int i = 0;
491 for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
492 Array.set(arr, i++, mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
493 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
494 throw new ClassFormatError("Expected annotation of type " + arrName + " as array element");
495 }
496 @SuppressWarnings("unchecked")
497 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
498 // create elements based on their own type, provided they are subtypes of the array element type
499 String tn = host.getAnnotation().getType();
500 if ((tn.length()==0) || (tn.charAt(0)!='L')) {
501 throw new ClassFormatError("Unexpected type name for enum constant: "+host.getAnnotation().toString());
502 }
503 final String eltName = tn.replace('/', '.').replace('$', '.').substring(1, tn.length() - 1);
504 try {
505 final Class eltClass = Class.forName(eltName, true, arrClass.getClassLoader());
506 return Proxy.newProxyInstance(
507 eltClass.getClassLoader(),
508 new Class[]{eltClass},
509 new AnnotationDynamicProxyHandler(eltClass, host.getAnnotation()));
510 } catch (ClassNotFoundException e) {
511 throw new ClassFormatError("Could not find class for " + eltName);
512 }
513
514 }
515 }, null));
516 }
517 return arr;
518 }
519 catch (ClassNotFoundException e) {
520 throw new ClassFormatError("Could not find class for " + arrName);
521 }
522 }
523
524 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host, Object param) {
525 throw new ClassFormatError("Array of arrays not supported as annotation member");
526 }
527 }, null);
528 }
529 }, nvp);
530 }
531
532 /**
533 * Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an
534 * invocation handler when a method is invoked on a proxy instance that it is associated with.
535 *
536 * @param proxy the proxy instance that the method was invoked on
537 * @param m the <code>Method</code> instance corresponding to the interface method invoked on the proxy
538 * instance.
539 * @param args an array of objects containing the values of the arguments passed in the method invocation on the
540 * proxy instance, or <code>null</code> if interface method takes no arguments. Arguments of primitive types are
541 * wrapped in instances of the appropriate primitive wrapper class, such as <code>java.lang.Integer</code> or
542 * <code>java.lang.Boolean</code>.
543 * @return the value to return from the method invocation on the proxy instance. If the declared return type of the
544 * interface method is a primitive type, then the value returned by this method must be an instance of the
545 * corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If
546 * the value returned by this method is <code>null</code> and the interface method's return type is primitive, then
547 * a <code>NullPointerException</code> will be thrown by the method invocation on the proxy instance. If the value
548 * returned by this method is otherwise not compatible with the interface method's declared return type as described
549 * above, a <code>ClassCastException</code> will be thrown by the method invocation on the proxy instance.
550 * @throws Throwable the exception to throw from the method invocation on the proxy instance. The exception's type
551 * must be assignable either to any of the exception types declared in the <code>throws</code> clause of the
552 * interface method or to the unchecked exception types <code>java.lang.RuntimeException</code> or
553 * <code>java.lang.Error</code>. If a checked exception is thrown by this method that is not assignable to any of
554 * the exception types declared in the <code>throws</code> clause of the interface method, then an {@link
555 * java.lang.reflect.UndeclaredThrowableException} containing the exception that was thrown by this method will be
556 * thrown by the method invocation on the proxy instance.
557 * @see java.lang.reflect.UndeclaredThrowableException
558 */
559 public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
560 Class<?>[] parTypes = m.getParameterTypes();
561 if ((m.getName().equals("toString")) &&
562 (parTypes.length==0)) {
563 // handle toString
564 // TODO: figure out sort order for toString of annotations
565 StringBuilder sb = new StringBuilder();
566 sb.append('@');
567 sb.append(_annotClass.getName());
568 LinkedHashMap<String,Object> kvPairs = new LinkedHashMap<String,Object>();
569 for(Method method: _annotClass.getMethods()) {
570 if ((!method.getDeclaringClass().equals(Object.class)) &&
571 (!method.getDeclaringClass().equals(Annotation.class)) &&
572 (method.getParameterTypes().length==0)) {
573 kvPairs.put(method.getName(), _values.get(method.getName()));
574 }
575 }
576 if (kvPairs.size()>0) {
577 sb.append('(');
578 boolean comma = false;
579 for(Map.Entry<String,Object> entry: kvPairs.entrySet()) {
580 if (comma) { sb.append(", "); } else { comma = true; }
581 sb.append(entry.getKey());
582 sb.append('=');
583 if ((entry.getValue()!=null) && (entry.getValue().getClass().isArray())) {
584 sb.append('[');
585 for(int i=0; i<Array.getLength(entry.getValue()); ++i) {
586 if (i>0) { sb.append(", "); }
587 sb.append(Array.get(entry.getValue(), i));
588 }
589 sb.append(']');
590 }
591 else {
592 sb.append(entry.getValue());
593 }
594 }
595 sb.append(')');
596 }
597 return sb.toString();
598 }
599 else if ((m.getName().equals("equals")) &&
600 (parTypes.length==1)) {
601 // handle equals
602 if (args[0]==null) { return false; }
603 if (!(args[0] instanceof Annotation)) { return false; }
604 Annotation ours = (Annotation)proxy;
605 Annotation other = (Annotation)args[0];
606 if (!other.annotationType().equals(ours.annotationType())) { return false; }
607 for(Method method: ours.annotationType().getMethods()) {
608 if ((!method.getDeclaringClass().equals(Object.class)) &&
609 (!method.getDeclaringClass().equals(Annotation.class)) &&
610 (method.getParameterTypes().length==0)) {
611 Method otherMethod;
612 try {
613 otherMethod = other.annotationType().getMethod(method.getName(), method.getParameterTypes());
614 }
615 catch(Exception e) {
616 return false;
617 }
618 Object value = method.invoke(proxy);
619 Object otherValue = otherMethod.invoke(other);
620 if ((value==null) && (otherValue!=null) ||
621 (value!=null) && (otherValue==null)) { return false; }
622 if ((value!=null) && (!value.equals(otherValue))) { return false; }
623 }
624 }
625 return true;
626 }
627 else if ((m.getName().equals("hashCode")) &&
628 (parTypes.length==0)) {
629 // handle hashCode
630 int result = 0;
631 for(Object value: _values.values()) {
632 result = 31 * result + (value != null ? value.hashCode() : 0);
633 }
634 return result;
635 }
636 else if ((m.getName().equals("annotationType")) &&
637 (parTypes.length==0)) {
638 // handle annotationType
639 return _annotClass;
640 }
641 else {
642 // handle annotation member methods
643 if ((!m.getDeclaringClass().equals(Object.class)) &&
644 (!m.getDeclaringClass().equals(Annotation.class)) &&
645 (m.getParameterTypes().length==0)) {
646 return _values.get(m.getName());
647 }
648 throw new NoSuchMethodException("Unexpected method "+m);
649 }
650 }
651 }