1 /* sun.reflect.annotation.AnnotationInvocationHandler
2    Copyright (C) 2006, 2007
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.Method;
47 import java.lang.reflect.Proxy;
48 import java.lang.reflect.Array;
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<? extends Annotation> type;
66   private final Map<String, ?> 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    */
AnnotationInvocationHandler(Class<? extends Annotation> type, Map memberValues)73   public AnnotationInvocationHandler(Class<? extends Annotation> type, Map memberValues)
74   {
75     this.type = type;
76     this.memberValues = (Map<String, ?>)memberValues;
77   }
78 
create(Class<? extends Annotation> type, Map memberValues)79   public static Annotation create(Class<? extends Annotation> 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    */
equals(Object proxy, Object other)109   public boolean equals(Object proxy, 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, (Object[])null);
122 		    Object thisVal
123 		      = invoke(proxy,
124 			       methods[i],
125 			       (Object[])null);
126 		    if (! deepEquals(thisVal, val))
127 		      {
128 			return false;
129 		      }
130 		  }
131 		return true;
132 	      }
133 	  }
134 	catch (Throwable _)
135 	  {
136 	    // Ignore exception, like the JDK
137 	  }
138       }
139     return false;
140   }
141 
deepEquals(Object o1, Object o2)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 
deepHashCode(Object obj)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    */
hashCode()220   public int hashCode()
221   {
222     int h = 0;
223     Iterator iter = memberValues.keySet().iterator();
224     while (iter.hasNext())
225       {
226 	Object key = iter.next();
227 	try
228 	  {
229 	    Object val
230 	      = invoke(null,
231 		       type.getDeclaredMethod((String)key, (Class[])null),
232 		       (Object[])null);
233 	    h += deepHashCode(val) ^ 127 * key.hashCode();
234 	  }
235 	catch (Throwable _)
236 	  {
237 	  }
238       }
239     return h;
240   }
241 
deepToString(Object obj)242   private static String deepToString(Object obj)
243   {
244     if (obj instanceof Object[])
245       return Arrays.toString((Object[]) obj);
246 
247     return obj.toString();
248   }
249 
250   /**
251    * This method is public for use by other parts of the VM. Some VMs
252    * (can) use different representations of annotations that reuse this
253    * method.
254    */
toString()255   public String toString()
256   {
257     StringBuffer sb = new StringBuffer();
258     sb.append('@').append(type.getName()).append('(');
259     String sep = "";
260     Iterator iter = memberValues.keySet().iterator();
261     while (iter.hasNext())
262       {
263 	Object key = iter.next();
264 	Object val = memberValues.get(key);
265 	sb.append(sep).append(key).append('=').append(deepToString(val));
266 	sep = ", ";
267       }
268     sb.append(')');
269     return sb.toString();
270   }
271 
272 
getBoxedReturnType(Method method)273   private static Class getBoxedReturnType(Method method)
274   {
275     Class returnType = method.getReturnType();
276 
277     if (returnType == boolean.class)
278       return Boolean.class;
279 
280     if (returnType == byte.class)
281       return Byte.class;
282 
283     if (returnType == char.class)
284       return Character.class;
285 
286     if (returnType == short.class)
287       return Short.class;
288 
289     if (returnType == int.class)
290       return Integer.class;
291 
292     if (returnType == float.class)
293       return Float.class;
294 
295     if (returnType == long.class)
296       return Long.class;
297 
298     if (returnType == double.class)
299       return Double.class;
300 
301     return returnType;
302   }
303 
304   // This is slightly awkward.  When the value of an annotation is an
305   // array, libgcj constructs an Object[], but the value() method
306   // returns an arrays of the appropriate primitive type.  We should
307   // perhaps save the resulting array rather than the Object[].
308 
coerce(Object val, Class dstType)309   private Object coerce(Object val, Class dstType)
310     throws ArrayStoreException
311   {
312     if (! val.getClass().isArray())
313       return val;
314 
315     Object[] srcArray = (Object[])val;
316     final int len = srcArray.length;
317 
318     if (dstType.getComponentType().isPrimitive())
319       {
320 	if (dstType == boolean[].class)
321 	  {
322 	    boolean[] dst = new boolean[len];
323 	    for (int i = 0; i < len; i++)
324 	      dst[i] = (Boolean)srcArray[i];
325 	    return dst;
326 	  }
327 
328 	if (dstType == byte[].class)
329 	  {
330 	    byte[] dst = new byte[len];
331 	    for (int i = 0; i < len; i++)
332 	      dst[i] = (Byte)srcArray[i];
333 	    return dst;
334 	  }
335 
336 	if (dstType == char[].class)
337 	  {
338 	    char[] dst = new char[len];
339 	    for (int i = 0; i < len; i++)
340 	      dst[i] = (Character)srcArray[i];
341 	    return dst;
342 	  }
343 
344 	if (dstType == short[].class)
345 	  {
346 	    short[] dst = new short[len];
347 	    for (int i = 0; i < len; i++)
348 	      dst[i] = (Short)srcArray[i];
349 	    return dst;
350 	  }
351 
352 	if (dstType == int[].class)
353 	  {
354 	    int[] dst = new int[len];
355 	    for (int i = 0; i < len; i++)
356 	      dst[i] = (Integer)srcArray[i];
357 	    return dst;
358 	  }
359 
360 	if (dstType == long[].class)
361 	  {
362 	    long[] dst = new long[len];
363 	    for (int i = 0; i < len; i++)
364 	      dst[i] = (Long)srcArray[i];
365 	    return dst;
366 	  }
367 
368 	if (dstType == float[].class)
369 	  {
370 	    float[] dst = new float[len];
371 	    for (int i = 0; i < len; i++)
372 	      dst[i] = (Float)srcArray[i];
373 	    return dst;
374 	  }
375 
376 	if (dstType == double[].class)
377 	  {
378 	    double[] dst = new double[len];
379 	    for (int i = 0; i < len; i++)
380 	      dst[i] = (Double)srcArray[i];
381 	    return dst;
382 	  }
383       }
384 
385     Object dst = Array.newInstance(dstType.getComponentType(), len);
386     System.arraycopy((Object)srcArray, 0, dst, 0, len);
387     return dst;
388   }
389 
invoke(Object proxy, Method method, Object[] args)390   public Object invoke(Object proxy, Method method, Object[] args)
391     throws Throwable
392   {
393     String methodName = method.getName().intern();
394 
395     if (args == null || args.length == 0)
396       {
397 	if (methodName == "toString")
398 	  {
399 	    return toString();
400 	  }
401 	else if (methodName == "hashCode")
402 	  {
403 	    return Integer.valueOf(hashCode());
404 	  }
405 	else if (methodName == "annotationType")
406 	  {
407 	    return type;
408 	  }
409 	else
410 	  {
411 	    Object val = memberValues.get(methodName);
412 	    if (val == null)
413 	      {
414 		throw new IncompleteAnnotationException(type, methodName);
415 	      }
416 	    try
417 	      {
418 		if (val.getClass().isArray())
419 		  val = coerce((Object[])val, method.getReturnType());
420 	      }
421 	    catch (ArrayStoreException _)
422 	      {
423 		throw new AnnotationTypeMismatchException
424 		  (method, val.getClass().getName());
425 	      }
426 	    if (! getBoxedReturnType(method).isInstance(val))
427 	      throw (new AnnotationTypeMismatchException
428 		     (method, val.getClass().getName()));
429 	    return val;
430 	  }
431       }
432     else if (args.length == 1)
433       {
434 	if (methodName == "equals")
435 	  {
436 	    return Boolean.valueOf(equals(proxy, args[0]));
437 	  }
438       }
439     throw new InternalError("Invalid annotation proxy");
440   }
441 }
442