duke@1: /* jjg@1645: * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.model; duke@1: jjg@1357: import java.io.IOException; darcy@506: import java.io.ObjectInputStream; duke@1: import java.lang.annotation.*; duke@1: import java.lang.reflect.Array; duke@1: import java.lang.reflect.Method; duke@1: import java.util.LinkedHashMap; duke@1: import java.util.Map; duke@1: import sun.reflect.annotation.*; duke@1: duke@1: import javax.lang.model.type.MirroredTypeException; duke@1: import javax.lang.model.type.MirroredTypesException; jjg@1357: import javax.lang.model.type.TypeMirror; jjg@1357: duke@1: import com.sun.tools.javac.code.*; duke@1: import com.sun.tools.javac.code.Symbol.*; duke@1: import com.sun.tools.javac.code.Type.ArrayType; jjg@1357: import com.sun.tools.javac.util.*; duke@1: duke@1: duke@1: /** duke@1: * A generator of dynamic proxy implementations of duke@1: * java.lang.annotation.Annotation. duke@1: * duke@1: *

The "dynamic proxy return form" of an annotation element value is duke@1: * the form used by sun.reflect.annotation.AnnotationInvocationHandler. duke@1: * jjg@581: *

This is NOT part of any supported API. jjg@581: * If you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: duke@1: public class AnnotationProxyMaker { duke@1: duke@1: private final Attribute.Compound anno; duke@1: private final Class annoType; duke@1: duke@1: duke@1: private AnnotationProxyMaker(Attribute.Compound anno, duke@1: Class annoType) { duke@1: this.anno = anno; duke@1: this.annoType = annoType; duke@1: } duke@1: duke@1: duke@1: /** duke@1: * Returns a dynamic proxy for an annotation mirror. duke@1: */ duke@1: public static A generateAnnotation( duke@1: Attribute.Compound anno, Class annoType) { duke@1: AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType); duke@1: return annoType.cast(apm.generateAnnotation()); duke@1: } duke@1: duke@1: duke@1: /** duke@1: * Returns a dynamic proxy for an annotation mirror. duke@1: */ duke@1: private Annotation generateAnnotation() { duke@1: return AnnotationParser.annotationForMap(annoType, duke@1: getAllReflectedValues()); duke@1: } duke@1: duke@1: /** duke@1: * Returns a map from element names to their values in "dynamic duke@1: * proxy return form". Includes all elements, whether explicit or duke@1: * defaulted. duke@1: */ duke@1: private Map getAllReflectedValues() { duke@1: Map res = new LinkedHashMap(); duke@1: duke@1: for (Map.Entry entry : duke@1: getAllValues().entrySet()) { duke@1: MethodSymbol meth = entry.getKey(); duke@1: Object value = generateValue(meth, entry.getValue()); duke@1: if (value != null) { duke@1: res.put(meth.name.toString(), value); duke@1: } else { duke@1: // Ignore this element. May (properly) lead to duke@1: // IncompleteAnnotationException somewhere down the line. duke@1: } duke@1: } duke@1: return res; duke@1: } duke@1: duke@1: /** duke@1: * Returns a map from element symbols to their values. duke@1: * Includes all elements, whether explicit or defaulted. duke@1: */ duke@1: private Map getAllValues() { duke@1: Map res = duke@1: new LinkedHashMap(); duke@1: duke@1: // First find the default values. duke@1: ClassSymbol sym = (ClassSymbol) anno.type.tsym; duke@1: for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) { duke@1: if (e.sym.kind == Kinds.MTH) { duke@1: MethodSymbol m = (MethodSymbol) e.sym; duke@1: Attribute def = m.getDefaultValue(); duke@1: if (def != null) duke@1: res.put(m, def); duke@1: } duke@1: } duke@1: // Next find the explicit values, possibly overriding defaults. duke@1: for (Pair p : anno.values) duke@1: res.put(p.fst, p.snd); duke@1: return res; duke@1: } duke@1: duke@1: /** duke@1: * Converts an element value to its "dynamic proxy return form". duke@1: * Returns an exception proxy on some errors, but may return null if duke@1: * a useful exception cannot or should not be generated at this point. duke@1: */ duke@1: private Object generateValue(MethodSymbol meth, Attribute attr) { duke@1: ValueVisitor vv = new ValueVisitor(meth); duke@1: return vv.getValue(attr); duke@1: } duke@1: duke@1: duke@1: private class ValueVisitor implements Attribute.Visitor { duke@1: duke@1: private MethodSymbol meth; // annotation element being visited duke@1: private Class returnClass; // return type of annotation element duke@1: private Object value; // value in "dynamic proxy return form" duke@1: duke@1: ValueVisitor(MethodSymbol meth) { duke@1: this.meth = meth; duke@1: } duke@1: duke@1: Object getValue(Attribute attr) { duke@1: Method method; // runtime method of annotation element duke@1: try { duke@1: method = annoType.getMethod(meth.name.toString()); duke@1: } catch (NoSuchMethodException e) { duke@1: return null; duke@1: } duke@1: returnClass = method.getReturnType(); duke@1: attr.accept(this); duke@1: if (!(value instanceof ExceptionProxy) && duke@1: !AnnotationType.invocationHandlerReturnType(returnClass) duke@1: .isInstance(value)) { duke@1: typeMismatch(method, attr); duke@1: } duke@1: return value; duke@1: } duke@1: duke@1: duke@1: public void visitConstant(Attribute.Constant c) { duke@1: value = c.getValue(); duke@1: } duke@1: duke@1: public void visitClass(Attribute.Class c) { jfranck@1313: value = new MirroredTypeExceptionProxy(c.classType); duke@1: } duke@1: duke@1: public void visitArray(Attribute.Array a) { darcy@577: Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName(); duke@1: darcy@577: if (elemName.equals(elemName.table.names.java_lang_Class)) { // Class[] duke@1: // Construct a proxy for a MirroredTypesException darcy@577: ListBuffer elems = new ListBuffer(); duke@1: for (Attribute value : a.values) { jfranck@1313: Type elem = ((Attribute.Class) value).classType; darcy@577: elems.append(elem); duke@1: } darcy@577: value = new MirroredTypesExceptionProxy(elems.toList()); duke@1: duke@1: } else { duke@1: int len = a.values.length; duke@1: Class returnClassSaved = returnClass; duke@1: returnClass = returnClass.getComponentType(); duke@1: try { duke@1: Object res = Array.newInstance(returnClass, len); duke@1: for (int i = 0; i < len; i++) { duke@1: a.values[i].accept(this); duke@1: if (value == null || value instanceof ExceptionProxy) { duke@1: return; duke@1: } duke@1: try { duke@1: Array.set(res, i, value); duke@1: } catch (IllegalArgumentException e) { duke@1: value = null; // indicates a type mismatch duke@1: return; duke@1: } duke@1: } duke@1: value = res; duke@1: } finally { duke@1: returnClass = returnClassSaved; duke@1: } duke@1: } duke@1: } duke@1: mcimadamore@184: @SuppressWarnings({"unchecked", "rawtypes"}) duke@1: public void visitEnum(Attribute.Enum e) { duke@1: if (returnClass.isEnum()) { duke@1: String constName = e.value.toString(); duke@1: try { mcimadamore@184: value = Enum.valueOf((Class)returnClass, constName); duke@1: } catch (IllegalArgumentException ex) { duke@1: value = new EnumConstantNotPresentExceptionProxy( mcimadamore@184: (Class>) returnClass, constName); duke@1: } duke@1: } else { duke@1: value = null; // indicates a type mismatch duke@1: } duke@1: } duke@1: duke@1: public void visitCompound(Attribute.Compound c) { duke@1: try { duke@1: Class nested = duke@1: returnClass.asSubclass(Annotation.class); duke@1: value = generateAnnotation(c, nested); duke@1: } catch (ClassCastException ex) { duke@1: value = null; // indicates a type mismatch duke@1: } duke@1: } duke@1: duke@1: public void visitError(Attribute.Error e) { jfranck@1960: if (e instanceof Attribute.UnresolvedClass) jfranck@1960: value = new MirroredTypeExceptionProxy(((Attribute.UnresolvedClass)e).classType); jfranck@1960: else jfranck@1960: value = null; // indicates a type mismatch duke@1: } duke@1: duke@1: duke@1: /** duke@1: * Sets "value" to an ExceptionProxy indicating a type mismatch. duke@1: */ jjg@713: private void typeMismatch(Method method, final Attribute attr) { jjg@713: class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy { duke@1: static final long serialVersionUID = 269; jjg@713: transient final Method method; jjg@713: AnnotationTypeMismatchExceptionProxy(Method method) { jjg@713: this.method = method; jjg@713: } duke@1: public String toString() { duke@1: return ""; // eg: @Anno(value=) duke@1: } duke@1: protected RuntimeException generateException() { duke@1: return new AnnotationTypeMismatchException(method, duke@1: attr.type.toString()); duke@1: } jjg@713: } jjg@713: value = new AnnotationTypeMismatchExceptionProxy(method); duke@1: } duke@1: } duke@1: duke@1: duke@1: /** duke@1: * ExceptionProxy for MirroredTypeException. jjg@1645: * The toString, hashCode, and equals methods forward to the underlying duke@1: * type. duke@1: */ darcy@506: private static final class MirroredTypeExceptionProxy extends ExceptionProxy { duke@1: static final long serialVersionUID = 269; duke@1: darcy@506: private transient TypeMirror type; duke@1: private final String typeString; duke@1: duke@1: MirroredTypeExceptionProxy(TypeMirror t) { duke@1: type = t; duke@1: typeString = t.toString(); duke@1: } duke@1: duke@1: public String toString() { duke@1: return typeString; duke@1: } duke@1: duke@1: public int hashCode() { duke@1: return (type != null ? type : typeString).hashCode(); duke@1: } duke@1: duke@1: public boolean equals(Object obj) { duke@1: return type != null && duke@1: obj instanceof MirroredTypeExceptionProxy && duke@1: type.equals(((MirroredTypeExceptionProxy) obj).type); duke@1: } duke@1: duke@1: protected RuntimeException generateException() { duke@1: return new MirroredTypeException(type); duke@1: } darcy@506: darcy@506: // Explicitly set all transient fields. darcy@506: private void readObject(ObjectInputStream s) darcy@506: throws IOException, ClassNotFoundException { darcy@506: s.defaultReadObject(); darcy@506: type = null; darcy@506: } duke@1: } duke@1: duke@1: duke@1: /** duke@1: * ExceptionProxy for MirroredTypesException. duke@1: * The toString, hashCode, and equals methods foward to the underlying duke@1: * types. duke@1: */ darcy@506: private static final class MirroredTypesExceptionProxy extends ExceptionProxy { duke@1: static final long serialVersionUID = 269; duke@1: darcy@506: private transient List types; duke@1: private final String typeStrings; duke@1: duke@1: MirroredTypesExceptionProxy(List ts) { duke@1: types = ts; duke@1: typeStrings = ts.toString(); duke@1: } duke@1: duke@1: public String toString() { duke@1: return typeStrings; duke@1: } duke@1: duke@1: public int hashCode() { duke@1: return (types != null ? types : typeStrings).hashCode(); duke@1: } duke@1: duke@1: public boolean equals(Object obj) { duke@1: return types != null && duke@1: obj instanceof MirroredTypesExceptionProxy && duke@1: types.equals( duke@1: ((MirroredTypesExceptionProxy) obj).types); duke@1: } duke@1: duke@1: protected RuntimeException generateException() { duke@1: return new MirroredTypesException(types); duke@1: } darcy@506: darcy@506: // Explicitly set all transient fields. darcy@506: private void readObject(ObjectInputStream s) darcy@506: throws IOException, ClassNotFoundException { darcy@506: s.defaultReadObject(); darcy@506: types = null; darcy@506: } duke@1: } duke@1: }