| /* sun.reflect.annotation.AnnotationInvocationHandler | |
| Copyright (C) 2006 | |
| Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package sun.reflect.annotation; | |
| import java.io.Serializable; | |
| import java.lang.annotation.AnnotationTypeMismatchException; | |
| import java.lang.annotation.IncompleteAnnotationException; | |
| import java.lang.reflect.InvocationHandler; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Method; | |
| import java.util.Arrays; | |
| import java.util.Iterator; | |
| import java.util.Map; | |
| /** | |
| * This class exists for serialization compatibility with the JDK. | |
| * VMs can choose to implement annotations by constructing proxies | |
| * with this invocation handler, but that is not required. | |
| * If a different strategy for proxy objects is chosen, they can | |
| * have a writeReplace method to substitute a Proxy based on this | |
| * invocation handler is used for serialization. | |
| */ | |
| public final class AnnotationInvocationHandler | |
| implements InvocationHandler, Serializable | |
| { | |
| private static final long serialVersionUID = 6182022883658399397L; | |
| private final Class type; | |
| private final Map memberValues; | |
| /** | |
| * Construct a new invocation handler for an annotation proxy. | |
| * Note that the VM is responsible for filling the memberValues map | |
| * with the default values of all the annotation members. | |
| */ | |
| public AnnotationInvocationHandler(Class type, Map memberValues) | |
| { | |
| this.type = type; | |
| this.memberValues = memberValues; | |
| } | |
| /** | |
| * Compare an instance of AnnotationInvocationHandler with another object. | |
| * Note that the other object does not have to be an | |
| * AnnotationInvocationHandler, any implementation of the annotation | |
| * interface is allowed to be compared for equality. | |
| * Note that this makes the equals method asymmetric, but this behavior | |
| * is specified by Annotation.equals and identical to the JDK. | |
| * | |
| * This method is public for use by other parts of the VM. Some VMs | |
| * (can) use different representations of annotations that reuse this | |
| * method. | |
| */ | |
| public static boolean equals(Class type, Map memberValues, Object other) | |
| { | |
| if (type.isInstance(other)) | |
| { | |
| try | |
| { | |
| Method[] methods = type.getDeclaredMethods(); | |
| if (methods.length == memberValues.size()) | |
| { | |
| for (int i = 0; i < methods.length; i++) | |
| { | |
| String key = methods[i].getName(); | |
| Object val = methods[i].invoke(other, new Object[0]); | |
| if (! deepEquals(memberValues.get(key), val)) | |
| { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| catch (IllegalAccessException _) | |
| { | |
| // Ignore exception, like the JDK | |
| } | |
| catch (InvocationTargetException _) | |
| { | |
| // Ignore exception, like the JDK | |
| } | |
| } | |
| return false; | |
| } | |
| private static boolean deepEquals(Object o1, Object o2) | |
| { | |
| if (o1 == o2) | |
| return true; | |
| if (o1 == null || o2 == null) | |
| return false; | |
| if (o1 instanceof boolean[] && o2 instanceof boolean[]) | |
| return Arrays.equals((boolean[]) o1, (boolean[]) o2); | |
| if (o1 instanceof byte[] && o2 instanceof byte[]) | |
| return Arrays.equals((byte[]) o1, (byte[]) o2); | |
| if (o1 instanceof char[] && o2 instanceof char[]) | |
| return Arrays.equals((char[]) o1, (char[]) o2); | |
| if (o1 instanceof short[] && o2 instanceof short[]) | |
| return Arrays.equals((short[]) o1, (short[]) o2); | |
| if (o1 instanceof int[] && o2 instanceof int[]) | |
| return Arrays.equals((int[]) o1, (int[]) o2); | |
| if (o1 instanceof float[] && o2 instanceof float[]) | |
| return Arrays.equals((float[]) o1, (float[]) o2); | |
| if (o1 instanceof long[] && o2 instanceof long[]) | |
| return Arrays.equals((long[]) o1, (long[]) o2); | |
| if (o1 instanceof double[] && o2 instanceof double[]) | |
| return Arrays.equals((double[]) o1, (double[]) o2); | |
| if (o1 instanceof Object[] && o2 instanceof Object[]) | |
| return Arrays.equals((Object[]) o1, (Object[]) o2); | |
| return o1.equals(o2); | |
| } | |
| private static int deepHashCode(Object obj) | |
| { | |
| if (obj instanceof boolean[]) | |
| return Arrays.hashCode((boolean[]) obj); | |
| if (obj instanceof byte[]) | |
| return Arrays.hashCode((byte[]) obj); | |
| if (obj instanceof char[]) | |
| return Arrays.hashCode((char[]) obj); | |
| if (obj instanceof short[]) | |
| return Arrays.hashCode((short[]) obj); | |
| if (obj instanceof int[]) | |
| return Arrays.hashCode((int[]) obj); | |
| if (obj instanceof float[]) | |
| return Arrays.hashCode((float[]) obj); | |
| if (obj instanceof long[]) | |
| return Arrays.hashCode((long[]) obj); | |
| if (obj instanceof double[]) | |
| return Arrays.hashCode((double[]) obj); | |
| if (obj instanceof Object[]) | |
| return Arrays.hashCode((Object[]) obj); | |
| return obj.hashCode(); | |
| } | |
| /** | |
| * Compute the hashCode for an annotation. Note that the algorithm is | |
| * specified by Annotation.hashCode. | |
| * | |
| * This method is public for use by other parts of the VM. Some VMs | |
| * (can) use different representations of annotations that reuse this | |
| * method. | |
| */ | |
| public static int hashCode(Class type, Map memberValues) | |
| { | |
| int h = 0; | |
| Iterator iter = memberValues.keySet().iterator(); | |
| while (iter.hasNext()) | |
| { | |
| Object key = iter.next(); | |
| Object val = memberValues.get(key); | |
| h += deepHashCode(val) ^ 127 * key.hashCode(); | |
| } | |
| return h; | |
| } | |
| private static String deepToString(Object obj) | |
| { | |
| if (obj instanceof boolean[]) | |
| return Arrays.toString((boolean[]) obj); | |
| if (obj instanceof byte[]) | |
| return Arrays.toString((byte[]) obj); | |
| if (obj instanceof char[]) | |
| return Arrays.toString((char[]) obj); | |
| if (obj instanceof short[]) | |
| return Arrays.toString((short[]) obj); | |
| if (obj instanceof int[]) | |
| return Arrays.toString((int[]) obj); | |
| if (obj instanceof float[]) | |
| return Arrays.toString((float[]) obj); | |
| if (obj instanceof long[]) | |
| return Arrays.toString((long[]) obj); | |
| if (obj instanceof double[]) | |
| return Arrays.toString((double[]) obj); | |
| if (obj instanceof Object[]) | |
| return Arrays.toString((Object[]) obj); | |
| return obj.toString(); | |
| } | |
| /** | |
| * This method is public for use by other parts of the VM. Some VMs | |
| * (can) use different representations of annotations that reuse this | |
| * method. | |
| */ | |
| public static String toString(Class type, Map memberValues) | |
| { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.append('@').append(type.getName()).append('('); | |
| String sep = ""; | |
| Iterator iter = memberValues.keySet().iterator(); | |
| while (iter.hasNext()) | |
| { | |
| Object key = iter.next(); | |
| Object val = memberValues.get(key); | |
| sb.append(sep).append(key).append('=').append(deepToString(val)); | |
| sep = ", "; | |
| } | |
| sb.append(')'); | |
| return sb.toString(); | |
| } | |
| private static Class getBoxedReturnType(Method method) | |
| { | |
| Class returnType = method.getReturnType(); | |
| if (returnType == boolean.class) | |
| return Boolean.class; | |
| if (returnType == byte.class) | |
| return Byte.class; | |
| if (returnType == char.class) | |
| return Character.class; | |
| if (returnType == short.class) | |
| return Short.class; | |
| if (returnType == int.class) | |
| return Integer.class; | |
| if (returnType == float.class) | |
| return Float.class; | |
| if (returnType == long.class) | |
| return Long.class; | |
| if (returnType == double.class) | |
| return Double.class; | |
| return returnType; | |
| } | |
| public Object invoke(Object proxy, Method method, Object[] args) | |
| throws Throwable | |
| { | |
| String methodName = method.getName().intern(); | |
| if (args == null || args.length == 0) | |
| { | |
| if (methodName == "toString") | |
| { | |
| return toString(type, memberValues); | |
| } | |
| else if (methodName == "hashCode") | |
| { | |
| return Integer.valueOf(hashCode(type, memberValues)); | |
| } | |
| else if (methodName == "annotationType") | |
| { | |
| return type; | |
| } | |
| else | |
| { | |
| Object val = memberValues.get(methodName); | |
| if (val == null) | |
| { | |
| throw new IncompleteAnnotationException(type, methodName); | |
| } | |
| if (! getBoxedReturnType(method).isInstance(val)) | |
| { | |
| throw new AnnotationTypeMismatchException(method, | |
| val.getClass().getName()); | |
| } | |
| return val; | |
| } | |
| } | |
| else if (args.length == 1) | |
| { | |
| if (methodName == "equals") | |
| { | |
| return Boolean.valueOf(equals(type, memberValues, args[0])); | |
| } | |
| } | |
| throw new InternalError("Invalid annotation proxy"); | |
| } | |
| } |