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