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