001 /*
002 * Copyright 2004-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.apt.mirror.declaration;
027
028
029 import java.lang.annotation.*;
030 import java.lang.reflect.Array;
031 import java.lang.reflect.Method;
032 import java.util.*;
033 import sun.reflect.annotation.*;
034
035 import com.sun.mirror.type.TypeMirror;
036 import com.sun.mirror.type.MirroredTypeException;
037 import com.sun.mirror.type.MirroredTypesException;
038 import com.sun.tools.apt.mirror.AptEnv;
039 import com.sun.tools.javac.code.*;
040 import com.sun.tools.javac.code.Symbol.*;
041 import com.sun.tools.javac.util.Name;
042 import com.sun.tools.javac.util.Pair;
043
044
045 /**
046 * A generator of dynamic proxy implementations of
047 * java.lang.annotation.Annotation.
048 *
049 * <p> The "dynamic proxy return form" of an attribute element value is
050 * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
051 */
052
053 class AnnotationProxyMaker {
054
055 private final AptEnv env;
056 private final Attribute.Compound attrs;
057 private final Class<? extends Annotation> annoType;
058
059
060 private AnnotationProxyMaker(AptEnv env,
061 Attribute.Compound attrs,
062 Class<? extends Annotation> annoType) {
063 this.env = env;
064 this.attrs = attrs;
065 this.annoType = annoType;
066 }
067
068
069 /**
070 * Returns a dynamic proxy for an annotation mirror.
071 */
072 public static <A extends Annotation> A generateAnnotation(
073 AptEnv env, Attribute.Compound attrs, Class<A> annoType) {
074 AnnotationProxyMaker apm = new AnnotationProxyMaker(env, attrs, annoType);
075 return annoType.cast(apm.generateAnnotation());
076 }
077
078
079 /**
080 * Returns a dynamic proxy for an annotation mirror.
081 */
082 private Annotation generateAnnotation() {
083 return AnnotationParser.annotationForMap(annoType,
084 getAllReflectedValues());
085 }
086
087 /**
088 * Returns a map from element names to their values in "dynamic
089 * proxy return form". Includes all elements, whether explicit or
090 * defaulted.
091 */
092 private Map<String, Object> getAllReflectedValues() {
093 Map<String, Object> res = new LinkedHashMap<String, Object>();
094
095 for (Map.Entry<MethodSymbol, Attribute> entry :
096 getAllValues().entrySet()) {
097 MethodSymbol meth = entry.getKey();
098 Object value = generateValue(meth, entry.getValue());
099 if (value != null) {
100 res.put(meth.name.toString(), value);
101 } else {
102 // Ignore this element. May lead to
103 // IncompleteAnnotationException somewhere down the line.
104 }
105 }
106 return res;
107 }
108
109 /**
110 * Returns a map from element symbols to their values.
111 * Includes all elements, whether explicit or defaulted.
112 */
113 private Map<MethodSymbol, Attribute> getAllValues() {
114 Map<MethodSymbol, Attribute> res =
115 new LinkedHashMap<MethodSymbol, Attribute>();
116
117 // First find the default values.
118 ClassSymbol sym = (ClassSymbol) attrs.type.tsym;
119 for (Scope.Entry e = sym.members().elems; e != null; e = e.sibling) {
120 if (e.sym.kind == Kinds.MTH) {
121 MethodSymbol m = (MethodSymbol) e.sym;
122 Attribute def = m.defaultValue;
123 if (def != null) {
124 res.put(m, def);
125 }
126 }
127 }
128 // Next find the explicit values, possibly overriding defaults.
129 for (Pair<MethodSymbol, Attribute> p : attrs.values) {
130 res.put(p.fst, p.snd);
131 }
132 return res;
133 }
134
135 /**
136 * Converts an element value to its "dynamic proxy return form".
137 * Returns an exception proxy on some errors, but may return null if
138 * a useful exception cannot or should not be generated at this point.
139 */
140 private Object generateValue(MethodSymbol meth, Attribute attr) {
141 ValueVisitor vv = new ValueVisitor(meth);
142 return vv.getValue(attr);
143 }
144
145
146 private class ValueVisitor implements Attribute.Visitor {
147
148 private MethodSymbol meth; // annotation element being visited
149 private Class<?> runtimeType; // runtime type of annotation element
150 private Object value; // value in "dynamic proxy return form"
151
152 ValueVisitor(MethodSymbol meth) {
153 this.meth = meth;
154 }
155
156 Object getValue(Attribute attr) {
157 Method method; // runtime method of annotation element
158 try {
159 method = annoType.getMethod(meth.name.toString());
160 } catch (NoSuchMethodException e) {
161 return null;
162 }
163 runtimeType = method.getReturnType();
164 attr.accept(this);
165 if (!(value instanceof ExceptionProxy) &&
166 !AnnotationType.invocationHandlerReturnType(runtimeType)
167 .isInstance(value)) {
168 typeMismatch(method, attr);
169 }
170 return value;
171 }
172
173
174 public void visitConstant(Attribute.Constant c) {
175 value = Constants.decodeConstant(c.value, c.type);
176 }
177
178 public void visitClass(Attribute.Class c) {
179 value = new MirroredTypeExceptionProxy(
180 env.typeMaker.getType(c.type));
181 }
182
183 public void visitArray(Attribute.Array a) {
184 Type elemtype = env.jctypes.elemtype(a.type);
185
186 if (elemtype.tsym == env.symtab.classType.tsym) { // Class[]
187 // Construct a proxy for a MirroredTypesException
188 ArrayList<TypeMirror> elems = new ArrayList<TypeMirror>();
189 for (int i = 0; i < a.values.length; i++) {
190 Type elem = ((Attribute.Class) a.values[i]).type;
191 elems.add(env.typeMaker.getType(elem));
192 }
193 value = new MirroredTypesExceptionProxy(elems);
194
195 } else {
196 int len = a.values.length;
197 Class<?> runtimeTypeSaved = runtimeType;
198 runtimeType = runtimeType.getComponentType();
199 try {
200 Object res = Array.newInstance(runtimeType, len);
201 for (int i = 0; i < len; i++) {
202 a.values[i].accept(this);
203 if (value == null || value instanceof ExceptionProxy) {
204 return;
205 }
206 try {
207 Array.set(res, i, value);
208 } catch (IllegalArgumentException e) {
209 value = null; // indicates a type mismatch
210 return;
211 }
212 }
213 value = res;
214 } finally {
215 runtimeType = runtimeTypeSaved;
216 }
217 }
218 }
219
220 @SuppressWarnings({"unchecked", "rawtypes"})
221 public void visitEnum(Attribute.Enum e) {
222 if (runtimeType.isEnum()) {
223 String constName = e.value.toString();
224 try {
225 value = Enum.valueOf((Class)runtimeType, constName);
226 } catch (IllegalArgumentException ex) {
227 value = new EnumConstantNotPresentExceptionProxy(
228 (Class<Enum<?>>)runtimeType, constName);
229 }
230 } else {
231 value = null; // indicates a type mismatch
232 }
233 }
234
235 public void visitCompound(Attribute.Compound c) {
236 try {
237 Class<? extends Annotation> nested =
238 runtimeType.asSubclass(Annotation.class);
239 value = generateAnnotation(env, c, nested);
240 } catch (ClassCastException ex) {
241 value = null; // indicates a type mismatch
242 }
243 }
244
245 public void visitError(Attribute.Error e) {
246 value = null; // indicates a type mismatch
247 }
248
249
250 /**
251 * Sets "value" to an ExceptionProxy indicating a type mismatch.
252 */
253 private void typeMismatch(final Method method, final Attribute attr) {
254 value = new ExceptionProxy() {
255 private static final long serialVersionUID = 8473323277815075163L;
256 public String toString() {
257 return "<error>"; // eg: @Anno(value=<error>)
258 }
259 protected RuntimeException generateException() {
260 return new AnnotationTypeMismatchException(method,
261 attr.type.toString());
262 }
263 };
264 }
265 }
266
267
268 /**
269 * ExceptionProxy for MirroredTypeException.
270 * The toString, hashCode, and equals methods foward to the underlying
271 * type.
272 */
273 private static class MirroredTypeExceptionProxy extends ExceptionProxy {
274 private static final long serialVersionUID = 6662035281599933545L;
275
276 private MirroredTypeException ex;
277
278 MirroredTypeExceptionProxy(TypeMirror t) {
279 // It would be safer if we could construct the exception in
280 // generateException(), but there would be no way to do
281 // that properly following deserialization.
282 ex = new MirroredTypeException(t);
283 }
284
285 public String toString() {
286 return ex.getQualifiedName();
287 }
288
289 public int hashCode() {
290 TypeMirror t = ex.getTypeMirror();
291 return (t != null)
292 ? t.hashCode()
293 : ex.getQualifiedName().hashCode();
294 }
295
296 public boolean equals(Object obj) {
297 TypeMirror t = ex.getTypeMirror();
298 return t != null &&
299 obj instanceof MirroredTypeExceptionProxy &&
300 t.equals(
301 ((MirroredTypeExceptionProxy) obj).ex.getTypeMirror());
302 }
303
304 protected RuntimeException generateException() {
305 return (RuntimeException) ex.fillInStackTrace();
306 }
307 }
308
309
310 /**
311 * ExceptionProxy for MirroredTypesException.
312 * The toString, hashCode, and equals methods foward to the underlying
313 * types.
314 */
315 private static class MirroredTypesExceptionProxy extends ExceptionProxy {
316 private static final long serialVersionUID = -6670822532616693951L;
317
318 private MirroredTypesException ex;
319
320 MirroredTypesExceptionProxy(Collection<TypeMirror> ts) {
321 // It would be safer if we could construct the exception in
322 // generateException(), but there would be no way to do
323 // that properly following deserialization.
324 ex = new MirroredTypesException(ts);
325 }
326
327 public String toString() {
328 return ex.getQualifiedNames().toString();
329 }
330
331 public int hashCode() {
332 Collection<TypeMirror> ts = ex.getTypeMirrors();
333 return (ts != null)
334 ? ts.hashCode()
335 : ex.getQualifiedNames().hashCode();
336 }
337
338 public boolean equals(Object obj) {
339 Collection<TypeMirror> ts = ex.getTypeMirrors();
340 return ts != null &&
341 obj instanceof MirroredTypesExceptionProxy &&
342 ts.equals(
343 ((MirroredTypesExceptionProxy) obj).ex.getTypeMirrors());
344 }
345
346 protected RuntimeException generateException() {
347 return (RuntimeException) ex.fillInStackTrace();
348 }
349 }
350 }