Save This Page
Home » openjdk-7 » sun » reflect » annotation » [javadoc | source]
    1   /*
    2    * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.reflect.annotation;
   27   
   28   import java.lang.annotation;
   29   import java.lang.reflect;
   30   import java.io.Serializable;
   31   import java.util;
   32   import java.lang.annotation;
   33   import java.security.AccessController;
   34   import java.security.PrivilegedAction;
   35   
   36   /**
   37    * InvocationHandler for dynamic proxy implementation of Annotation.
   38    *
   39    * @author  Josh Bloch
   40    * @since   1.5
   41    */
   42   class AnnotationInvocationHandler implements InvocationHandler, Serializable {
   43       private final Class<? extends Annotation> type;
   44       private final Map<String, Object> memberValues;
   45   
   46       AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
   47           this.type = type;
   48           this.memberValues = memberValues;
   49       }
   50   
   51       public Object invoke(Object proxy, Method method, Object[] args) {
   52           String member = method.getName();
   53           Class<?>[] paramTypes = method.getParameterTypes();
   54   
   55           // Handle Object and Annotation methods
   56           if (member.equals("equals") && paramTypes.length == 1 &&
   57               paramTypes[0] == Object.class)
   58               return equalsImpl(args[0]);
   59           assert paramTypes.length == 0;
   60           if (member.equals("toString"))
   61               return toStringImpl();
   62           if (member.equals("hashCode"))
   63               return hashCodeImpl();
   64           if (member.equals("annotationType"))
   65               return type;
   66   
   67           // Handle annotation member accessors
   68           Object result = memberValues.get(member);
   69   
   70           if (result == null)
   71               throw new IncompleteAnnotationException(type, member);
   72   
   73           if (result instanceof ExceptionProxy)
   74               throw ((ExceptionProxy) result).generateException();
   75   
   76           if (result.getClass().isArray() && Array.getLength(result) != 0)
   77               result = cloneArray(result);
   78   
   79           return result;
   80       }
   81   
   82       /**
   83        * This method, which clones its array argument, would not be necessary
   84        * if Cloneable had a public clone method.
   85        */
   86       private Object cloneArray(Object array) {
   87           Class<?> type = array.getClass();
   88   
   89           if (type == byte[].class) {
   90               byte[] byteArray = (byte[])array;
   91               return byteArray.clone();
   92           }
   93           if (type == char[].class) {
   94               char[] charArray = (char[])array;
   95               return charArray.clone();
   96           }
   97           if (type == double[].class) {
   98               double[] doubleArray = (double[])array;
   99               return doubleArray.clone();
  100           }
  101           if (type == float[].class) {
  102               float[] floatArray = (float[])array;
  103               return floatArray.clone();
  104           }
  105           if (type == int[].class) {
  106               int[] intArray = (int[])array;
  107               return intArray.clone();
  108           }
  109           if (type == long[].class) {
  110               long[] longArray = (long[])array;
  111               return longArray.clone();
  112           }
  113           if (type == short[].class) {
  114               short[] shortArray = (short[])array;
  115               return shortArray.clone();
  116           }
  117           if (type == boolean[].class) {
  118               boolean[] booleanArray = (boolean[])array;
  119               return booleanArray.clone();
  120           }
  121   
  122           Object[] objectArray = (Object[])array;
  123           return objectArray.clone();
  124       }
  125   
  126   
  127       /**
  128        * Implementation of dynamicProxy.toString()
  129        */
  130       private String toStringImpl() {
  131           StringBuffer result = new StringBuffer(128);
  132           result.append('@');
  133           result.append(type.getName());
  134           result.append('(');
  135           boolean firstMember = true;
  136           for (Map.Entry<String, Object> e : memberValues.entrySet()) {
  137               if (firstMember)
  138                   firstMember = false;
  139               else
  140                   result.append(", ");
  141   
  142               result.append(e.getKey());
  143               result.append('=');
  144               result.append(memberValueToString(e.getValue()));
  145           }
  146           result.append(')');
  147           return result.toString();
  148       }
  149   
  150       /**
  151        * Translates a member value (in "dynamic proxy return form") into a string
  152        */
  153       private static String memberValueToString(Object value) {
  154           Class<?> type = value.getClass();
  155           if (!type.isArray())    // primitive, string, class, enum const,
  156                                   // or annotation
  157               return value.toString();
  158   
  159           if (type == byte[].class)
  160               return Arrays.toString((byte[]) value);
  161           if (type == char[].class)
  162               return Arrays.toString((char[]) value);
  163           if (type == double[].class)
  164               return Arrays.toString((double[]) value);
  165           if (type == float[].class)
  166               return Arrays.toString((float[]) value);
  167           if (type == int[].class)
  168               return Arrays.toString((int[]) value);
  169           if (type == long[].class)
  170               return Arrays.toString((long[]) value);
  171           if (type == short[].class)
  172               return Arrays.toString((short[]) value);
  173           if (type == boolean[].class)
  174               return Arrays.toString((boolean[]) value);
  175           return Arrays.toString((Object[]) value);
  176       }
  177   
  178       /**
  179        * Implementation of dynamicProxy.equals(Object o)
  180        */
  181       private Boolean equalsImpl(Object o) {
  182           if (o == this)
  183               return true;
  184   
  185           if (!type.isInstance(o))
  186               return false;
  187           for (Method memberMethod : getMemberMethods()) {
  188               String member = memberMethod.getName();
  189               Object ourValue = memberValues.get(member);
  190               Object hisValue = null;
  191               AnnotationInvocationHandler hisHandler = asOneOfUs(o);
  192               if (hisHandler != null) {
  193                   hisValue = hisHandler.memberValues.get(member);
  194               } else {
  195                   try {
  196                       hisValue = memberMethod.invoke(o);
  197                   } catch (InvocationTargetException e) {
  198                       return false;
  199                   } catch (IllegalAccessException e) {
  200                       throw new AssertionError(e);
  201                   }
  202               }
  203               if (!memberValueEquals(ourValue, hisValue))
  204                   return false;
  205           }
  206           return true;
  207       }
  208   
  209       /**
  210        * Returns an object's invocation handler if that object is a dynamic
  211        * proxy with a handler of type AnnotationInvocationHandler.
  212        * Returns null otherwise.
  213        */
  214       private AnnotationInvocationHandler asOneOfUs(Object o) {
  215           if (Proxy.isProxyClass(o.getClass())) {
  216               InvocationHandler handler = Proxy.getInvocationHandler(o);
  217               if (handler instanceof AnnotationInvocationHandler)
  218                   return (AnnotationInvocationHandler) handler;
  219           }
  220           return null;
  221       }
  222   
  223       /**
  224        * Returns true iff the two member values in "dynamic proxy return form"
  225        * are equal using the appropriate equality function depending on the
  226        * member type.  The two values will be of the same type unless one of
  227        * the containing annotations is ill-formed.  If one of the containing
  228        * annotations is ill-formed, this method will return false unless the
  229        * two members are identical object references.
  230        */
  231       private static boolean memberValueEquals(Object v1, Object v2) {
  232           Class<?> type = v1.getClass();
  233   
  234           // Check for primitive, string, class, enum const, annotation,
  235           // or ExceptionProxy
  236           if (!type.isArray())
  237               return v1.equals(v2);
  238   
  239           // Check for array of string, class, enum const, annotation,
  240           // or ExceptionProxy
  241           if (v1 instanceof Object[] && v2 instanceof Object[])
  242               return Arrays.equals((Object[]) v1, (Object[]) v2);
  243   
  244           // Check for ill formed annotation(s)
  245           if (v2.getClass() != type)
  246               return false;
  247   
  248           // Deal with array of primitives
  249           if (type == byte[].class)
  250               return Arrays.equals((byte[]) v1, (byte[]) v2);
  251           if (type == char[].class)
  252               return Arrays.equals((char[]) v1, (char[]) v2);
  253           if (type == double[].class)
  254               return Arrays.equals((double[]) v1, (double[]) v2);
  255           if (type == float[].class)
  256               return Arrays.equals((float[]) v1, (float[]) v2);
  257           if (type == int[].class)
  258               return Arrays.equals((int[]) v1, (int[]) v2);
  259           if (type == long[].class)
  260               return Arrays.equals((long[]) v1, (long[]) v2);
  261           if (type == short[].class)
  262               return Arrays.equals((short[]) v1, (short[]) v2);
  263           assert type == boolean[].class;
  264           return Arrays.equals((boolean[]) v1, (boolean[]) v2);
  265       }
  266   
  267       /**
  268        * Returns the member methods for our annotation type.  These are
  269        * obtained lazily and cached, as they're expensive to obtain
  270        * and we only need them if our equals method is invoked (which should
  271        * be rare).
  272        */
  273       private Method[] getMemberMethods() {
  274           if (memberMethods == null) {
  275               memberMethods = AccessController.doPrivileged(
  276                   new PrivilegedAction<Method[]>() {
  277                       public Method[] run() {
  278                           final Method[] mm = type.getDeclaredMethods();
  279                           AccessibleObject.setAccessible(mm, true);
  280                           return mm;
  281                       }
  282                   });
  283           }
  284           return memberMethods;
  285       }
  286       private transient volatile Method[] memberMethods = null;
  287   
  288       /**
  289        * Implementation of dynamicProxy.hashCode()
  290        */
  291       private int hashCodeImpl() {
  292           int result = 0;
  293           for (Map.Entry<String, Object> e : memberValues.entrySet()) {
  294               result += (127 * e.getKey().hashCode()) ^
  295                   memberValueHashCode(e.getValue());
  296           }
  297           return result;
  298       }
  299   
  300       /**
  301        * Computes hashCode of a member value (in "dynamic proxy return form")
  302        */
  303       private static int memberValueHashCode(Object value) {
  304           Class<?> type = value.getClass();
  305           if (!type.isArray())    // primitive, string, class, enum const,
  306                                   // or annotation
  307               return value.hashCode();
  308   
  309           if (type == byte[].class)
  310               return Arrays.hashCode((byte[]) value);
  311           if (type == char[].class)
  312               return Arrays.hashCode((char[]) value);
  313           if (type == double[].class)
  314               return Arrays.hashCode((double[]) value);
  315           if (type == float[].class)
  316               return Arrays.hashCode((float[]) value);
  317           if (type == int[].class)
  318               return Arrays.hashCode((int[]) value);
  319           if (type == long[].class)
  320               return Arrays.hashCode((long[]) value);
  321           if (type == short[].class)
  322               return Arrays.hashCode((short[]) value);
  323           if (type == boolean[].class)
  324               return Arrays.hashCode((boolean[]) value);
  325           return Arrays.hashCode((Object[]) value);
  326       }
  327   
  328       private void readObject(java.io.ObjectInputStream s)
  329           throws java.io.IOException, ClassNotFoundException {
  330           s.defaultReadObject();
  331   
  332   
  333           // Check to make sure that types have not evolved incompatibly
  334   
  335           AnnotationType annotationType = null;
  336           try {
  337               annotationType = AnnotationType.getInstance(type);
  338           } catch(IllegalArgumentException e) {
  339               // Class is no longer an annotation type; all bets are off
  340               return;
  341           }
  342   
  343           Map<String, Class<?>> memberTypes = annotationType.memberTypes();
  344   
  345           for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
  346               String name = memberValue.getKey();
  347               Class<?> memberType = memberTypes.get(name);
  348               if (memberType != null) {  // i.e. member still exists
  349                   Object value = memberValue.getValue();
  350                   if (!(memberType.isInstance(value) ||
  351                         value instanceof ExceptionProxy)) {
  352                       memberValue.setValue(
  353                           new AnnotationTypeMismatchExceptionProxy(
  354                               value.getClass() + "[" + value + "]").setMember(
  355                                   annotationType.members().get(name)));
  356                   }
  357               }
  358           }
  359       }
  360   }

Save This Page
Home » openjdk-7 » sun » reflect » annotation » [javadoc | source]