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