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