1 /* 2 * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package sun.reflect.annotation; 27 28 import java.lang.annotation.*; 29 import java.lang.reflect.*; 30 import java.io.Serializable; 31 import java.util.*; 32 import java.lang.annotation.*; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 36 /** 37 * InvocationHandler for dynamic proxy implementation of Annotation. 38 * 39 * @author Josh Bloch 40 * @since 1.5 41 */ 42 class AnnotationInvocationHandler implements InvocationHandler, Serializable { 43 private final Class<? extends Annotation> type; 44 private final Map<String, Object> memberValues; 45 46 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { 47 this.type = type; 48 this.memberValues = memberValues; 49 } 50 51 public Object invoke(Object proxy, Method method, Object[] args) { 52 String member = method.getName(); 53 Class<?>[] paramTypes = method.getParameterTypes(); 54 55 // Handle Object and Annotation methods 56 if (member.equals("equals") && paramTypes.length == 1 && 57 paramTypes[0] == Object.class) 58 return equalsImpl(args[0]); 59 assert paramTypes.length == 0; 60 if (member.equals("toString")) 61 return toStringImpl(); 62 if (member.equals("hashCode")) 63 return hashCodeImpl(); 64 if (member.equals("annotationType")) 65 return type; 66 67 // Handle annotation member accessors 68 Object result = memberValues.get(member); 69 70 if (result == null) 71 throw new IncompleteAnnotationException(type, member); 72 73 if (result instanceof ExceptionProxy) 74 throw ((ExceptionProxy) result).generateException(); 75 76 if (result.getClass().isArray() && Array.getLength(result) != 0) 77 result = cloneArray(result); 78 79 return result; 80 } 81 82 /** 83 * This method, which clones its array argument, would not be necessary 84 * if Cloneable had a public clone method. 85 */ 86 private Object cloneArray(Object array) { 87 Class<?> type = array.getClass(); 88 89 if (type == byte[].class) { 90 byte[] byteArray = (byte[])array; 91 return byteArray.clone(); 92 } 93 if (type == char[].class) { 94 char[] charArray = (char[])array; 95 return charArray.clone(); 96 } 97 if (type == double[].class) { 98 double[] doubleArray = (double[])array; 99 return doubleArray.clone(); 100 } 101 if (type == float[].class) { 102 float[] floatArray = (float[])array; 103 return floatArray.clone(); 104 } 105 if (type == int[].class) { 106 int[] intArray = (int[])array; 107 return intArray.clone(); 108 } 109 if (type == long[].class) { 110 long[] longArray = (long[])array; 111 return longArray.clone(); 112 } 113 if (type == short[].class) { 114 short[] shortArray = (short[])array; 115 return shortArray.clone(); 116 } 117 if (type == boolean[].class) { 118 boolean[] booleanArray = (boolean[])array; 119 return booleanArray.clone(); 120 } 121 122 Object[] objectArray = (Object[])array; 123 return objectArray.clone(); 124 } 125 126 127 /** 128 * Implementation of dynamicProxy.toString() 129 */ 130 private String toStringImpl() { 131 StringBuffer result = new StringBuffer(128); 132 result.append('@'); 133 result.append(type.getName()); 134 result.append('('); 135 boolean firstMember = true; 136 for (Map.Entry<String, Object> e : memberValues.entrySet()) { 137 if (firstMember) 138 firstMember = false; 139 else 140 result.append(", "); 141 142 result.append(e.getKey()); 143 result.append('='); 144 result.append(memberValueToString(e.getValue())); 145 } 146 result.append(')'); 147 return result.toString(); 148 } 149 150 /** 151 * Translates a member value (in "dynamic proxy return form") into a string 152 */ 153 private static String memberValueToString(Object value) { 154 Class<?> type = value.getClass(); 155 if (!type.isArray()) // primitive, string, class, enum const, 156 // or annotation 157 return value.toString(); 158 159 if (type == byte[].class) 160 return Arrays.toString((byte[]) value); 161 if (type == char[].class) 162 return Arrays.toString((char[]) value); 163 if (type == double[].class) 164 return Arrays.toString((double[]) value); 165 if (type == float[].class) 166 return Arrays.toString((float[]) value); 167 if (type == int[].class) 168 return Arrays.toString((int[]) value); 169 if (type == long[].class) 170 return Arrays.toString((long[]) value); 171 if (type == short[].class) 172 return Arrays.toString((short[]) value); 173 if (type == boolean[].class) 174 return Arrays.toString((boolean[]) value); 175 return Arrays.toString((Object[]) value); 176 } 177 178 /** 179 * Implementation of dynamicProxy.equals(Object o) 180 */ 181 private Boolean equalsImpl(Object o) { 182 if (o == this) 183 return true; 184 185 if (!type.isInstance(o)) 186 return false; 187 for (Method memberMethod : getMemberMethods()) { 188 String member = memberMethod.getName(); 189 Object ourValue = memberValues.get(member); 190 Object hisValue = null; 191 AnnotationInvocationHandler hisHandler = asOneOfUs(o); 192 if (hisHandler != null) { 193 hisValue = hisHandler.memberValues.get(member); 194 } else { 195 try { 196 hisValue = memberMethod.invoke(o); 197 } catch (InvocationTargetException e) { 198 return false; 199 } catch (IllegalAccessException e) { 200 throw new AssertionError(e); 201 } 202 } 203 if (!memberValueEquals(ourValue, hisValue)) 204 return false; 205 } 206 return true; 207 } 208 209 /** 210 * Returns an object's invocation handler if that object is a dynamic 211 * proxy with a handler of type AnnotationInvocationHandler. 212 * Returns null otherwise. 213 */ 214 private AnnotationInvocationHandler asOneOfUs(Object o) { 215 if (Proxy.isProxyClass(o.getClass())) { 216 InvocationHandler handler = Proxy.getInvocationHandler(o); 217 if (handler instanceof AnnotationInvocationHandler) 218 return (AnnotationInvocationHandler) handler; 219 } 220 return null; 221 } 222 223 /** 224 * Returns true iff the two member values in "dynamic proxy return form" 225 * are equal using the appropriate equality function depending on the 226 * member type. The two values will be of the same type unless one of 227 * the containing annotations is ill-formed. If one of the containing 228 * annotations is ill-formed, this method will return false unless the 229 * two members are identical object references. 230 */ 231 private static boolean memberValueEquals(Object v1, Object v2) { 232 Class<?> type = v1.getClass(); 233 234 // Check for primitive, string, class, enum const, annotation, 235 // or ExceptionProxy 236 if (!type.isArray()) 237 return v1.equals(v2); 238 239 // Check for array of string, class, enum const, annotation, 240 // or ExceptionProxy 241 if (v1 instanceof Object[] && v2 instanceof Object[]) 242 return Arrays.equals((Object[]) v1, (Object[]) v2); 243 244 // Check for ill formed annotation(s) 245 if (v2.getClass() != type) 246 return false; 247 248 // Deal with array of primitives 249 if (type == byte[].class) 250 return Arrays.equals((byte[]) v1, (byte[]) v2); 251 if (type == char[].class) 252 return Arrays.equals((char[]) v1, (char[]) v2); 253 if (type == double[].class) 254 return Arrays.equals((double[]) v1, (double[]) v2); 255 if (type == float[].class) 256 return Arrays.equals((float[]) v1, (float[]) v2); 257 if (type == int[].class) 258 return Arrays.equals((int[]) v1, (int[]) v2); 259 if (type == long[].class) 260 return Arrays.equals((long[]) v1, (long[]) v2); 261 if (type == short[].class) 262 return Arrays.equals((short[]) v1, (short[]) v2); 263 assert type == boolean[].class; 264 return Arrays.equals((boolean[]) v1, (boolean[]) v2); 265 } 266 267 /** 268 * Returns the member methods for our annotation type. These are 269 * obtained lazily and cached, as they're expensive to obtain 270 * and we only need them if our equals method is invoked (which should 271 * be rare). 272 */ 273 private Method[] getMemberMethods() { 274 if (memberMethods == null) { 275 memberMethods = AccessController.doPrivileged( 276 new PrivilegedAction<Method[]>() { 277 public Method[] run() { 278 final Method[] mm = type.getDeclaredMethods(); 279 AccessibleObject.setAccessible(mm, true); 280 return mm; 281 } 282 }); 283 } 284 return memberMethods; 285 } 286 private transient volatile Method[] memberMethods = null; 287 288 /** 289 * Implementation of dynamicProxy.hashCode() 290 */ 291 private int hashCodeImpl() { 292 int result = 0; 293 for (Map.Entry<String, Object> e : memberValues.entrySet()) { 294 result += (127 * e.getKey().hashCode()) ^ 295 memberValueHashCode(e.getValue()); 296 } 297 return result; 298 } 299 300 /** 301 * Computes hashCode of a member value (in "dynamic proxy return form") 302 */ 303 private static int memberValueHashCode(Object value) { 304 Class<?> type = value.getClass(); 305 if (!type.isArray()) // primitive, string, class, enum const, 306 // or annotation 307 return value.hashCode(); 308 309 if (type == byte[].class) 310 return Arrays.hashCode((byte[]) value); 311 if (type == char[].class) 312 return Arrays.hashCode((char[]) value); 313 if (type == double[].class) 314 return Arrays.hashCode((double[]) value); 315 if (type == float[].class) 316 return Arrays.hashCode((float[]) value); 317 if (type == int[].class) 318 return Arrays.hashCode((int[]) value); 319 if (type == long[].class) 320 return Arrays.hashCode((long[]) value); 321 if (type == short[].class) 322 return Arrays.hashCode((short[]) value); 323 if (type == boolean[].class) 324 return Arrays.hashCode((boolean[]) value); 325 return Arrays.hashCode((Object[]) value); 326 } 327 328 private void readObject(java.io.ObjectInputStream s) 329 throws java.io.IOException, ClassNotFoundException { 330 s.defaultReadObject(); 331 332 333 // Check to make sure that types have not evolved incompatibly 334 335 AnnotationType annotationType = null; 336 try { 337 annotationType = AnnotationType.getInstance(type); 338 } catch(IllegalArgumentException e) { 339 // Class is no longer an annotation type; all bets are off 340 return; 341 } 342 343 Map<String, Class<?>> memberTypes = annotationType.memberTypes(); 344 345 for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { 346 String name = memberValue.getKey(); 347 Class<?> memberType = memberTypes.get(name); 348 if (memberType != null) { // i.e. member still exists 349 Object value = memberValue.getValue(); 350 if (!(memberType.isInstance(value) || 351 value instanceof ExceptionProxy)) { 352 memberValue.setValue( 353 new AnnotationTypeMismatchExceptionProxy( 354 value.getClass() + "[" + value + "]").setMember( 355 annotationType.members().get(name))); 356 } 357 } 358 } 359 } 360 }