blob: 45147dc51cf42828046107aa48f7501e6d1a1f07 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2004 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
26package sun.reflect.annotation;
27
28import java.lang.annotation.*;
29import java.lang.reflect.*;
30import java.io.Serializable;
31import java.util.*;
32import java.lang.annotation.*;
33import java.security.AccessController;
34import java.security.PrivilegedAction;
35
36/**
37 * InvocationHandler for dynamic proxy implementation of Annotation.
38 *
39 * @author Josh Bloch
40 * @since 1.5
41 */
42class AnnotationInvocationHandler implements InvocationHandler, Serializable {
43 private final Class type;
44 private final Map<String, Object> memberValues;
45
46 AnnotationInvocationHandler(Class 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 final Method[] mm = type.getDeclaredMethods();
276 AccessController.doPrivileged(new PrivilegedAction() {
277 public Object run() {
278 AccessibleObject.setAccessible(mm, true);
279 return null;
280 }
281 });
282 memberMethods = mm;
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}