langtools/src/share/classes/com/sun/tools/apt/mirror/declaration/AnnotationProxyMaker.java
changeset 11870 bc664cc5f2a0
parent 11863 ac6277ec304a
parent 11869 d659025e6575
child 11871 08f8da764f8f
equal deleted inserted replaced
11863:ac6277ec304a 11870:bc664cc5f2a0
     1 /*
       
     2  * Copyright (c) 2004, 2010, 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 com.sun.tools.apt.mirror.declaration;
       
    27 
       
    28 
       
    29 import java.lang.annotation.*;
       
    30 import java.lang.reflect.Array;
       
    31 import java.lang.reflect.Method;
       
    32 import java.util.*;
       
    33 import sun.reflect.annotation.*;
       
    34 
       
    35 import com.sun.mirror.type.TypeMirror;
       
    36 import com.sun.mirror.type.MirroredTypeException;
       
    37 import com.sun.mirror.type.MirroredTypesException;
       
    38 import com.sun.tools.apt.mirror.AptEnv;
       
    39 import com.sun.tools.javac.code.*;
       
    40 import com.sun.tools.javac.code.Symbol.*;
       
    41 import com.sun.tools.javac.util.Name;
       
    42 import com.sun.tools.javac.util.Pair;
       
    43 
       
    44 
       
    45 /**
       
    46  * A generator of dynamic proxy implementations of
       
    47  * java.lang.annotation.Annotation.
       
    48  *
       
    49  * <p> The "dynamic proxy return form" of an attribute element value is
       
    50  * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
       
    51  */
       
    52 @SuppressWarnings("deprecation")
       
    53 class AnnotationProxyMaker {
       
    54 
       
    55     private final AptEnv env;
       
    56     private final Attribute.Compound attrs;
       
    57     private final Class<? extends Annotation> annoType;
       
    58 
       
    59 
       
    60     private AnnotationProxyMaker(AptEnv env,
       
    61                                  Attribute.Compound attrs,
       
    62                                  Class<? extends Annotation> annoType) {
       
    63         this.env = env;
       
    64         this.attrs = attrs;
       
    65         this.annoType = annoType;
       
    66     }
       
    67 
       
    68 
       
    69     /**
       
    70      * Returns a dynamic proxy for an annotation mirror.
       
    71      */
       
    72     public static <A extends Annotation> A generateAnnotation(
       
    73             AptEnv env, Attribute.Compound attrs, Class<A> annoType) {
       
    74         AnnotationProxyMaker apm = new AnnotationProxyMaker(env, attrs, annoType);
       
    75         return annoType.cast(apm.generateAnnotation());
       
    76     }
       
    77 
       
    78 
       
    79     /**
       
    80      * Returns a dynamic proxy for an annotation mirror.
       
    81      */
       
    82     private Annotation generateAnnotation() {
       
    83         return AnnotationParser.annotationForMap(annoType,
       
    84                                                  getAllReflectedValues());
       
    85     }
       
    86 
       
    87     /**
       
    88      * Returns a map from element names to their values in "dynamic
       
    89      * proxy return form".  Includes all elements, whether explicit or
       
    90      * defaulted.
       
    91      */
       
    92     private Map<String, Object> getAllReflectedValues() {
       
    93         Map<String, Object> res = new LinkedHashMap<String, Object>();
       
    94 
       
    95         for (Map.Entry<MethodSymbol, Attribute> entry :
       
    96                                                   getAllValues().entrySet()) {
       
    97             MethodSymbol meth = entry.getKey();
       
    98             Object value = generateValue(meth, entry.getValue());
       
    99             if (value != null) {
       
   100                 res.put(meth.name.toString(), value);
       
   101             } else {
       
   102                 // Ignore this element.  May lead to
       
   103                 // IncompleteAnnotationException somewhere down the line.
       
   104             }
       
   105         }
       
   106         return res;
       
   107     }
       
   108 
       
   109     /**
       
   110      * Returns a map from element symbols to their values.
       
   111      * Includes all elements, whether explicit or defaulted.
       
   112      */
       
   113     private Map<MethodSymbol, Attribute> getAllValues() {
       
   114         Map<MethodSymbol, Attribute> res =
       
   115             new LinkedHashMap<MethodSymbol, Attribute>();
       
   116 
       
   117         // First find the default values.
       
   118         ClassSymbol sym = (ClassSymbol) attrs.type.tsym;
       
   119         for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) {
       
   120             if (e.sym.kind == Kinds.MTH) {
       
   121                 MethodSymbol m = (MethodSymbol) e.sym;
       
   122                 Attribute def = m.defaultValue;
       
   123                 if (def != null) {
       
   124                     res.put(m, def);
       
   125                 }
       
   126             }
       
   127         }
       
   128         // Next find the explicit values, possibly overriding defaults.
       
   129         for (Pair<MethodSymbol, Attribute> p : attrs.values) {
       
   130             res.put(p.fst, p.snd);
       
   131         }
       
   132         return res;
       
   133     }
       
   134 
       
   135     /**
       
   136      * Converts an element value to its "dynamic proxy return form".
       
   137      * Returns an exception proxy on some errors, but may return null if
       
   138      * a useful exception cannot or should not be generated at this point.
       
   139      */
       
   140     private Object generateValue(MethodSymbol meth, Attribute attr) {
       
   141         ValueVisitor vv = new ValueVisitor(meth);
       
   142         return vv.getValue(attr);
       
   143     }
       
   144 
       
   145 
       
   146     private class ValueVisitor implements Attribute.Visitor {
       
   147 
       
   148         private MethodSymbol meth;      // annotation element being visited
       
   149         private Class<?> runtimeType;   // runtime type of annotation element
       
   150         private Object value;           // value in "dynamic proxy return form"
       
   151 
       
   152         ValueVisitor(MethodSymbol meth) {
       
   153             this.meth = meth;
       
   154         }
       
   155 
       
   156         Object getValue(Attribute attr) {
       
   157             Method method;              // runtime method of annotation element
       
   158             try {
       
   159                 method = annoType.getMethod(meth.name.toString());
       
   160             } catch (NoSuchMethodException e) {
       
   161                 return null;
       
   162             }
       
   163             runtimeType = method.getReturnType();
       
   164             attr.accept(this);
       
   165             if (!(value instanceof ExceptionProxy) &&
       
   166                 !AnnotationType.invocationHandlerReturnType(runtimeType)
       
   167                                                         .isInstance(value)) {
       
   168                 typeMismatch(method, attr);
       
   169             }
       
   170             return value;
       
   171         }
       
   172 
       
   173 
       
   174         public void visitConstant(Attribute.Constant c) {
       
   175             value = Constants.decodeConstant(c.value, c.type);
       
   176         }
       
   177 
       
   178         public void visitClass(Attribute.Class c) {
       
   179             value = new MirroredTypeExceptionProxy(
       
   180                                 env.typeMaker.getType(c.type));
       
   181         }
       
   182 
       
   183         public void visitArray(Attribute.Array a) {
       
   184             Type elemtype = env.jctypes.elemtype(a.type);
       
   185 
       
   186             if (elemtype.tsym == env.symtab.classType.tsym) {   // Class[]
       
   187                 // Construct a proxy for a MirroredTypesException
       
   188                 ArrayList<TypeMirror> elems = new ArrayList<TypeMirror>();
       
   189                 for (int i = 0; i < a.values.length; i++) {
       
   190                     Type elem = ((Attribute.Class) a.values[i]).type;
       
   191                     elems.add(env.typeMaker.getType(elem));
       
   192                 }
       
   193                 value = new MirroredTypesExceptionProxy(elems);
       
   194 
       
   195             } else {
       
   196                 int len = a.values.length;
       
   197                 Class<?> runtimeTypeSaved = runtimeType;
       
   198                 runtimeType = runtimeType.getComponentType();
       
   199                 try {
       
   200                     Object res = Array.newInstance(runtimeType, len);
       
   201                     for (int i = 0; i < len; i++) {
       
   202                         a.values[i].accept(this);
       
   203                         if (value == null || value instanceof ExceptionProxy) {
       
   204                             return;
       
   205                         }
       
   206                         try {
       
   207                             Array.set(res, i, value);
       
   208                         } catch (IllegalArgumentException e) {
       
   209                             value = null;       // indicates a type mismatch
       
   210                             return;
       
   211                         }
       
   212                     }
       
   213                     value = res;
       
   214                 } finally {
       
   215                     runtimeType = runtimeTypeSaved;
       
   216                 }
       
   217             }
       
   218         }
       
   219 
       
   220         @SuppressWarnings({"unchecked", "rawtypes"})
       
   221         public void visitEnum(Attribute.Enum e) {
       
   222             if (runtimeType.isEnum()) {
       
   223                 String constName = e.value.toString();
       
   224                 try {
       
   225                     value = Enum.valueOf((Class)runtimeType, constName);
       
   226                 } catch (IllegalArgumentException ex) {
       
   227                     value = new EnumConstantNotPresentExceptionProxy(
       
   228                                                         (Class<Enum<?>>)runtimeType, constName);
       
   229                 }
       
   230             } else {
       
   231                 value = null;   // indicates a type mismatch
       
   232             }
       
   233         }
       
   234 
       
   235         public void visitCompound(Attribute.Compound c) {
       
   236             try {
       
   237                 Class<? extends Annotation> nested =
       
   238                     runtimeType.asSubclass(Annotation.class);
       
   239                 value = generateAnnotation(env, c, nested);
       
   240             } catch (ClassCastException ex) {
       
   241                 value = null;   // indicates a type mismatch
       
   242             }
       
   243         }
       
   244 
       
   245         public void visitError(Attribute.Error e) {
       
   246             value = null;       // indicates a type mismatch
       
   247         }
       
   248 
       
   249 
       
   250         /**
       
   251          * Sets "value" to an ExceptionProxy indicating a type mismatch.
       
   252          */
       
   253         private void typeMismatch(Method method, final Attribute attr) {
       
   254             class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy {
       
   255                 private static final long serialVersionUID = 8473323277815075163L;
       
   256                 transient final Method method;
       
   257                 AnnotationTypeMismatchExceptionProxy(Method method) {
       
   258                     this.method = method;
       
   259                 }
       
   260                 public String toString() {
       
   261                     return "<error>";   // eg:  @Anno(value=<error>)
       
   262                 }
       
   263                 protected RuntimeException generateException() {
       
   264                     return new AnnotationTypeMismatchException(method,
       
   265                                 attr.type.toString());
       
   266                 }
       
   267             }
       
   268             value = new AnnotationTypeMismatchExceptionProxy(method);
       
   269         }
       
   270     }
       
   271 
       
   272 
       
   273     /**
       
   274      * ExceptionProxy for MirroredTypeException.
       
   275      * The toString, hashCode, and equals methods foward to the underlying
       
   276      * type.
       
   277      */
       
   278     private static final class MirroredTypeExceptionProxy extends ExceptionProxy {
       
   279         private static final long serialVersionUID = 6662035281599933545L;
       
   280 
       
   281         private MirroredTypeException ex;
       
   282 
       
   283         MirroredTypeExceptionProxy(TypeMirror t) {
       
   284             // It would be safer if we could construct the exception in
       
   285             // generateException(), but there would be no way to do
       
   286             // that properly following deserialization.
       
   287             ex = new MirroredTypeException(t);
       
   288         }
       
   289 
       
   290         public String toString() {
       
   291             return ex.getQualifiedName();
       
   292         }
       
   293 
       
   294         public int hashCode() {
       
   295             TypeMirror t = ex.getTypeMirror();
       
   296             return (t != null)
       
   297                     ? t.hashCode()
       
   298                     : ex.getQualifiedName().hashCode();
       
   299         }
       
   300 
       
   301         public boolean equals(Object obj) {
       
   302             TypeMirror t = ex.getTypeMirror();
       
   303             return t != null &&
       
   304                    obj instanceof MirroredTypeExceptionProxy &&
       
   305                    t.equals(
       
   306                         ((MirroredTypeExceptionProxy) obj).ex.getTypeMirror());
       
   307         }
       
   308 
       
   309         protected RuntimeException generateException() {
       
   310             return (RuntimeException) ex.fillInStackTrace();
       
   311         }
       
   312     }
       
   313 
       
   314 
       
   315     /**
       
   316      * ExceptionProxy for MirroredTypesException.
       
   317      * The toString, hashCode, and equals methods foward to the underlying
       
   318      * types.
       
   319      */
       
   320     private static final class MirroredTypesExceptionProxy extends ExceptionProxy {
       
   321         private static final long serialVersionUID = -6670822532616693951L;
       
   322 
       
   323         private MirroredTypesException ex;
       
   324 
       
   325         MirroredTypesExceptionProxy(Collection<TypeMirror> ts) {
       
   326             // It would be safer if we could construct the exception in
       
   327             // generateException(), but there would be no way to do
       
   328             // that properly following deserialization.
       
   329             ex = new MirroredTypesException(ts);
       
   330         }
       
   331 
       
   332         public String toString() {
       
   333             return ex.getQualifiedNames().toString();
       
   334         }
       
   335 
       
   336         public int hashCode() {
       
   337             Collection<TypeMirror> ts = ex.getTypeMirrors();
       
   338             return (ts != null)
       
   339                     ? ts.hashCode()
       
   340                     : ex.getQualifiedNames().hashCode();
       
   341         }
       
   342 
       
   343         public boolean equals(Object obj) {
       
   344             Collection<TypeMirror> ts = ex.getTypeMirrors();
       
   345             return ts != null &&
       
   346                    obj instanceof MirroredTypesExceptionProxy &&
       
   347                    ts.equals(
       
   348                       ((MirroredTypesExceptionProxy) obj).ex.getTypeMirrors());
       
   349         }
       
   350 
       
   351         protected RuntimeException generateException() {
       
   352             return (RuntimeException) ex.fillInStackTrace();
       
   353         }
       
   354     }
       
   355 }