AnnotationProxyMaker.java revision 2571:10fc81ac75b4
1/*
2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.javac.model;
27
28import java.io.IOException;
29import java.io.ObjectInputStream;
30import java.lang.annotation.*;
31import java.lang.reflect.Array;
32import java.lang.reflect.Method;
33import java.util.LinkedHashMap;
34import java.util.Map;
35import sun.reflect.annotation.*;
36
37import javax.lang.model.type.MirroredTypeException;
38import javax.lang.model.type.MirroredTypesException;
39import javax.lang.model.type.TypeMirror;
40
41import com.sun.tools.javac.code.*;
42import com.sun.tools.javac.code.Symbol.*;
43import com.sun.tools.javac.code.Type.ArrayType;
44import com.sun.tools.javac.util.*;
45
46import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
47
48/**
49 * A generator of dynamic proxy implementations of
50 * java.lang.annotation.Annotation.
51 *
52 * <p> The "dynamic proxy return form" of an annotation element value is
53 * the form used by sun.reflect.annotation.AnnotationInvocationHandler.
54 *
55 * <p><b>This is NOT part of any supported API.
56 * If you write code that depends on this, you do so at your own risk.
57 * This code and its internal interfaces are subject to change or
58 * deletion without notice.</b>
59 */
60
61public class AnnotationProxyMaker {
62
63    private final Attribute.Compound anno;
64    private final Class<? extends Annotation> annoType;
65
66
67    private AnnotationProxyMaker(Attribute.Compound anno,
68                                 Class<? extends Annotation> annoType) {
69        this.anno = anno;
70        this.annoType = annoType;
71    }
72
73
74    /**
75     * Returns a dynamic proxy for an annotation mirror.
76     */
77    public static <A extends Annotation> A generateAnnotation(
78            Attribute.Compound anno, Class<A> annoType) {
79        AnnotationProxyMaker apm = new AnnotationProxyMaker(anno, annoType);
80        return annoType.cast(apm.generateAnnotation());
81    }
82
83
84    /**
85     * Returns a dynamic proxy for an annotation mirror.
86     */
87    private Annotation generateAnnotation() {
88        return AnnotationParser.annotationForMap(annoType,
89                                                 getAllReflectedValues());
90    }
91
92    /**
93     * Returns a map from element names to their values in "dynamic
94     * proxy return form".  Includes all elements, whether explicit or
95     * defaulted.
96     */
97    private Map<String, Object> getAllReflectedValues() {
98        Map<String, Object> res = new LinkedHashMap<>();
99
100        for (Map.Entry<MethodSymbol, Attribute> entry :
101                                                  getAllValues().entrySet()) {
102            MethodSymbol meth = entry.getKey();
103            Object value = generateValue(meth, entry.getValue());
104            if (value != null) {
105                res.put(meth.name.toString(), value);
106            } else {
107                // Ignore this element.  May (properly) lead to
108                // IncompleteAnnotationException somewhere down the line.
109            }
110        }
111        return res;
112    }
113
114    /**
115     * Returns a map from element symbols to their values.
116     * Includes all elements, whether explicit or defaulted.
117     */
118    private Map<MethodSymbol, Attribute> getAllValues() {
119        Map<MethodSymbol, Attribute> res = new LinkedHashMap<>();
120
121        // First find the default values.
122        ClassSymbol sym = (ClassSymbol) anno.type.tsym;
123        for (Symbol s : sym.members().getSymbols(NON_RECURSIVE)) {
124            if (s.kind == Kinds.MTH) {
125                MethodSymbol m = (MethodSymbol) s;
126                Attribute def = m.getDefaultValue();
127                if (def != null)
128                    res.put(m, def);
129            }
130        }
131        // Next find the explicit values, possibly overriding defaults.
132        for (Pair<MethodSymbol, Attribute> p : anno.values)
133            res.put(p.fst, p.snd);
134        return res;
135    }
136
137    /**
138     * Converts an element value to its "dynamic proxy return form".
139     * Returns an exception proxy on some errors, but may return null if
140     * a useful exception cannot or should not be generated at this point.
141     */
142    private Object generateValue(MethodSymbol meth, Attribute attr) {
143        ValueVisitor vv = new ValueVisitor(meth);
144        return vv.getValue(attr);
145    }
146
147
148    private class ValueVisitor implements Attribute.Visitor {
149
150        private MethodSymbol meth;      // annotation element being visited
151        private Class<?> returnClass;   // return type of annotation element
152        private Object value;           // value in "dynamic proxy return form"
153
154        ValueVisitor(MethodSymbol meth) {
155            this.meth = meth;
156        }
157
158        Object getValue(Attribute attr) {
159            Method method;              // runtime method of annotation element
160            try {
161                method = annoType.getMethod(meth.name.toString());
162            } catch (NoSuchMethodException e) {
163                return null;
164            }
165            returnClass = method.getReturnType();
166            attr.accept(this);
167            if (!(value instanceof ExceptionProxy) &&
168                !AnnotationType.invocationHandlerReturnType(returnClass)
169                                                        .isInstance(value)) {
170                typeMismatch(method, attr);
171            }
172            return value;
173        }
174
175
176        public void visitConstant(Attribute.Constant c) {
177            value = c.getValue();
178        }
179
180        public void visitClass(Attribute.Class c) {
181            value = new MirroredTypeExceptionProxy(c.classType);
182        }
183
184        public void visitArray(Attribute.Array a) {
185            Name elemName = ((ArrayType) a.type).elemtype.tsym.getQualifiedName();
186
187            if (elemName.equals(elemName.table.names.java_lang_Class)) {   // Class[]
188                // Construct a proxy for a MirroredTypesException
189                ListBuffer<TypeMirror> elems = new ListBuffer<>();
190                for (Attribute value : a.values) {
191                    Type elem = ((Attribute.Class) value).classType;
192                    elems.append(elem);
193                }
194                value = new MirroredTypesExceptionProxy(elems.toList());
195
196            } else {
197                int len = a.values.length;
198                Class<?> returnClassSaved = returnClass;
199                returnClass = returnClass.getComponentType();
200                try {
201                    Object res = Array.newInstance(returnClass, len);
202                    for (int i = 0; i < len; i++) {
203                        a.values[i].accept(this);
204                        if (value == null || value instanceof ExceptionProxy) {
205                            return;
206                        }
207                        try {
208                            Array.set(res, i, value);
209                        } catch (IllegalArgumentException e) {
210                            value = null;       // indicates a type mismatch
211                            return;
212                        }
213                    }
214                    value = res;
215                } finally {
216                    returnClass = returnClassSaved;
217                }
218            }
219        }
220
221        @SuppressWarnings({"unchecked", "rawtypes"})
222        public void visitEnum(Attribute.Enum e) {
223            if (returnClass.isEnum()) {
224                String constName = e.value.toString();
225                try {
226                    value = Enum.valueOf((Class)returnClass, constName);
227                } catch (IllegalArgumentException ex) {
228                    value = new EnumConstantNotPresentExceptionProxy(
229                                        (Class<Enum<?>>) returnClass, constName);
230                }
231            } else {
232                value = null;   // indicates a type mismatch
233            }
234        }
235
236        public void visitCompound(Attribute.Compound c) {
237            try {
238                Class<? extends Annotation> nested =
239                    returnClass.asSubclass(Annotation.class);
240                value = generateAnnotation(c, nested);
241            } catch (ClassCastException ex) {
242                value = null;   // indicates a type mismatch
243            }
244        }
245
246        public void visitError(Attribute.Error e) {
247            if (e instanceof Attribute.UnresolvedClass)
248                value = new MirroredTypeExceptionProxy(((Attribute.UnresolvedClass)e).classType);
249            else
250                value = null;       // indicates a type mismatch
251        }
252
253
254        /**
255         * Sets "value" to an ExceptionProxy indicating a type mismatch.
256         */
257        private void typeMismatch(Method method, final Attribute attr) {
258            class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy {
259                static final long serialVersionUID = 269;
260                transient final Method method;
261                AnnotationTypeMismatchExceptionProxy(Method method) {
262                    this.method = method;
263                }
264                public String toString() {
265                    return "<error>";   // eg:  @Anno(value=<error>)
266                }
267                protected RuntimeException generateException() {
268                    return new AnnotationTypeMismatchException(method,
269                                attr.type.toString());
270                }
271            }
272            value = new AnnotationTypeMismatchExceptionProxy(method);
273        }
274    }
275
276
277    /**
278     * ExceptionProxy for MirroredTypeException.
279     * The toString, hashCode, and equals methods forward to the underlying
280     * type.
281     */
282    private static final class MirroredTypeExceptionProxy extends ExceptionProxy {
283        static final long serialVersionUID = 269;
284
285        private transient TypeMirror type;
286        private final String typeString;
287
288        MirroredTypeExceptionProxy(TypeMirror t) {
289            type = t;
290            typeString = t.toString();
291        }
292
293        public String toString() {
294            return typeString;
295        }
296
297        public int hashCode() {
298            return (type != null ? type : typeString).hashCode();
299        }
300
301        public boolean equals(Object obj) {
302            return type != null &&
303                   obj instanceof MirroredTypeExceptionProxy &&
304                   type.equals(((MirroredTypeExceptionProxy) obj).type);
305        }
306
307        protected RuntimeException generateException() {
308            return new MirroredTypeException(type);
309        }
310
311        // Explicitly set all transient fields.
312        private void readObject(ObjectInputStream s)
313            throws IOException, ClassNotFoundException {
314            s.defaultReadObject();
315            type = null;
316        }
317    }
318
319
320    /**
321     * ExceptionProxy for MirroredTypesException.
322     * The toString, hashCode, and equals methods foward to the underlying
323     * types.
324     */
325    private static final class MirroredTypesExceptionProxy extends ExceptionProxy {
326        static final long serialVersionUID = 269;
327
328        private transient List<TypeMirror> types;
329        private final String typeStrings;
330
331        MirroredTypesExceptionProxy(List<TypeMirror> ts) {
332            types = ts;
333            typeStrings = ts.toString();
334        }
335
336        public String toString() {
337            return typeStrings;
338        }
339
340        public int hashCode() {
341            return (types != null ? types : typeStrings).hashCode();
342        }
343
344        public boolean equals(Object obj) {
345            return types != null &&
346                   obj instanceof MirroredTypesExceptionProxy &&
347                   types.equals(
348                      ((MirroredTypesExceptionProxy) obj).types);
349        }
350
351        protected RuntimeException generateException() {
352            return new MirroredTypesException(types);
353        }
354
355        // Explicitly set all transient fields.
356        private void readObject(ObjectInputStream s)
357            throws IOException, ClassNotFoundException {
358            s.defaultReadObject();
359            types = null;
360        }
361    }
362}
363