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    }