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 }