001/* sun.reflect.annotation.AnnotationInvocationHandler
002   Copyright (C) 2006
003   Free Software Foundation, Inc.
004
005This file is part of GNU Classpath.
006
007GNU Classpath is free software; you can redistribute it and/or modify
008it under the terms of the GNU General Public License as published by
009the Free Software Foundation; either version 2, or (at your option)
010any later version.
011 
012GNU Classpath is distributed in the hope that it will be useful, but
013WITHOUT ANY WARRANTY; without even the implied warranty of
014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015General Public License for more details.
016
017You should have received a copy of the GNU General Public License
018along with GNU Classpath; see the file COPYING.  If not, write to the
019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02002110-1301 USA.
021
022Linking this library statically or dynamically with other modules is
023making a combined work based on this library.  Thus, the terms and
024conditions of the GNU General Public License cover the whole
025combination.
026
027As a special exception, the copyright holders of this library give you
028permission to link this library with independent modules to produce an
029executable, regardless of the license terms of these independent
030modules, and to copy and distribute the resulting executable under
031terms of your choice, provided that you also meet, for each linked
032independent module, the terms and conditions of the license of that
033module.  An independent module is a module which is not derived from
034or based on this library.  If you modify this library, you may extend
035this exception to your version of the library, but you are not
036obligated to do so.  If you do not wish to do so, delete this
037exception statement from your version. */
038
039package sun.reflect.annotation;
040
041import java.io.Serializable;
042import java.lang.annotation.Annotation;
043import java.lang.annotation.AnnotationTypeMismatchException;
044import java.lang.annotation.IncompleteAnnotationException;
045import java.lang.reflect.InvocationHandler;
046import java.lang.reflect.InvocationTargetException;
047import java.lang.reflect.Method;
048import java.lang.reflect.Proxy;
049import java.util.Arrays;
050import java.util.Iterator;
051import 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 */
061public 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}