001 /* sun.reflect.annotation.AnnotationInvocationHandler 002 Copyright (C) 2006 003 Free Software Foundation, Inc. 004 005 This file is part of GNU Classpath. 006 007 GNU Classpath is free software; you can redistribute it and/or modify 008 it under the terms of the GNU General Public License as published by 009 the Free Software Foundation; either version 2, or (at your option) 010 any later version. 011 012 GNU Classpath is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of 014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 General Public License for more details. 016 017 You should have received a copy of the GNU General Public License 018 along with GNU Classpath; see the file COPYING. If not, write to the 019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 020 02110-1301 USA. 021 022 Linking this library statically or dynamically with other modules is 023 making a combined work based on this library. Thus, the terms and 024 conditions of the GNU General Public License cover the whole 025 combination. 026 027 As a special exception, the copyright holders of this library give you 028 permission to link this library with independent modules to produce an 029 executable, regardless of the license terms of these independent 030 modules, and to copy and distribute the resulting executable under 031 terms of your choice, provided that you also meet, for each linked 032 independent module, the terms and conditions of the license of that 033 module. An independent module is a module which is not derived from 034 or based on this library. If you modify this library, you may extend 035 this exception to your version of the library, but you are not 036 obligated to do so. If you do not wish to do so, delete this 037 exception statement from your version. */ 038 039 package sun.reflect.annotation; 040 041 import java.io.Serializable; 042 import java.lang.annotation.Annotation; 043 import java.lang.annotation.AnnotationTypeMismatchException; 044 import java.lang.annotation.IncompleteAnnotationException; 045 import java.lang.reflect.InvocationHandler; 046 import java.lang.reflect.InvocationTargetException; 047 import java.lang.reflect.Method; 048 import java.lang.reflect.Proxy; 049 import java.util.Arrays; 050 import java.util.Iterator; 051 import java.util.Map; 052 053 /** 054 * This class exists for serialization compatibility with the JDK. 055 * VMs can choose to implement annotations by constructing proxies 056 * with this invocation handler, but that is not required. 057 * If a different strategy for proxy objects is chosen, they can 058 * have a writeReplace method to substitute a Proxy based on this 059 * invocation handler is used for serialization. 060 */ 061 public final class AnnotationInvocationHandler 062 implements InvocationHandler, Serializable 063 { 064 private static final long serialVersionUID = 6182022883658399397L; 065 private final Class type; 066 private final Map memberValues; 067 068 /** 069 * Construct a new invocation handler for an annotation proxy. 070 * Note that the VM is responsible for filling the memberValues map 071 * with the default values of all the annotation members. 072 */ 073 public AnnotationInvocationHandler(Class type, Map memberValues) 074 { 075 this.type = type; 076 this.memberValues = memberValues; 077 } 078 079 public static Annotation create(Class type, Map memberValues) 080 { 081 for (Method m : type.getDeclaredMethods()) 082 { 083 String name = m.getName(); 084 if (! memberValues.containsKey(name)) 085 { 086 // FIXME: what to do about exceptions here? 087 memberValues.put(name, m.getDefaultValue()); 088 } 089 } 090 AnnotationInvocationHandler handler 091 = new AnnotationInvocationHandler(type, memberValues); 092 return (Annotation) Proxy.newProxyInstance(type.getClassLoader(), 093 new Class[] { type }, 094 handler); 095 } 096 097 /** 098 * Compare an instance of AnnotationInvocationHandler with another object. 099 * 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 */ 109 public static boolean equals(Class type, Map memberValues, 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, new Object[0]); 122 if (! deepEquals(memberValues.get(key), val)) 123 { 124 return false; 125 } 126 } 127 return true; 128 } 129 } 130 catch (IllegalAccessException _) 131 { 132 // Ignore exception, like the JDK 133 } 134 catch (InvocationTargetException _) 135 { 136 // Ignore exception, like the JDK 137 } 138 } 139 return false; 140 } 141 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 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 */ 220 public static int hashCode(Class type, Map memberValues) 221 { 222 int h = 0; 223 Iterator iter = memberValues.keySet().iterator(); 224 while (iter.hasNext()) 225 { 226 Object key = iter.next(); 227 Object val = memberValues.get(key); 228 h += deepHashCode(val) ^ 127 * key.hashCode(); 229 } 230 return h; 231 } 232 233 private static String deepToString(Object obj) 234 { 235 if (obj instanceof boolean[]) 236 return Arrays.toString((boolean[]) obj); 237 238 if (obj instanceof byte[]) 239 return Arrays.toString((byte[]) obj); 240 241 if (obj instanceof char[]) 242 return Arrays.toString((char[]) obj); 243 244 if (obj instanceof short[]) 245 return Arrays.toString((short[]) obj); 246 247 if (obj instanceof int[]) 248 return Arrays.toString((int[]) obj); 249 250 if (obj instanceof float[]) 251 return Arrays.toString((float[]) obj); 252 253 if (obj instanceof long[]) 254 return Arrays.toString((long[]) obj); 255 256 if (obj instanceof double[]) 257 return Arrays.toString((double[]) obj); 258 259 if (obj instanceof Object[]) 260 return Arrays.toString((Object[]) obj); 261 262 return obj.toString(); 263 } 264 265 /** 266 * This method is public for use by other parts of the VM. Some VMs 267 * (can) use different representations of annotations that reuse this 268 * method. 269 */ 270 public static String toString(Class type, Map memberValues) 271 { 272 StringBuffer sb = new StringBuffer(); 273 sb.append('@').append(type.getName()).append('('); 274 String sep = ""; 275 Iterator iter = memberValues.keySet().iterator(); 276 while (iter.hasNext()) 277 { 278 Object key = iter.next(); 279 Object val = memberValues.get(key); 280 sb.append(sep).append(key).append('=').append(deepToString(val)); 281 sep = ", "; 282 } 283 sb.append(')'); 284 return sb.toString(); 285 } 286 287 private static Class getBoxedReturnType(Method method) 288 { 289 Class returnType = method.getReturnType(); 290 291 if (returnType == boolean.class) 292 return Boolean.class; 293 294 if (returnType == byte.class) 295 return Byte.class; 296 297 if (returnType == char.class) 298 return Character.class; 299 300 if (returnType == short.class) 301 return Short.class; 302 303 if (returnType == int.class) 304 return Integer.class; 305 306 if (returnType == float.class) 307 return Float.class; 308 309 if (returnType == long.class) 310 return Long.class; 311 312 if (returnType == double.class) 313 return Double.class; 314 315 return returnType; 316 } 317 318 private Object arrayClone(Object obj) 319 { 320 if (obj instanceof boolean[]) 321 return ((boolean[]) obj).clone(); 322 323 if (obj instanceof byte[]) 324 return ((byte[]) obj).clone(); 325 326 if (obj instanceof char[]) 327 return ((char[]) obj).clone(); 328 329 if (obj instanceof short[]) 330 return ((short[]) obj).clone(); 331 332 if (obj instanceof int[]) 333 return ((int[]) obj).clone(); 334 335 if (obj instanceof float[]) 336 return ((float[]) obj).clone(); 337 338 if (obj instanceof long[]) 339 return ((long[]) obj).clone(); 340 341 if (obj instanceof double[]) 342 return ((double[]) obj).clone(); 343 344 if (obj instanceof Object[]) 345 return ((Object[]) obj).clone(); 346 347 return obj; 348 } 349 350 public Object invoke(Object proxy, Method method, Object[] args) 351 throws Throwable 352 { 353 String methodName = method.getName().intern(); 354 if (args == null || args.length == 0) 355 { 356 if (methodName == "toString") 357 { 358 return toString(type, memberValues); 359 } 360 else if (methodName == "hashCode") 361 { 362 return Integer.valueOf(hashCode(type, memberValues)); 363 } 364 else if (methodName == "annotationType") 365 { 366 return type; 367 } 368 else 369 { 370 Object val = memberValues.get(methodName); 371 if (val == null) 372 { 373 throw new IncompleteAnnotationException(type, methodName); 374 } 375 if (! getBoxedReturnType(method).isInstance(val)) 376 { 377 throw new AnnotationTypeMismatchException(method, 378 val.getClass().getName()); 379 } 380 if (val.getClass().isArray()) 381 { 382 val = arrayClone(val); 383 } 384 return val; 385 } 386 } 387 else if (args.length == 1) 388 { 389 if (methodName == "equals") 390 { 391 return Boolean.valueOf(equals(type, memberValues, args[0])); 392 } 393 } 394 throw new InternalError("Invalid annotation proxy"); 395 } 396 }