1 /*
   2  * Copyright 2004-2009 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any 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(final Method method, final Attribute attr) {
 254             value = new ExceptionProxy() {
 255                 private static final long serialVersionUID = 8473323277815075163L;
 256                 public String toString() {
 257                     return "<error>";   // eg:  @Anno(value=<error>)
 258                 }
 259                 protected RuntimeException generateException() {
 260                     return new AnnotationTypeMismatchException(method,
 261                                 attr.type.toString());
 262                 }
 263             };
 264         }
 265     }
 266 
 267 
 268     /**
 269      * ExceptionProxy for MirroredTypeException.
 270      * The toString, hashCode, and equals methods foward to the underlying
 271      * type.
 272      */
 273     private static final class MirroredTypeExceptionProxy extends ExceptionProxy {
 274         private static final long serialVersionUID = 6662035281599933545L;
 275 
 276         private MirroredTypeException ex;
 277 
 278         MirroredTypeExceptionProxy(TypeMirror t) {
 279             // It would be safer if we could construct the exception in
 280             // generateException(), but there would be no way to do
 281             // that properly following deserialization.
 282             ex = new MirroredTypeException(t);
 283         }
 284 
 285         public String toString() {
 286             return ex.getQualifiedName();
 287         }
 288 
 289         public int hashCode() {
 290             TypeMirror t = ex.getTypeMirror();
 291             return (t != null)
 292                     ? t.hashCode()
 293                     : ex.getQualifiedName().hashCode();
 294         }
 295 
 296         public boolean equals(Object obj) {
 297             TypeMirror t = ex.getTypeMirror();
 298             return t != null &&
 299                    obj instanceof MirroredTypeExceptionProxy &&
 300                    t.equals(
 301                         ((MirroredTypeExceptionProxy) obj).ex.getTypeMirror());
 302         }
 303 
 304         protected RuntimeException generateException() {
 305             return (RuntimeException) ex.fillInStackTrace();
 306         }
 307     }
 308 
 309 
 310     /**
 311      * ExceptionProxy for MirroredTypesException.
 312      * The toString, hashCode, and equals methods foward to the underlying
 313      * types.
 314      */
 315     private static final class MirroredTypesExceptionProxy extends ExceptionProxy {
 316         private static final long serialVersionUID = -6670822532616693951L;
 317 
 318         private MirroredTypesException ex;
 319 
 320         MirroredTypesExceptionProxy(Collection<TypeMirror> ts) {
 321             // It would be safer if we could construct the exception in
 322             // generateException(), but there would be no way to do
 323             // that properly following deserialization.
 324             ex = new MirroredTypesException(ts);
 325         }
 326 
 327         public String toString() {
 328             return ex.getQualifiedNames().toString();
 329         }
 330 
 331         public int hashCode() {
 332             Collection<TypeMirror> ts = ex.getTypeMirrors();
 333             return (ts != null)
 334                     ? ts.hashCode()
 335                     : ex.getQualifiedNames().hashCode();
 336         }
 337 
 338         public boolean equals(Object obj) {
 339             Collection<TypeMirror> ts = ex.getTypeMirrors();
 340             return ts != null &&
 341                    obj instanceof MirroredTypesExceptionProxy &&
 342                    ts.equals(
 343                       ((MirroredTypesExceptionProxy) obj).ex.getTypeMirrors());
 344         }
 345 
 346         protected RuntimeException generateException() {
 347             return (RuntimeException) ex.fillInStackTrace();
 348         }
 349     }
 350 }