OSDN Git Service

2007-03-02 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / sun / reflect / annotation / AnnotationInvocationHandler.java
1 /* sun.reflect.annotation.AnnotationInvocationHandler
2    Copyright (C) 2006
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11  
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39 package sun.reflect.annotation;
40
41 import java.io.Serializable;
42 import java.lang.annotation.Annotation;
43 import java.lang.annotation.AnnotationTypeMismatchException;
44 import java.lang.annotation.IncompleteAnnotationException;
45 import java.lang.reflect.InvocationHandler;
46 import java.lang.reflect.InvocationTargetException;
47 import java.lang.reflect.Method;
48 import java.lang.reflect.Proxy;
49 import java.util.Arrays;
50 import java.util.Iterator;
51 import java.util.Map;
52
53 /**
54  * This class exists for serialization compatibility with the JDK.
55  * VMs can choose to implement annotations by constructing proxies
56  * with this invocation handler, but that is not required.
57  * If a different strategy for proxy objects is chosen, they can
58  * have a writeReplace method to substitute a Proxy based on this
59  * invocation handler is used for serialization.
60  */
61 public final class AnnotationInvocationHandler
62   implements InvocationHandler, Serializable
63 {
64     private static final long serialVersionUID = 6182022883658399397L;
65     private final Class type;
66     private final Map memberValues;
67
68     /**
69      * Construct a new invocation handler for an annotation proxy.
70      * Note that the VM is responsible for filling the memberValues map
71      * with the default values of all the annotation members.
72      */
73     public AnnotationInvocationHandler(Class type, Map memberValues)
74     {
75         this.type = type;
76         this.memberValues = memberValues;
77     }
78
79     public static Annotation create(Class type, Map memberValues)
80     {
81       for (Method m : type.getDeclaredMethods())
82         {
83           String name = m.getName();
84           if (! memberValues.containsKey(name))
85             {
86               // FIXME: what to do about exceptions here?
87               memberValues.put(name, m.getDefaultValue());
88             }
89         }
90       AnnotationInvocationHandler handler
91         = new AnnotationInvocationHandler(type, memberValues);
92       return (Annotation) Proxy.newProxyInstance(type.getClassLoader(),
93                                                  new Class[] { type },
94                                                  handler);
95     }
96
97     /**
98      * Compare an instance of AnnotationInvocationHandler with another object.
99      * 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 }