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    }