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