363 lines
13 KiB
Java
363 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. 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. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package com.sun.tools.javac.model;
|
|
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.lang.annotation.*;
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.Method;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
import sun.reflect.annotation.*;
|
|
|
|
import javax.lang.model.type.MirroredTypeException;
|
|
import javax.lang.model.type.MirroredTypesException;
|
|
import javax.lang.model.type.TypeMirror;
|
|
|
|
import com.sun.tools.javac.code.*;
|
|
import com.sun.tools.javac.code.Symbol.*;
|
|
import com.sun.tools.javac.code.Type.ArrayType;
|
|
import com.sun.tools.javac.util.*;
|
|
|
|
|
|
/**
|
|
* A generator of dynamic proxy implementations of
|
|
* java.lang.annotation.Annotation.
|
|
*
|
|
* <p> The "dynamic proxy return form" of an annotation element value is
|
|
* the form used by sun.reflect.annotation.AnnotationInvocationHandler.
|
|
*
|
|
* <p><b>This is NOT part of any supported API.
|
|
* If you write code that depends on this, you do so at your own risk.
|
|
* This code and its internal interfaces are subject to change or
|
|
* deletion without notice.</b>
|
|
*/
|
|
|
|
public class AnnotationProxyMaker {
|
|
|
|
private final Attribute.Compound anno;
|
|
private final Class<? extends Annotation> annoType;
|
|
|
|
|
|
private AnnotationProxyMaker(Attribute.Compound anno,
|
|
Class<? extends Annotation> annoType) {
|
|
this.anno = anno;
|
|
this.annoType = annoType;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a dynamic proxy for an annotation mirror.
|
|
*/
|
|
public static <A extends Annotation> A generateAnnotation(
|
|
Attribute.Compound anno, Class<A> annoType) {
|
|
AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, 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<String, Object> getAllReflectedValues() {
|
|
Map<String, Object> res = new LinkedHashMap<String, Object>();
|
|
|
|
for (Map.Entry<MethodSymbol, Attribute> 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 (properly) 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<MethodSymbol, Attribute> getAllValues() {
|
|
Map<MethodSymbol, Attribute> res =
|
|
new LinkedHashMap<MethodSymbol, Attribute>();
|
|
|
|
// First find the default values.
|
|
ClassSymbol sym = (ClassSymbol) anno.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.getDefaultValue();
|
|
if (def != null)
|
|
res.put(m, def);
|
|
}
|
|
}
|
|
// Next find the explicit values, possibly overriding defaults.
|
|
for (Pair<MethodSymbol, Attribute> p : anno.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<?> returnClass; // return 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;
|
|
}
|
|
returnClass = method.getReturnType();
|
|
attr.accept(this);
|
|
if (!(value instanceof ExceptionProxy) &&
|
|
!AnnotationType.invocationHandlerReturnType(returnClass)
|
|
.isInstance(value)) {
|
|
typeMismatch(method, attr);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
public void visitConstant(Attribute.Constant c) {
|
|
value = c.getValue();
|
|
}
|
|
|
|
public void visitClass(Attribute.Class c) {
|
|
value = new MirroredTypeExceptionProxy(c.classType);
|
|
}
|
|
|
|
public void visitArray(Attribute.Array a) {
|
|
Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName();
|
|
|
|
if (elemName.equals(elemName.table.names.java_lang_Class)) { // Class[]
|
|
// Construct a proxy for a MirroredTypesException
|
|
ListBuffer<TypeMirror> elems = new ListBuffer<TypeMirror>();
|
|
for (Attribute value : a.values) {
|
|
Type elem = ((Attribute.Class) value).classType;
|
|
elems.append(elem);
|
|
}
|
|
value = new MirroredTypesExceptionProxy(elems.toList());
|
|
|
|
} else {
|
|
int len = a.values.length;
|
|
Class<?> returnClassSaved = returnClass;
|
|
returnClass = returnClass.getComponentType();
|
|
try {
|
|
Object res = Array.newInstance(returnClass, 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 {
|
|
returnClass = returnClassSaved;
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
public void visitEnum(Attribute.Enum e) {
|
|
if (returnClass.isEnum()) {
|
|
String constName = e.value.toString();
|
|
try {
|
|
value = Enum.valueOf((Class)returnClass, constName);
|
|
} catch (IllegalArgumentException ex) {
|
|
value = new EnumConstantNotPresentExceptionProxy(
|
|
(Class<Enum<?>>) returnClass, constName);
|
|
}
|
|
} else {
|
|
value = null; // indicates a type mismatch
|
|
}
|
|
}
|
|
|
|
public void visitCompound(Attribute.Compound c) {
|
|
try {
|
|
Class<? extends Annotation> nested =
|
|
returnClass.asSubclass(Annotation.class);
|
|
value = generateAnnotation(c, nested);
|
|
} catch (ClassCastException ex) {
|
|
value = null; // indicates a type mismatch
|
|
}
|
|
}
|
|
|
|
public void visitError(Attribute.Error e) {
|
|
if (e instanceof Attribute.UnresolvedClass)
|
|
value = new MirroredTypeExceptionProxy(((Attribute.UnresolvedClass)e).classType);
|
|
else
|
|
value = null; // indicates a type mismatch
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets "value" to an ExceptionProxy indicating a type mismatch.
|
|
*/
|
|
private void typeMismatch(Method method, final Attribute attr) {
|
|
class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy {
|
|
static final long serialVersionUID = 269;
|
|
transient final Method method;
|
|
AnnotationTypeMismatchExceptionProxy(Method method) {
|
|
this.method = method;
|
|
}
|
|
public String toString() {
|
|
return "<error>"; // eg: @Anno(value=<error>)
|
|
}
|
|
protected RuntimeException generateException() {
|
|
return new AnnotationTypeMismatchException(method,
|
|
attr.type.toString());
|
|
}
|
|
}
|
|
value = new AnnotationTypeMismatchExceptionProxy(method);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* ExceptionProxy for MirroredTypeException.
|
|
* The toString, hashCode, and equals methods forward to the underlying
|
|
* type.
|
|
*/
|
|
private static final class MirroredTypeExceptionProxy extends ExceptionProxy {
|
|
static final long serialVersionUID = 269;
|
|
|
|
private transient TypeMirror type;
|
|
private final String typeString;
|
|
|
|
MirroredTypeExceptionProxy(TypeMirror t) {
|
|
type = t;
|
|
typeString = t.toString();
|
|
}
|
|
|
|
public String toString() {
|
|
return typeString;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return (type != null ? type : typeString).hashCode();
|
|
}
|
|
|
|
public boolean equals(Object obj) {
|
|
return type != null &&
|
|
obj instanceof MirroredTypeExceptionProxy &&
|
|
type.equals(((MirroredTypeExceptionProxy) obj).type);
|
|
}
|
|
|
|
protected RuntimeException generateException() {
|
|
return new MirroredTypeException(type);
|
|
}
|
|
|
|
// Explicitly set all transient fields.
|
|
private void readObject(ObjectInputStream s)
|
|
throws IOException, ClassNotFoundException {
|
|
s.defaultReadObject();
|
|
type = null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* ExceptionProxy for MirroredTypesException.
|
|
* The toString, hashCode, and equals methods foward to the underlying
|
|
* types.
|
|
*/
|
|
private static final class MirroredTypesExceptionProxy extends ExceptionProxy {
|
|
static final long serialVersionUID = 269;
|
|
|
|
private transient List<TypeMirror> types;
|
|
private final String typeStrings;
|
|
|
|
MirroredTypesExceptionProxy(List<TypeMirror> ts) {
|
|
types = ts;
|
|
typeStrings = ts.toString();
|
|
}
|
|
|
|
public String toString() {
|
|
return typeStrings;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return (types != null ? types : typeStrings).hashCode();
|
|
}
|
|
|
|
public boolean equals(Object obj) {
|
|
return types != null &&
|
|
obj instanceof MirroredTypesExceptionProxy &&
|
|
types.equals(
|
|
((MirroredTypesExceptionProxy) obj).types);
|
|
}
|
|
|
|
protected RuntimeException generateException() {
|
|
return new MirroredTypesException(types);
|
|
}
|
|
|
|
// Explicitly set all transient fields.
|
|
private void readObject(ObjectInputStream s)
|
|
throws IOException, ClassNotFoundException {
|
|
s.defaultReadObject();
|
|
types = null;
|
|
}
|
|
}
|
|
}
|