diff options
author | aph <aph@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-03-02 10:58:26 +0000 |
---|---|---|
committer | aph <aph@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-03-02 10:58:26 +0000 |
commit | 538436018b3a896c50be6ebc958bc1d98fabb520 (patch) | |
tree | f57e2c930100bd1f00cba542bab0188debe3214f /libjava | |
parent | 7a128c21e57157d7ab93abba77d2a4f1d7fd985b (diff) | |
download | gcc-538436018b3a896c50be6ebc958bc1d98fabb520.tar.gz |
2007-03-02 Andrew Haley <aph@redhat.com>
* sun/reflect/annotation/AnnotationInvocationHandler.java: Moved
from Classpath to libgcj local.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@122470 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava')
-rw-r--r-- | libjava/sun/reflect/annotation/AnnotationInvocationHandler.java | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/libjava/sun/reflect/annotation/AnnotationInvocationHandler.java b/libjava/sun/reflect/annotation/AnnotationInvocationHandler.java new file mode 100644 index 00000000000..f132d81bad8 --- /dev/null +++ b/libjava/sun/reflect/annotation/AnnotationInvocationHandler.java @@ -0,0 +1,396 @@ +/* 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.Annotation; +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.lang.reflect.Proxy; +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; + } + + public static Annotation create(Class type, Map memberValues) + { + for (Method m : type.getDeclaredMethods()) + { + String name = m.getName(); + if (! memberValues.containsKey(name)) + { + // FIXME: what to do about exceptions here? + memberValues.put(name, m.getDefaultValue()); + } + } + AnnotationInvocationHandler handler + = new AnnotationInvocationHandler(type, memberValues); + return (Annotation) Proxy.newProxyInstance(type.getClassLoader(), + new Class[] { type }, + handler); + } + + /** + * 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; + } + + private Object arrayClone(Object obj) + { + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + + if (obj instanceof char[]) + return ((char[]) obj).clone(); + + if (obj instanceof short[]) + return ((short[]) obj).clone(); + + if (obj instanceof int[]) + return ((int[]) obj).clone(); + + if (obj instanceof float[]) + return ((float[]) obj).clone(); + + if (obj instanceof long[]) + return ((long[]) obj).clone(); + + if (obj instanceof double[]) + return ((double[]) obj).clone(); + + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + + return obj; + } + + 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()); + } + if (val.getClass().isArray()) + { + val = arrayClone(val); + } + return val; + } + } + else if (args.length == 1) + { + if (methodName == "equals") + { + return Boolean.valueOf(equals(type, memberValues, args[0])); + } + } + throw new InternalError("Invalid annotation proxy"); + } +} |