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