/* * Copyright 2004-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.tools.apt.mirror.declaration; import java.lang.annotation.*; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.*; import sun.reflect.annotation.*; import com.sun.mirror.type.TypeMirror; import com.sun.mirror.type.MirroredTypeException; import com.sun.mirror.type.MirroredTypesException; import com.sun.tools.apt.mirror.AptEnv; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Pair; /** * A generator of dynamic proxy implementations of * java.lang.annotation.Annotation. * *

The "dynamic proxy return form" of an attribute element value is * the form used by sun.reflect.annotation.AnnotationInvocationHandler. */ @SuppressWarnings("deprecation") class AnnotationProxyMaker { private final AptEnv env; private final Attribute.Compound attrs; private final Class annoType; private AnnotationProxyMaker(AptEnv env, Attribute.Compound attrs, Class annoType) { this.env = env; this.attrs = attrs; this.annoType = annoType; } /** * Returns a dynamic proxy for an annotation mirror. */ public static A generateAnnotation( AptEnv env, Attribute.Compound attrs, Class annoType) { AnnotationProxyMaker apm = new AnnotationProxyMaker(env, attrs, annoType); return annoType.cast(apm.generateAnnotation()); } /** * Returns a dynamic proxy for an annotation mirror. */ private Annotation generateAnnotation() { return AnnotationParser.annotationForMap(annoType, getAllReflectedValues()); } /** * Returns a map from element names to their values in "dynamic * proxy return form". Includes all elements, whether explicit or * defaulted. */ private Map getAllReflectedValues() { Map res = new LinkedHashMap(); for (Map.Entry entry : getAllValues().entrySet()) { MethodSymbol meth = entry.getKey(); Object value = generateValue(meth, entry.getValue()); if (value != null) { res.put(meth.name.toString(), value); } else { // Ignore this element. May lead to // IncompleteAnnotationException somewhere down the line. } } return res; } /** * Returns a map from element symbols to their values. * Includes all elements, whether explicit or defaulted. */ private Map getAllValues() { Map res = new LinkedHashMap(); // First find the default values. ClassSymbol sym = (ClassSymbol) attrs.type.tsym; for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) { if (e.sym.kind == Kinds.MTH) { MethodSymbol m = (MethodSymbol) e.sym; Attribute def = m.defaultValue; if (def != null) { res.put(m, def); } } } // Next find the explicit values, possibly overriding defaults. for (Pair p : attrs.values) { res.put(p.fst, p.snd); } return res; } /** * Converts an element value to its "dynamic proxy return form". * Returns an exception proxy on some errors, but may return null if * a useful exception cannot or should not be generated at this point. */ private Object generateValue(MethodSymbol meth, Attribute attr) { ValueVisitor vv = new ValueVisitor(meth); return vv.getValue(attr); } private class ValueVisitor implements Attribute.Visitor { private MethodSymbol meth; // annotation element being visited private Class runtimeType; // runtime type of annotation element private Object value; // value in "dynamic proxy return form" ValueVisitor(MethodSymbol meth) { this.meth = meth; } Object getValue(Attribute attr) { Method method; // runtime method of annotation element try { method = annoType.getMethod(meth.name.toString()); } catch (NoSuchMethodException e) { return null; } runtimeType = method.getReturnType(); attr.accept(this); if (!(value instanceof ExceptionProxy) && !AnnotationType.invocationHandlerReturnType(runtimeType) .isInstance(value)) { typeMismatch(method, attr); } return value; } public void visitConstant(Attribute.Constant c) { value = Constants.decodeConstant(c.value, c.type); } public void visitClass(Attribute.Class c) { value = new MirroredTypeExceptionProxy( env.typeMaker.getType(c.type)); } public void visitArray(Attribute.Array a) { Type elemtype = env.jctypes.elemtype(a.type); if (elemtype.tsym == env.symtab.classType.tsym) { // Class[] // Construct a proxy for a MirroredTypesException ArrayList elems = new ArrayList(); for (int i = 0; i < a.values.length; i++) { Type elem = ((Attribute.Class) a.values[i]).type; elems.add(env.typeMaker.getType(elem)); } value = new MirroredTypesExceptionProxy(elems); } else { int len = a.values.length; Class runtimeTypeSaved = runtimeType; runtimeType = runtimeType.getComponentType(); try { Object res = Array.newInstance(runtimeType, len); for (int i = 0; i < len; i++) { a.values[i].accept(this); if (value == null || value instanceof ExceptionProxy) { return; } try { Array.set(res, i, value); } catch (IllegalArgumentException e) { value = null; // indicates a type mismatch return; } } value = res; } finally { runtimeType = runtimeTypeSaved; } } } @SuppressWarnings({"unchecked", "rawtypes"}) public void visitEnum(Attribute.Enum e) { if (runtimeType.isEnum()) { String constName = e.value.toString(); try { value = Enum.valueOf((Class)runtimeType, constName); } catch (IllegalArgumentException ex) { value = new EnumConstantNotPresentExceptionProxy( (Class>)runtimeType, constName); } } else { value = null; // indicates a type mismatch } } public void visitCompound(Attribute.Compound c) { try { Class nested = runtimeType.asSubclass(Annotation.class); value = generateAnnotation(env, c, nested); } catch (ClassCastException ex) { value = null; // indicates a type mismatch } } public void visitError(Attribute.Error e) { value = null; // indicates a type mismatch } /** * Sets "value" to an ExceptionProxy indicating a type mismatch. */ private void typeMismatch(final Method method, final Attribute attr) { value = new ExceptionProxy() { private static final long serialVersionUID = 8473323277815075163L; public String toString() { return ""; // eg: @Anno(value=) } protected RuntimeException generateException() { return new AnnotationTypeMismatchException(method, attr.type.toString()); } }; } } /** * ExceptionProxy for MirroredTypeException. * The toString, hashCode, and equals methods foward to the underlying * type. */ private static final class MirroredTypeExceptionProxy extends ExceptionProxy { private static final long serialVersionUID = 6662035281599933545L; private MirroredTypeException ex; MirroredTypeExceptionProxy(TypeMirror t) { // It would be safer if we could construct the exception in // generateException(), but there would be no way to do // that properly following deserialization. ex = new MirroredTypeException(t); } public String toString() { return ex.getQualifiedName(); } public int hashCode() { TypeMirror t = ex.getTypeMirror(); return (t != null) ? t.hashCode() : ex.getQualifiedName().hashCode(); } public boolean equals(Object obj) { TypeMirror t = ex.getTypeMirror(); return t != null && obj instanceof MirroredTypeExceptionProxy && t.equals( ((MirroredTypeExceptionProxy) obj).ex.getTypeMirror()); } protected RuntimeException generateException() { return (RuntimeException) ex.fillInStackTrace(); } } /** * ExceptionProxy for MirroredTypesException. * The toString, hashCode, and equals methods foward to the underlying * types. */ private static final class MirroredTypesExceptionProxy extends ExceptionProxy { private static final long serialVersionUID = -6670822532616693951L; private MirroredTypesException ex; MirroredTypesExceptionProxy(Collection ts) { // It would be safer if we could construct the exception in // generateException(), but there would be no way to do // that properly following deserialization. ex = new MirroredTypesException(ts); } public String toString() { return ex.getQualifiedNames().toString(); } public int hashCode() { Collection ts = ex.getTypeMirrors(); return (ts != null) ? ts.hashCode() : ex.getQualifiedNames().hashCode(); } public boolean equals(Object obj) { Collection ts = ex.getTypeMirrors(); return ts != null && obj instanceof MirroredTypesExceptionProxy && ts.equals( ((MirroredTypesExceptionProxy) obj).ex.getTypeMirrors()); } protected RuntimeException generateException() { return (RuntimeException) ex.fillInStackTrace(); } } }