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 }