1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package libcore.reflect;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.Serializable;
23import java.lang.annotation.Annotation;
24import java.lang.annotation.IncompleteAnnotationException;
25import java.lang.reflect.InvocationHandler;
26import java.lang.reflect.Method;
27import java.lang.reflect.Proxy;
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Map;
31import java.util.WeakHashMap;
32
33/**
34 * The annotation implementation based on dynamically generated proxy instances.
35 * It conforms to all requirements stated in public APIs, see in particular
36 * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
37 * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
38 * Namely, annotation instances are immutable and serializable; they provide
39 * conforming access to annotation member values and required implementations of
40 * methods declared in Annotation interface.
41 *
42 * @see AnnotationMember
43 * @see java.lang.annotation.Annotation
44 *
45 * @author Alexey V. Varlamov, Serguei S. Zapreyev
46 * @version $Revision$
47 */
48@SuppressWarnings({"serial"})
49public final class AnnotationFactory implements InvocationHandler, Serializable {
50
51    private static final transient Map<Class<? extends Annotation>, AnnotationMember[]> cache =
52            new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>();
53
54    /**
55     * Reflects specified annotation type and returns an array
56     * of member element definitions with default values.
57     */
58    public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType) {
59        synchronized (cache) {
60            AnnotationMember[] desc = cache.get(annotationType);
61            if (desc != null) {
62                return desc;
63            }
64        }
65        if (!annotationType.isAnnotation()) {
66            throw new IllegalArgumentException("Type is not annotation: " + annotationType.getName());
67        }
68        Method[] declaredMethods = annotationType.getDeclaredMethods();
69        AnnotationMember[] desc = new AnnotationMember[declaredMethods.length];
70        for (int i = 0; i < declaredMethods.length; ++i) {
71            Method element = declaredMethods[i];
72            String name = element.getName();
73            Class<?> type = element.getReturnType();
74            try {
75                desc[i] = new AnnotationMember(name, element.getDefaultValue(), type, element);
76            } catch (Throwable t) {
77                desc[i] = new AnnotationMember(name, t, type, element);
78            }
79        }
80        synchronized (cache) {
81            cache.put(annotationType, desc);
82        }
83        return desc;
84    }
85
86    /**
87     * Provides a new annotation instance.
88     * @param annotationType the annotation type definition
89     * @param elements name-value pairs representing elements of the annotation
90     * @return a new annotation instance
91     */
92    public static <A extends Annotation> A createAnnotation(Class<? extends Annotation> annotationType,
93                                                            AnnotationMember[] elements) {
94        AnnotationFactory factory = new AnnotationFactory(annotationType, elements);
95        return (A) Proxy.newProxyInstance(annotationType.getClassLoader(),
96                                          new Class[]{annotationType}, factory);
97    }
98
99    private final Class<? extends Annotation> klazz;
100    private AnnotationMember[] elements;
101
102    /**
103     * New instances should not be created directly, use factory method
104     * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
105     * instead.
106     *
107     * @param klzz class defining the annotation type
108     * @param values actual element values
109     */
110    private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) {
111        klazz = klzz;
112        AnnotationMember[] defs = getElementsDescription(klazz);
113        if (values == null) {
114            elements = defs;
115        } else {
116            //merge default and actual values
117            elements = new AnnotationMember[defs.length];
118            next: for (int i = elements.length - 1; i >= 0; i--) {
119                for (AnnotationMember val : values) {
120                    if (val.name.equals(defs[i].name)) {
121                        elements[i] = val.setDefinition(defs[i]);
122                        continue next;
123                    }
124                }
125                elements[i] = defs[i];
126            }
127        }
128    }
129
130    /**
131     * Reads the object, obtains actual member definitions for the annotation type,
132     * and merges deserialized values with the new definitions.
133     */
134    private void readObject(ObjectInputStream os) throws IOException, ClassNotFoundException {
135        os.defaultReadObject();
136        // Annotation type members can be changed arbitrarily
137        // So there may be zombi elements from the previous life;
138        // they hardly fit into this new annotation's incarnation,
139        // as we have no defining methods for them.
140        // Reasonably just drop such elements,
141        // but seems better to keep them for compatibility
142        AnnotationMember[] defs = getElementsDescription(klazz);
143        AnnotationMember[] old = elements;
144        List<AnnotationMember> merged = new ArrayList<AnnotationMember>(defs.length + old.length);
145        nextOld: for (AnnotationMember el1 : old) {
146            for (AnnotationMember el2 : defs) {
147                if (el2.name.equals(el1.name)) {
148                    continue nextOld;
149                }
150            }
151            merged.add(el1); //phantom element
152        }
153        nextNew: for (AnnotationMember def : defs) {
154            for (AnnotationMember val : old) {
155                if (val.name.equals(def.name)) {
156                    // nothing to do about cached errors (if any)
157                    // anyway they remain relevant to values
158                    merged.add(val.setDefinition(def));
159                    continue nextNew;
160                }
161            }
162            merged.add(def); // brand new element
163        }
164        elements = merged.toArray(new AnnotationMember[merged.size()]);
165    }
166
167    /**
168     * Returns true if the specified object represents the same annotation instance.
169     * That is, if it implements the same annotation type and
170     * returns the same element values.
171     * <br>Note, actual underlying implementation mechanism does not matter - it may
172     * differ completely from this class.
173     * @return true if the passed object is equivalent annotation instance,
174     * false otherwise.
175     * @see AnnotationMember#equals(Object)
176     */
177    public boolean equals(Object obj) {
178        if (obj == this) {
179            return true;
180        }
181        if (!klazz.isInstance(obj)) {
182            return false;
183        }
184        Object handler = null;
185        if (Proxy.isProxyClass(obj.getClass())
186                && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory) {
187            AnnotationFactory other = (AnnotationFactory) handler;
188            if (elements.length != other.elements.length) {
189                return false;
190            }
191            next: for (AnnotationMember el1 : elements) {
192                for (AnnotationMember el2 : other.elements) {
193                    if (el1.equals(el2)) {
194                        continue next;
195                    }
196                }
197                return false;
198            }
199            return true;
200        } else {
201            // encountered foreign annotation implementation
202            // so have to obtain element values via invocation
203            // of corresponding methods
204            for (final AnnotationMember el : elements) {
205                if (el.tag == AnnotationMember.ERROR) {
206                    // undefined value is incomparable (transcendent)
207                    return false;
208                }
209                try {
210                    if (!el.definingMethod.isAccessible()) {
211                        el.definingMethod.setAccessible(true);
212                    }
213                    Object otherValue = el.definingMethod.invoke(obj);
214                    if (otherValue != null) {
215                        if (el.tag == AnnotationMember.ARRAY) {
216                            if (!el.equalArrayValue(otherValue)) {
217                                return false;
218                            }
219                        } else {
220                            if (!el.value.equals(otherValue)) {
221                                return false;
222                            }
223                        }
224                    } else if (el.value != AnnotationMember.NO_VALUE) {
225                        return false;
226                    }
227                } catch (Throwable e) {
228                    return false;
229                }
230            }
231            return true;
232        }
233    }
234
235    /**
236     * Returns a hash code composed as a sum of hash codes of member elements,
237     * including elements with default values.
238     * @see AnnotationMember#hashCode()
239     */
240    public int hashCode() {
241        int hash = 0;
242        for (AnnotationMember element : elements) {
243            hash += element.hashCode();
244        }
245        return hash;
246    }
247
248    /**
249     * Provides detailed description of this annotation instance,
250     * including all member name-values pairs.
251     * @return string representation of this annotation
252     */
253    public String toString() {
254        StringBuilder result = new StringBuilder();
255        result.append('@');
256        result.append(klazz.getName());
257        result.append('(');
258        for (int i = 0; i < elements.length; ++i) {
259            if (i != 0) {
260                result.append(", ");
261            }
262            result.append(elements[i]);
263        }
264        result.append(')');
265        return result.toString();
266    }
267
268    /**
269     * Processes a method invocation request to this annotation instance.
270     * Recognizes the methods declared in the
271     * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
272     * interface, and member-defining methods of the implemented annotation type.
273     * @throws IllegalArgumentException If the specified method is none of the above
274     * @return the invocation result
275     */
276    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
277        String name = method.getName();
278        Class[] params = method.getParameterTypes();
279        if (params.length == 0) {
280            if ("annotationType".equals(name)) {
281                return klazz;
282            } else if ("toString".equals(name)) {
283                return toString();
284            } else if ("hashCode".equals(name)) {
285                return hashCode();
286            }
287
288            // this must be element value request
289            AnnotationMember element = null;
290            for (AnnotationMember el : elements) {
291                if (name.equals(el.name)) {
292                    element = el;
293                    break;
294                }
295            }
296            if (element == null || !method.equals(element.definingMethod)) {
297                throw new IllegalArgumentException(method.toString());
298            } else {
299                Object value = element.validateValue();
300                if (value == null) {
301                    throw new IncompleteAnnotationException(klazz, name);
302                }
303                return value;
304            }
305        } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)) {
306            return Boolean.valueOf(equals(args[0]));
307        }
308        throw new IllegalArgumentException("Invalid method for annotation type: " + method);
309    }
310}
311