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