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 
18 package libcore.reflect;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.Serializable;
23 import java.lang.annotation.Annotation;
24 import java.lang.annotation.IncompleteAnnotationException;
25 import java.lang.reflect.InvocationHandler;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.Proxy;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Map;
31 import 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"})
49 public 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      */
getElementsDescription(Class<? extends Annotation> annotationType)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      */
createAnnotation(Class<? extends Annotation> annotationType, AnnotationMember[] elements)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      */
AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values)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      */
readObject(ObjectInputStream os)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      */
equals(Object obj)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      */
hashCode()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      */
toString()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      */
invoke(Object proxy, Method method, Object[] args)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