001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javac.model;
027:
028: import com.sun.tools.javac.util.*;
029: import java.lang.annotation.*;
030: import java.lang.reflect.Array;
031: import java.lang.reflect.Method;
032: import java.util.LinkedHashMap;
033: import java.util.Map;
034: import sun.reflect.annotation.*;
035:
036: import javax.lang.model.type.TypeMirror;
037: import javax.lang.model.type.MirroredTypeException;
038: import javax.lang.model.type.MirroredTypesException;
039: import com.sun.tools.javac.code.*;
040: import com.sun.tools.javac.code.Symbol.*;
041: import com.sun.tools.javac.code.Type.ArrayType;
042:
043: /**
044: * A generator of dynamic proxy implementations of
045: * java.lang.annotation.Annotation.
046: *
047: * <p> The "dynamic proxy return form" of an annotation element value is
048: * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
049: *
050: * <p><b>This is NOT part of any API supported by Sun Microsystems. If
051: * you write code that depends on this, you do so at your own risk.
052: * This code and its internal interfaces are subject to change or
053: * deletion without notice.</b>
054: */
055:
056: public class AnnotationProxyMaker {
057:
058: private final Attribute.Compound anno;
059: private final Class<? extends Annotation> annoType;
060:
061: private AnnotationProxyMaker(Attribute.Compound anno,
062: Class<? extends Annotation> annoType) {
063: this .anno = anno;
064: this .annoType = annoType;
065: }
066:
067: /**
068: * Returns a dynamic proxy for an annotation mirror.
069: */
070: public static <A extends Annotation> A generateAnnotation(
071: Attribute.Compound anno, Class<A> annoType) {
072: AnnotationProxyMaker apm = new AnnotationProxyMaker(anno,
073: annoType);
074: return annoType.cast(apm.generateAnnotation());
075: }
076:
077: /**
078: * Returns a dynamic proxy for an annotation mirror.
079: */
080: private Annotation generateAnnotation() {
081: return AnnotationParser.annotationForMap(annoType,
082: getAllReflectedValues());
083: }
084:
085: /**
086: * Returns a map from element names to their values in "dynamic
087: * proxy return form". Includes all elements, whether explicit or
088: * defaulted.
089: */
090: private Map<String, Object> getAllReflectedValues() {
091: Map<String, Object> res = new LinkedHashMap<String, Object>();
092:
093: for (Map.Entry<MethodSymbol, Attribute> entry : getAllValues()
094: .entrySet()) {
095: MethodSymbol meth = entry.getKey();
096: Object value = generateValue(meth, entry.getValue());
097: if (value != null) {
098: res.put(meth.name.toString(), value);
099: } else {
100: // Ignore this element. May (properly) lead to
101: // IncompleteAnnotationException somewhere down the line.
102: }
103: }
104: return res;
105: }
106:
107: /**
108: * Returns a map from element symbols to their values.
109: * Includes all elements, whether explicit or defaulted.
110: */
111: private Map<MethodSymbol, Attribute> getAllValues() {
112: Map<MethodSymbol, Attribute> res = new LinkedHashMap<MethodSymbol, Attribute>();
113:
114: // First find the default values.
115: ClassSymbol sym = (ClassSymbol) anno.type.tsym;
116: for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) {
117: if (e.sym.kind == Kinds.MTH) {
118: MethodSymbol m = (MethodSymbol) e.sym;
119: Attribute def = m.getDefaultValue();
120: if (def != null)
121: res.put(m, def);
122: }
123: }
124: // Next find the explicit values, possibly overriding defaults.
125: for (Pair<MethodSymbol, Attribute> p : anno.values)
126: res.put(p.fst, p.snd);
127: return res;
128: }
129:
130: /**
131: * Converts an element value to its "dynamic proxy return form".
132: * Returns an exception proxy on some errors, but may return null if
133: * a useful exception cannot or should not be generated at this point.
134: */
135: private Object generateValue(MethodSymbol meth, Attribute attr) {
136: ValueVisitor vv = new ValueVisitor(meth);
137: return vv.getValue(attr);
138: }
139:
140: private class ValueVisitor implements Attribute.Visitor {
141:
142: private MethodSymbol meth; // annotation element being visited
143: private Class<?> returnClass; // return type of annotation element
144: private Object value; // value in "dynamic proxy return form"
145:
146: ValueVisitor(MethodSymbol meth) {
147: this .meth = meth;
148: }
149:
150: Object getValue(Attribute attr) {
151: Method method; // runtime method of annotation element
152: try {
153: method = annoType.getMethod(meth.name.toString());
154: } catch (NoSuchMethodException e) {
155: return null;
156: }
157: returnClass = method.getReturnType();
158: attr.accept(this );
159: if (!(value instanceof ExceptionProxy)
160: && !AnnotationType.invocationHandlerReturnType(
161: returnClass).isInstance(value)) {
162: typeMismatch(method, attr);
163: }
164: return value;
165: }
166:
167: public void visitConstant(Attribute.Constant c) {
168: value = c.getValue();
169: }
170:
171: public void visitClass(Attribute.Class c) {
172: value = new MirroredTypeExceptionProxy(c.type);
173: }
174:
175: public void visitArray(Attribute.Array a) {
176: Name elemName = ((ArrayType) a.type).elemtype.tsym.name;
177:
178: if (elemName == elemName.table.java_lang_Class) { // Class[]
179: // Construct a proxy for a MirroredTypesException
180: List<TypeMirror> elems = List.nil();
181: for (Attribute value : a.values) {
182: Type elem = ((Attribute.Class) value).type;
183: elems.add(elem);
184: }
185: value = new MirroredTypesExceptionProxy(elems);
186:
187: } else {
188: int len = a.values.length;
189: Class<?> returnClassSaved = returnClass;
190: returnClass = returnClass.getComponentType();
191: try {
192: Object res = Array.newInstance(returnClass, len);
193: for (int i = 0; i < len; i++) {
194: a.values[i].accept(this );
195: if (value == null
196: || value instanceof ExceptionProxy) {
197: return;
198: }
199: try {
200: Array.set(res, i, value);
201: } catch (IllegalArgumentException e) {
202: value = null; // indicates a type mismatch
203: return;
204: }
205: }
206: value = res;
207: } finally {
208: returnClass = returnClassSaved;
209: }
210: }
211: }
212:
213: @SuppressWarnings("unchecked")
214: public void visitEnum(Attribute.Enum e) {
215: if (returnClass.isEnum()) {
216: String constName = e.value.toString();
217: try {
218: value = Enum
219: .valueOf((Class) returnClass, constName);
220: } catch (IllegalArgumentException ex) {
221: value = new EnumConstantNotPresentExceptionProxy(
222: (Class) returnClass, constName);
223: }
224: } else {
225: value = null; // indicates a type mismatch
226: }
227: }
228:
229: public void visitCompound(Attribute.Compound c) {
230: try {
231: Class<? extends Annotation> nested = returnClass
232: .asSubclass(Annotation.class);
233: value = generateAnnotation(c, nested);
234: } catch (ClassCastException ex) {
235: value = null; // indicates a type mismatch
236: }
237: }
238:
239: public void visitError(Attribute.Error e) {
240: value = null; // indicates a type mismatch
241: }
242:
243: /**
244: * Sets "value" to an ExceptionProxy indicating a type mismatch.
245: */
246: private void typeMismatch(final Method method,
247: final Attribute attr) {
248: value = new ExceptionProxy() {
249: static final long serialVersionUID = 269;
250:
251: public String toString() {
252: return "<error>"; // eg: @Anno(value=<error>)
253: }
254:
255: protected RuntimeException generateException() {
256: return new AnnotationTypeMismatchException(method,
257: attr.type.toString());
258: }
259: };
260: }
261: }
262:
263: /**
264: * ExceptionProxy for MirroredTypeException.
265: * The toString, hashCode, and equals methods foward to the underlying
266: * type.
267: */
268: private static class MirroredTypeExceptionProxy extends
269: ExceptionProxy {
270: static final long serialVersionUID = 269;
271:
272: private transient final TypeMirror type;
273: private final String typeString;
274:
275: MirroredTypeExceptionProxy(TypeMirror t) {
276: type = t;
277: typeString = t.toString();
278: }
279:
280: public String toString() {
281: return typeString;
282: }
283:
284: public int hashCode() {
285: return (type != null ? type : typeString).hashCode();
286: }
287:
288: public boolean equals(Object obj) {
289: return type != null
290: && obj instanceof MirroredTypeExceptionProxy
291: && type
292: .equals(((MirroredTypeExceptionProxy) obj).type);
293: }
294:
295: protected RuntimeException generateException() {
296: return new MirroredTypeException(type);
297: }
298: }
299:
300: /**
301: * ExceptionProxy for MirroredTypesException.
302: * The toString, hashCode, and equals methods foward to the underlying
303: * types.
304: */
305: private static class MirroredTypesExceptionProxy extends
306: ExceptionProxy {
307: static final long serialVersionUID = 269;
308:
309: private transient final List<TypeMirror> types;
310: private final String typeStrings;
311:
312: MirroredTypesExceptionProxy(List<TypeMirror> ts) {
313: types = ts;
314: typeStrings = ts.toString();
315: }
316:
317: public String toString() {
318: return typeStrings;
319: }
320:
321: public int hashCode() {
322: return (types != null ? types : typeStrings).hashCode();
323: }
324:
325: public boolean equals(Object obj) {
326: return types != null
327: && obj instanceof MirroredTypesExceptionProxy
328: && types
329: .equals(((MirroredTypesExceptionProxy) obj).types);
330: }
331:
332: protected RuntimeException generateException() {
333: return new MirroredTypesException(types);
334: }
335: }
336: }
|