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    }