duke@1: /*
ohair@798: * Copyright (c) 2005, 2010, 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:
duke@1: import com.sun.tools.javac.util.*;
darcy@506: import java.io.ObjectInputStream;
darcy@506: import java.io.IOException;
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.TypeMirror;
duke@1: import javax.lang.model.type.MirroredTypeException;
duke@1: import javax.lang.model.type.MirroredTypesException;
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;
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 extends Annotation> annoType;
duke@1:
duke@1:
duke@1: private AnnotationProxyMaker(Attribute.Compound anno,
duke@1: Class extends Annotation> 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) {
duke@1: value = new MirroredTypeExceptionProxy(c.type);
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) {
duke@1: Type elem = ((Attribute.Class) value).type;
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 extends Annotation> 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) {
duke@1: 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.
duke@1: * The toString, hashCode, and equals methods foward 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: }