1 | /* |
2 | * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Sun designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Sun in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
23 | * have any questions. |
24 | */ |
25 | |
26 | package com.sun.tools.javac.model; |
27 | |
28 | import com.sun.tools.javac.util.*; |
29 | import java.lang.annotation.*; |
30 | import java.lang.reflect.Array; |
31 | import java.lang.reflect.Method; |
32 | import java.util.LinkedHashMap; |
33 | import java.util.Map; |
34 | import sun.reflect.annotation.*; |
35 | |
36 | import javax.lang.model.type.TypeMirror; |
37 | import javax.lang.model.type.MirroredTypeException; |
38 | import javax.lang.model.type.MirroredTypesException; |
39 | import com.sun.tools.javac.code.*; |
40 | import com.sun.tools.javac.code.Symbol.*; |
41 | import com.sun.tools.javac.code.Type.ArrayType; |
42 | |
43 | |
44 | /** |
45 | * A generator of dynamic proxy implementations of |
46 | * java.lang.annotation.Annotation. |
47 | * |
48 | * <p> The "dynamic proxy return form" of an annotation element value is |
49 | * the form used by sun.reflect.annotation.AnnotationInvocationHandler. |
50 | * |
51 | * <p><b>This is NOT part of any API supported by Sun Microsystems. If |
52 | * you write code that depends on this, you do so at your own risk. |
53 | * This code and its internal interfaces are subject to change or |
54 | * deletion without notice.</b> |
55 | */ |
56 | |
57 | public class AnnotationProxyMaker { |
58 | |
59 | private final Attribute.Compound anno; |
60 | private final Class<? extends Annotation> annoType; |
61 | |
62 | |
63 | private AnnotationProxyMaker(Attribute.Compound anno, |
64 | Class<? extends Annotation> annoType) { |
65 | this.anno = anno; |
66 | this.annoType = annoType; |
67 | } |
68 | |
69 | |
70 | /** |
71 | * Returns a dynamic proxy for an annotation mirror. |
72 | */ |
73 | public static <A extends Annotation> A generateAnnotation( |
74 | Attribute.Compound anno, Class<A> annoType) { |
75 | AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType); |
76 | return annoType.cast(apm.generateAnnotation()); |
77 | } |
78 | |
79 | |
80 | /** |
81 | * Returns a dynamic proxy for an annotation mirror. |
82 | */ |
83 | private Annotation generateAnnotation() { |
84 | return AnnotationParser.annotationForMap(annoType, |
85 | getAllReflectedValues()); |
86 | } |
87 | |
88 | /** |
89 | * Returns a map from element names to their values in "dynamic |
90 | * proxy return form". Includes all elements, whether explicit or |
91 | * defaulted. |
92 | */ |
93 | private Map<String, Object> getAllReflectedValues() { |
94 | Map<String, Object> res = new LinkedHashMap<String, Object>(); |
95 | |
96 | for (Map.Entry<MethodSymbol, Attribute> entry : |
97 | getAllValues().entrySet()) { |
98 | MethodSymbol meth = entry.getKey(); |
99 | Object value = generateValue(meth, entry.getValue()); |
100 | if (value != null) { |
101 | res.put(meth.name.toString(), value); |
102 | } else { |
103 | // Ignore this element. May (properly) lead to |
104 | // IncompleteAnnotationException somewhere down the line. |
105 | } |
106 | } |
107 | return res; |
108 | } |
109 | |
110 | /** |
111 | * Returns a map from element symbols to their values. |
112 | * Includes all elements, whether explicit or defaulted. |
113 | */ |
114 | private Map<MethodSymbol, Attribute> getAllValues() { |
115 | Map<MethodSymbol, Attribute> res = |
116 | new LinkedHashMap<MethodSymbol, Attribute>(); |
117 | |
118 | // First find the default values. |
119 | ClassSymbol sym = (ClassSymbol) anno.type.tsym; |
120 | for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) { |
121 | if (e.sym.kind == Kinds.MTH) { |
122 | MethodSymbol m = (MethodSymbol) e.sym; |
123 | Attribute def = m.getDefaultValue(); |
124 | if (def != null) |
125 | res.put(m, def); |
126 | } |
127 | } |
128 | // Next find the explicit values, possibly overriding defaults. |
129 | for (Pair<MethodSymbol, Attribute> p : anno.values) |
130 | res.put(p.fst, p.snd); |
131 | return res; |
132 | } |
133 | |
134 | /** |
135 | * Converts an element value to its "dynamic proxy return form". |
136 | * Returns an exception proxy on some errors, but may return null if |
137 | * a useful exception cannot or should not be generated at this point. |
138 | */ |
139 | private Object generateValue(MethodSymbol meth, Attribute attr) { |
140 | ValueVisitor vv = new ValueVisitor(meth); |
141 | return vv.getValue(attr); |
142 | } |
143 | |
144 | |
145 | private class ValueVisitor implements Attribute.Visitor { |
146 | |
147 | private MethodSymbol meth; // annotation element being visited |
148 | private Class<?> returnClass; // return type of annotation element |
149 | private Object value; // value in "dynamic proxy return form" |
150 | |
151 | ValueVisitor(MethodSymbol meth) { |
152 | this.meth = meth; |
153 | } |
154 | |
155 | Object getValue(Attribute attr) { |
156 | Method method; // runtime method of annotation element |
157 | try { |
158 | method = annoType.getMethod(meth.name.toString()); |
159 | } catch (NoSuchMethodException e) { |
160 | return null; |
161 | } |
162 | returnClass = method.getReturnType(); |
163 | attr.accept(this); |
164 | if (!(value instanceof ExceptionProxy) && |
165 | !AnnotationType.invocationHandlerReturnType(returnClass) |
166 | .isInstance(value)) { |
167 | typeMismatch(method, attr); |
168 | } |
169 | return value; |
170 | } |
171 | |
172 | |
173 | public void visitConstant(Attribute.Constant c) { |
174 | value = c.getValue(); |
175 | } |
176 | |
177 | public void visitClass(Attribute.Class c) { |
178 | value = new MirroredTypeExceptionProxy(c.type); |
179 | } |
180 | |
181 | public void visitArray(Attribute.Array a) { |
182 | Name elemName = ((ArrayType) a.type).elemtype.tsym.name; |
183 | |
184 | if (elemName == elemName.table.java_lang_Class) { // Class[] |
185 | // Construct a proxy for a MirroredTypesException |
186 | List<TypeMirror> elems = List.nil(); |
187 | for (Attribute value : a.values) { |
188 | Type elem = ((Attribute.Class) value).type; |
189 | elems.add(elem); |
190 | } |
191 | value = new MirroredTypesExceptionProxy(elems); |
192 | |
193 | } else { |
194 | int len = a.values.length; |
195 | Class<?> returnClassSaved = returnClass; |
196 | returnClass = returnClass.getComponentType(); |
197 | try { |
198 | Object res = Array.newInstance(returnClass, len); |
199 | for (int i = 0; i < len; i++) { |
200 | a.values[i].accept(this); |
201 | if (value == null || value instanceof ExceptionProxy) { |
202 | return; |
203 | } |
204 | try { |
205 | Array.set(res, i, value); |
206 | } catch (IllegalArgumentException e) { |
207 | value = null; // indicates a type mismatch |
208 | return; |
209 | } |
210 | } |
211 | value = res; |
212 | } finally { |
213 | returnClass = returnClassSaved; |
214 | } |
215 | } |
216 | } |
217 | |
218 | @SuppressWarnings("unchecked") |
219 | public void visitEnum(Attribute.Enum e) { |
220 | if (returnClass.isEnum()) { |
221 | String constName = e.value.toString(); |
222 | try { |
223 | value = Enum.valueOf((Class) returnClass, constName); |
224 | } catch (IllegalArgumentException ex) { |
225 | value = new EnumConstantNotPresentExceptionProxy( |
226 | (Class) returnClass, constName); |
227 | } |
228 | } else { |
229 | value = null; // indicates a type mismatch |
230 | } |
231 | } |
232 | |
233 | public void visitCompound(Attribute.Compound c) { |
234 | try { |
235 | Class<? extends Annotation> nested = |
236 | returnClass.asSubclass(Annotation.class); |
237 | value = generateAnnotation(c, nested); |
238 | } catch (ClassCastException ex) { |
239 | value = null; // indicates a type mismatch |
240 | } |
241 | } |
242 | |
243 | public void visitError(Attribute.Error e) { |
244 | value = null; // indicates a type mismatch |
245 | } |
246 | |
247 | |
248 | /** |
249 | * Sets "value" to an ExceptionProxy indicating a type mismatch. |
250 | */ |
251 | private void typeMismatch(final Method method, final Attribute attr) { |
252 | value = new ExceptionProxy() { |
253 | static final long serialVersionUID = 269; |
254 | public String toString() { |
255 | return "<error>"; // eg: @Anno(value=<error>) |
256 | } |
257 | protected RuntimeException generateException() { |
258 | return new AnnotationTypeMismatchException(method, |
259 | attr.type.toString()); |
260 | } |
261 | }; |
262 | } |
263 | } |
264 | |
265 | |
266 | /** |
267 | * ExceptionProxy for MirroredTypeException. |
268 | * The toString, hashCode, and equals methods foward to the underlying |
269 | * type. |
270 | */ |
271 | private static class MirroredTypeExceptionProxy extends ExceptionProxy { |
272 | static final long serialVersionUID = 269; |
273 | |
274 | private transient final TypeMirror type; |
275 | private final String typeString; |
276 | |
277 | MirroredTypeExceptionProxy(TypeMirror t) { |
278 | type = t; |
279 | typeString = t.toString(); |
280 | } |
281 | |
282 | public String toString() { |
283 | return typeString; |
284 | } |
285 | |
286 | public int hashCode() { |
287 | return (type != null ? type : typeString).hashCode(); |
288 | } |
289 | |
290 | public boolean equals(Object obj) { |
291 | return type != null && |
292 | obj instanceof MirroredTypeExceptionProxy && |
293 | type.equals(((MirroredTypeExceptionProxy) obj).type); |
294 | } |
295 | |
296 | protected RuntimeException generateException() { |
297 | return new MirroredTypeException(type); |
298 | } |
299 | } |
300 | |
301 | |
302 | /** |
303 | * ExceptionProxy for MirroredTypesException. |
304 | * The toString, hashCode, and equals methods foward to the underlying |
305 | * types. |
306 | */ |
307 | private static class MirroredTypesExceptionProxy extends ExceptionProxy { |
308 | static final long serialVersionUID = 269; |
309 | |
310 | private transient final List<TypeMirror> types; |
311 | private final String typeStrings; |
312 | |
313 | MirroredTypesExceptionProxy(List<TypeMirror> ts) { |
314 | types = ts; |
315 | typeStrings = ts.toString(); |
316 | } |
317 | |
318 | public String toString() { |
319 | return typeStrings; |
320 | } |
321 | |
322 | public int hashCode() { |
323 | return (types != null ? types : typeStrings).hashCode(); |
324 | } |
325 | |
326 | public boolean equals(Object obj) { |
327 | return types != null && |
328 | obj instanceof MirroredTypesExceptionProxy && |
329 | types.equals( |
330 | ((MirroredTypesExceptionProxy) obj).types); |
331 | } |
332 | |
333 | protected RuntimeException generateException() { |
334 | return new MirroredTypesException(types); |
335 | } |
336 | } |
337 | } |