mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			397 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			397 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* 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");
 | |
|     }
 | |
| }
 |