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