1 /* sun.reflect.annotation.AnnotationInvocationHandler 2 Copyright (C) 2006, 2007 3 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 39 package sun.reflect.annotation; 40 41 import java.io.Serializable; 42 import java.lang.annotation.Annotation; 43 import java.lang.annotation.AnnotationTypeMismatchException; 44 import java.lang.annotation.IncompleteAnnotationException; 45 import java.lang.reflect.InvocationHandler; 46 import java.lang.reflect.Method; 47 import java.lang.reflect.Proxy; 48 import java.lang.reflect.Array; 49 import java.util.Arrays; 50 import java.util.Iterator; 51 import java.util.Map; 52 53 /** 54 * This class exists for serialization compatibility with the JDK. 55 * VMs can choose to implement annotations by constructing proxies 56 * with this invocation handler, but that is not required. 57 * If a different strategy for proxy objects is chosen, they can 58 * have a writeReplace method to substitute a Proxy based on this 59 * invocation handler is used for serialization. 60 */ 61 public final class AnnotationInvocationHandler 62 implements InvocationHandler, Serializable 63 { 64 private static final long serialVersionUID = 6182022883658399397L; 65 private final Class<? extends Annotation> type; 66 private final Map<String, ?> memberValues; 67 68 /** 69 * Construct a new invocation handler for an annotation proxy. 70 * Note that the VM is responsible for filling the memberValues map 71 * with the default values of all the annotation members. 72 */ AnnotationInvocationHandler(Class<? extends Annotation> type, Map memberValues)73 public AnnotationInvocationHandler(Class<? extends Annotation> type, Map memberValues) 74 { 75 this.type = type; 76 this.memberValues = (Map<String, ?>)memberValues; 77 } 78 create(Class<? extends Annotation> type, Map memberValues)79 public static Annotation create(Class<? extends Annotation> type, Map memberValues) 80 { 81 for (Method m : type.getDeclaredMethods()) 82 { 83 String name = m.getName(); 84 if (! memberValues.containsKey(name)) 85 { 86 // FIXME: what to do about exceptions here? 87 memberValues.put(name, m.getDefaultValue()); 88 } 89 } 90 AnnotationInvocationHandler handler 91 = new AnnotationInvocationHandler(type, memberValues); 92 return (Annotation) Proxy.newProxyInstance(type.getClassLoader(), 93 new Class[] { type }, 94 handler); 95 } 96 97 /** 98 * Compare an instance of AnnotationInvocationHandler with another object. 99 * Note that the other object does not have to be an 100 * AnnotationInvocationHandler, any implementation of the annotation 101 * interface is allowed to be compared for equality. 102 * Note that this makes the equals method asymmetric, but this behavior 103 * is specified by Annotation.equals and identical to the JDK. 104 * 105 * This method is public for use by other parts of the VM. Some VMs 106 * (can) use different representations of annotations that reuse this 107 * method. 108 */ equals(Object proxy, Object other)109 public boolean equals(Object proxy, Object other) 110 { 111 if (type.isInstance(other)) 112 { 113 try 114 { 115 Method[] methods = type.getDeclaredMethods(); 116 if (methods.length == memberValues.size()) 117 { 118 for (int i = 0; i < methods.length; i++) 119 { 120 String key = methods[i].getName(); 121 Object val = methods[i].invoke(other, (Object[])null); 122 Object thisVal 123 = invoke(proxy, 124 methods[i], 125 (Object[])null); 126 if (! deepEquals(thisVal, val)) 127 { 128 return false; 129 } 130 } 131 return true; 132 } 133 } 134 catch (Throwable _) 135 { 136 // Ignore exception, like the JDK 137 } 138 } 139 return false; 140 } 141 deepEquals(Object o1, Object o2)142 private static boolean deepEquals(Object o1, Object o2) 143 { 144 if (o1 == o2) 145 return true; 146 147 if (o1 == null || o2 == null) 148 return false; 149 150 if (o1 instanceof boolean[] && o2 instanceof boolean[]) 151 return Arrays.equals((boolean[]) o1, (boolean[]) o2); 152 153 if (o1 instanceof byte[] && o2 instanceof byte[]) 154 return Arrays.equals((byte[]) o1, (byte[]) o2); 155 156 if (o1 instanceof char[] && o2 instanceof char[]) 157 return Arrays.equals((char[]) o1, (char[]) o2); 158 159 if (o1 instanceof short[] && o2 instanceof short[]) 160 return Arrays.equals((short[]) o1, (short[]) o2); 161 162 if (o1 instanceof int[] && o2 instanceof int[]) 163 return Arrays.equals((int[]) o1, (int[]) o2); 164 165 if (o1 instanceof float[] && o2 instanceof float[]) 166 return Arrays.equals((float[]) o1, (float[]) o2); 167 168 if (o1 instanceof long[] && o2 instanceof long[]) 169 return Arrays.equals((long[]) o1, (long[]) o2); 170 171 if (o1 instanceof double[] && o2 instanceof double[]) 172 return Arrays.equals((double[]) o1, (double[]) o2); 173 174 if (o1 instanceof Object[] && o2 instanceof Object[]) 175 return Arrays.equals((Object[]) o1, (Object[]) o2); 176 177 return o1.equals(o2); 178 } 179 deepHashCode(Object obj)180 private static int deepHashCode(Object obj) 181 { 182 if (obj instanceof boolean[]) 183 return Arrays.hashCode((boolean[]) obj); 184 185 if (obj instanceof byte[]) 186 return Arrays.hashCode((byte[]) obj); 187 188 if (obj instanceof char[]) 189 return Arrays.hashCode((char[]) obj); 190 191 if (obj instanceof short[]) 192 return Arrays.hashCode((short[]) obj); 193 194 if (obj instanceof int[]) 195 return Arrays.hashCode((int[]) obj); 196 197 if (obj instanceof float[]) 198 return Arrays.hashCode((float[]) obj); 199 200 if (obj instanceof long[]) 201 return Arrays.hashCode((long[]) obj); 202 203 if (obj instanceof double[]) 204 return Arrays.hashCode((double[]) obj); 205 206 if (obj instanceof Object[]) 207 return Arrays.hashCode((Object[]) obj); 208 209 return obj.hashCode(); 210 } 211 212 /** 213 * Compute the hashCode for an annotation. Note that the algorithm is 214 * specified by Annotation.hashCode. 215 * 216 * This method is public for use by other parts of the VM. Some VMs 217 * (can) use different representations of annotations that reuse this 218 * method. 219 */ hashCode()220 public int hashCode() 221 { 222 int h = 0; 223 Iterator iter = memberValues.keySet().iterator(); 224 while (iter.hasNext()) 225 { 226 Object key = iter.next(); 227 try 228 { 229 Object val 230 = invoke(null, 231 type.getDeclaredMethod((String)key, (Class[])null), 232 (Object[])null); 233 h += deepHashCode(val) ^ 127 * key.hashCode(); 234 } 235 catch (Throwable _) 236 { 237 } 238 } 239 return h; 240 } 241 deepToString(Object obj)242 private static String deepToString(Object obj) 243 { 244 if (obj instanceof Object[]) 245 return Arrays.toString((Object[]) obj); 246 247 return obj.toString(); 248 } 249 250 /** 251 * This method is public for use by other parts of the VM. Some VMs 252 * (can) use different representations of annotations that reuse this 253 * method. 254 */ toString()255 public String toString() 256 { 257 StringBuffer sb = new StringBuffer(); 258 sb.append('@').append(type.getName()).append('('); 259 String sep = ""; 260 Iterator iter = memberValues.keySet().iterator(); 261 while (iter.hasNext()) 262 { 263 Object key = iter.next(); 264 Object val = memberValues.get(key); 265 sb.append(sep).append(key).append('=').append(deepToString(val)); 266 sep = ", "; 267 } 268 sb.append(')'); 269 return sb.toString(); 270 } 271 272 getBoxedReturnType(Method method)273 private static Class getBoxedReturnType(Method method) 274 { 275 Class returnType = method.getReturnType(); 276 277 if (returnType == boolean.class) 278 return Boolean.class; 279 280 if (returnType == byte.class) 281 return Byte.class; 282 283 if (returnType == char.class) 284 return Character.class; 285 286 if (returnType == short.class) 287 return Short.class; 288 289 if (returnType == int.class) 290 return Integer.class; 291 292 if (returnType == float.class) 293 return Float.class; 294 295 if (returnType == long.class) 296 return Long.class; 297 298 if (returnType == double.class) 299 return Double.class; 300 301 return returnType; 302 } 303 304 // This is slightly awkward. When the value of an annotation is an 305 // array, libgcj constructs an Object[], but the value() method 306 // returns an arrays of the appropriate primitive type. We should 307 // perhaps save the resulting array rather than the Object[]. 308 coerce(Object val, Class dstType)309 private Object coerce(Object val, Class dstType) 310 throws ArrayStoreException 311 { 312 if (! val.getClass().isArray()) 313 return val; 314 315 Object[] srcArray = (Object[])val; 316 final int len = srcArray.length; 317 318 if (dstType.getComponentType().isPrimitive()) 319 { 320 if (dstType == boolean[].class) 321 { 322 boolean[] dst = new boolean[len]; 323 for (int i = 0; i < len; i++) 324 dst[i] = (Boolean)srcArray[i]; 325 return dst; 326 } 327 328 if (dstType == byte[].class) 329 { 330 byte[] dst = new byte[len]; 331 for (int i = 0; i < len; i++) 332 dst[i] = (Byte)srcArray[i]; 333 return dst; 334 } 335 336 if (dstType == char[].class) 337 { 338 char[] dst = new char[len]; 339 for (int i = 0; i < len; i++) 340 dst[i] = (Character)srcArray[i]; 341 return dst; 342 } 343 344 if (dstType == short[].class) 345 { 346 short[] dst = new short[len]; 347 for (int i = 0; i < len; i++) 348 dst[i] = (Short)srcArray[i]; 349 return dst; 350 } 351 352 if (dstType == int[].class) 353 { 354 int[] dst = new int[len]; 355 for (int i = 0; i < len; i++) 356 dst[i] = (Integer)srcArray[i]; 357 return dst; 358 } 359 360 if (dstType == long[].class) 361 { 362 long[] dst = new long[len]; 363 for (int i = 0; i < len; i++) 364 dst[i] = (Long)srcArray[i]; 365 return dst; 366 } 367 368 if (dstType == float[].class) 369 { 370 float[] dst = new float[len]; 371 for (int i = 0; i < len; i++) 372 dst[i] = (Float)srcArray[i]; 373 return dst; 374 } 375 376 if (dstType == double[].class) 377 { 378 double[] dst = new double[len]; 379 for (int i = 0; i < len; i++) 380 dst[i] = (Double)srcArray[i]; 381 return dst; 382 } 383 } 384 385 Object dst = Array.newInstance(dstType.getComponentType(), len); 386 System.arraycopy((Object)srcArray, 0, dst, 0, len); 387 return dst; 388 } 389 invoke(Object proxy, Method method, Object[] args)390 public Object invoke(Object proxy, Method method, Object[] args) 391 throws Throwable 392 { 393 String methodName = method.getName().intern(); 394 395 if (args == null || args.length == 0) 396 { 397 if (methodName == "toString") 398 { 399 return toString(); 400 } 401 else if (methodName == "hashCode") 402 { 403 return Integer.valueOf(hashCode()); 404 } 405 else if (methodName == "annotationType") 406 { 407 return type; 408 } 409 else 410 { 411 Object val = memberValues.get(methodName); 412 if (val == null) 413 { 414 throw new IncompleteAnnotationException(type, methodName); 415 } 416 try 417 { 418 if (val.getClass().isArray()) 419 val = coerce((Object[])val, method.getReturnType()); 420 } 421 catch (ArrayStoreException _) 422 { 423 throw new AnnotationTypeMismatchException 424 (method, val.getClass().getName()); 425 } 426 if (! getBoxedReturnType(method).isInstance(val)) 427 throw (new AnnotationTypeMismatchException 428 (method, val.getClass().getName())); 429 return val; 430 } 431 } 432 else if (args.length == 1) 433 { 434 if (methodName == "equals") 435 { 436 return Boolean.valueOf(equals(proxy, args[0])); 437 } 438 } 439 throw new InternalError("Invalid annotation proxy"); 440 } 441 } 442