001 /*
002 * Copyright 2005-2008 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 /**
045 * A generator of dynamic proxy implementations of
046 * java.lang.annotation.Annotation.
047 *
048 * <p> The "dynamic proxy return form" of an annotation element value is
049 * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
050 *
051 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
052 * you write code that depends on this, you do so at your own risk.
053 * This code and its internal interfaces are subject to change or
054 * deletion without notice.</b>
055 */
056
057 public class AnnotationProxyMaker {
058
059 private final Attribute.Compound anno;
060 private final Class<? extends Annotation> annoType;
061
062
063 private AnnotationProxyMaker(Attribute.Compound anno,
064 Class<? extends Annotation> annoType) {
065 this.anno = anno;
066 this.annoType = annoType;
067 }
068
069
070 /**
071 * Returns a dynamic proxy for an annotation mirror.
072 */
073 public static <A extends Annotation> A generateAnnotation(
074 Attribute.Compound anno, Class<A> annoType) {
075 AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType);
076 return annoType.cast(apm.generateAnnotation());
077 }
078
079
080 /**
081 * Returns a dynamic proxy for an annotation mirror.
082 */
083 private Annotation generateAnnotation() {
084 return AnnotationParser.annotationForMap(annoType,
085 getAllReflectedValues());
086 }
087
088 /**
089 * Returns a map from element names to their values in "dynamic
090 * proxy return form". Includes all elements, whether explicit or
091 * defaulted.
092 */
093 private Map<String, Object> getAllReflectedValues() {
094 Map<String, Object> res = new LinkedHashMap<String, Object>();
095
096 for (Map.Entry<MethodSymbol, Attribute> entry :
097 getAllValues().entrySet()) {
098 MethodSymbol meth = entry.getKey();
099 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.names.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", "rawtypes"})
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<Enum<?>>) 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 }