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 }