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.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.io.Serializable;
25 import java.lang.annotation.AnnotationTypeMismatchException;
26 import java.lang.reflect.Array;
27 import java.lang.reflect.Method;
28 import java.util.Arrays;
29 
30 /**
31  * This class represents member element of an annotation.
32  * It consists of name and value, supplemented with element
33  * definition information (such as declared type of element).
34  * <br>The value may be one of the following types:
35  * <ul>
36  * <li> boxed primitive
37  * <li> Class
38  * <li> enum constant
39  * <li> annotation (nested)
40  * <li> one-dimensional array of the above
41  * <li> Throwable
42  * </ul>
43  * The last type is specific for this implementation; a Throwable value
44  * means that the error occured during parsing or resolution of corresponding
45  * class-data structures and throwing is delayed until the element
46  * is requested for value.
47  *
48  * @see AnnotationFactory
49  *
50  * @author Alexey V. Varlamov, Serguei S. Zapreyev
51  * @version $Revision$
52  */
53 @SuppressWarnings({"serial"})
54 public final class AnnotationMember implements Serializable {
55 
56     /**
57      * Tag description of a Throwable value type.
58      */
59     protected static final char ERROR = '!';
60 
61     /**
62      * Tag description of an array value type.
63      */
64     protected static final char ARRAY = '[';
65 
66     /**
67      * Tag description of all value types except arrays and Throwables.
68      */
69     protected static final char OTHER = '*';
70 
71 //    public static final char INT = 'I';
72 //    public static final char CHAR = 'C';
73 //    public static final char DOUBLE = 'D';
74 //    public static final char FLOAT = 'F';
75 //    public static final char BYTE = 'B';
76 //    public static final char LONG = 'J';
77 //    public static final char SHORT = 'S';
78 //    public static final char BOOL = 'Z';
79 //    public static final char CLASS = 'c';
80 //    public static final char ENUM = 'e';
81 //    public static final char ANTN = '@';
82 
83     private enum DefaultValues {NO_VALUE}
84 
85     /**
86      * Singleton representing missing element value.
87      */
88     protected static final Object NO_VALUE = DefaultValues.NO_VALUE;
89 
90     protected final String name;
91     protected final Object value; // a primitive value is wrapped to the corresponding wrapper class
92     protected final char tag;
93     // no sense to serialize definition info as it can be changed arbitrarily
94     protected transient Class<?> elementType;
95     protected transient Method definingMethod;
96 
97 
98     /**
99      * Creates a new element with specified name and value.
100      * Definition info will be provided later when this
101      * element becomes actual annotation member.
102      * @param name element name, must not be null
103      * @param val element value, should be of addmissible type,
104      * as specified in the description of this class
105      *
106      * @see #setDefinition(AnnotationMember)
107      */
AnnotationMember(String name, Object val)108     public AnnotationMember(String name, Object val) {
109         this.name = name;
110         value = val == null ? NO_VALUE : val;
111         if (value instanceof Throwable) {
112             tag = ERROR;
113         } else if (value.getClass().isArray()) {
114             tag = ARRAY;
115         } else {
116             tag = OTHER;
117         }
118     }
119 
120     /**
121      * Creates the completely defined element.
122      * @param name element name, must not be null
123      * @param value element value, should be of addmissible type,
124      * as specified in the description of this class
125      * @param m element-defining method, reflected on the annotation type
126      * @param type declared type of this element
127      * (return type of the defining method)
128      */
AnnotationMember(String name, Object val, Class type, Method m)129     public AnnotationMember(String name, Object val, Class type, Method m) {
130         this(name, val);
131 
132         definingMethod = m;
133 
134         if (type == int.class) {
135             elementType = Integer.class;
136         } else if (type == boolean.class) {
137             elementType = Boolean.class;
138         } else if (type == char.class) {
139             elementType = Character.class;
140         } else if (type == float.class) {
141             elementType = Float.class;
142         } else if (type == double.class) {
143             elementType = Double.class;
144         } else if (type == long.class) {
145             elementType = Long.class;
146         } else if (type == short.class) {
147             elementType = Short.class;
148         } else if (type == byte.class) {
149             elementType = Byte.class;
150         } else {
151             elementType = type;
152         }
153     }
154 
155     /**
156      * Fills in element's definition info and returns this.
157      */
setDefinition(AnnotationMember copy)158     protected AnnotationMember setDefinition(AnnotationMember copy) {
159         definingMethod = copy.definingMethod;
160         elementType = copy.elementType;
161         return this;
162     }
163 
164     /**
165      * Returns readable description of this annotation value.
166      */
toString()167     public String toString() {
168         if (tag == ARRAY) {
169             StringBuilder sb = new StringBuilder(80);
170             sb.append(name).append("=[");
171             int len = Array.getLength(value);
172             for (int i = 0; i < len; i++) {
173                 if (i != 0) sb.append(", ");
174                 sb.append(Array.get(value, i));
175             }
176             return sb.append("]").toString();
177         } else {
178             return name+ "=" +value;
179         }
180     }
181 
182     /**
183      * Returns true if the specified object represents equal element
184      * (equivalent name-value pair).
185      * <br> A special case is the contained Throwable value; it is considered
186      * transcendent so no other element would be equal.
187      * @return true if passed object is equivalent element representation,
188      * false otherwise
189      * @see #equalArrayValue(Object)
190      * @see java.lang.annotation.Annotation#equals(Object)
191      */
equals(Object obj)192     public boolean equals(Object obj) {
193         if (obj == this) {
194             // not a mere optimization,
195             // this is needed for consistency with hashCode()
196             return true;
197         }
198         if (obj instanceof AnnotationMember) {
199             AnnotationMember that = (AnnotationMember)obj;
200             if (name.equals(that.name) && tag == that.tag) {
201                 if (tag == ARRAY) {
202                     return equalArrayValue(that.value);
203                 } else if (tag == ERROR) {
204                     // undefined value is incomparable (transcendent)
205                     return false;
206                 } else {
207                     return value.equals(that.value);
208                 }
209             }
210         }
211         return false;
212     }
213 
214     /**
215      * Returns true if the contained value and a passed object are equal arrays,
216      * false otherwise. Appropriate overloaded method of Arrays.equals()
217      * is used for equality testing.
218      * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[])
219      * @return true if the value is array and is equal to specified object,
220      * false otherwise
221      */
equalArrayValue(Object otherValue)222     public boolean equalArrayValue(Object otherValue) {
223         if (value instanceof Object[] && otherValue instanceof Object[]) {
224             return Arrays.equals((Object[])value, (Object[])otherValue);
225         }
226         Class type = value.getClass();
227         if (type != otherValue.getClass()) {
228             return false;
229         }
230         if (type == int[].class) {
231             return Arrays.equals((int[])value, (int[])otherValue);
232         } else if (type == byte[].class) {
233             return Arrays.equals((byte[])value, (byte[])otherValue);
234         } else if (type == short[].class) {
235             return Arrays.equals((short[])value, (short[])otherValue);
236         } else if (type == long[].class) {
237             return Arrays.equals((long[])value, (long[])otherValue);
238         } else if (type == char[].class) {
239             return Arrays.equals((char[])value, (char[])otherValue);
240         } else if (type == boolean[].class) {
241             return Arrays.equals((boolean[])value, (boolean[])otherValue);
242         } else if (type == float[].class) {
243             return Arrays.equals((float[])value, (float[])otherValue);
244         } else if (type == double[].class) {
245             return Arrays.equals((double[])value, (double[])otherValue);
246         }
247         return false;
248     }
249 
250     /**
251      * Computes hash code of this element. The formula is as follows:
252      * <code> (name.hashCode() * 127) ^ value.hashCode() </code>
253      * <br>If value is an array, one of overloaded Arrays.hashCode()
254      * methods is used.
255      * @return the hash code
256      * @see java.util.Arrays#hashCode(java.lang.Object[])
257      * @see java.lang.annotation.Annotation#hashCode()
258      */
hashCode()259     public int hashCode() {
260         int hash = name.hashCode() * 127;
261         if (tag == ARRAY) {
262             Class type = value.getClass();
263             if (type == int[].class) {
264                 return hash ^ Arrays.hashCode((int[])value);
265             } else if (type == byte[].class) {
266                 return hash ^ Arrays.hashCode((byte[])value);
267             } else if (type == short[].class) {
268                 return hash ^ Arrays.hashCode((short[])value);
269             } else if (type == long[].class) {
270                 return hash ^ Arrays.hashCode((long[])value);
271             } else if (type == char[].class) {
272                 return hash ^ Arrays.hashCode((char[])value);
273             } else if (type == boolean[].class) {
274                 return hash ^ Arrays.hashCode((boolean[])value);
275             } else if (type == float[].class) {
276                 return hash ^ Arrays.hashCode((float[])value);
277             } else if (type == double[].class) {
278                 return hash ^ Arrays.hashCode((double[])value);
279             }
280             return hash ^ Arrays.hashCode((Object[])value);
281         } else {
282             return hash ^ value.hashCode();
283         }
284     }
285 
286     /**
287      * Throws contained error (if any) with a renewed stack trace.
288      */
rethrowError()289     public void rethrowError() throws Throwable {
290         if (tag == ERROR) {
291             // need to throw cloned exception for thread safety
292             // besides it is better to provide actual stack trace
293             // rather than recorded during parsing
294 
295             // first check for expected types
296             if (value instanceof TypeNotPresentException) {
297                 TypeNotPresentException tnpe = (TypeNotPresentException)value;
298                 throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause());
299             } else if (value instanceof EnumConstantNotPresentException) {
300                 EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value;
301                 throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName());
302             } else if (value instanceof ArrayStoreException) {
303                 ArrayStoreException ase = (ArrayStoreException)value;
304                 throw new ArrayStoreException(ase.getMessage());
305             }
306             // got some other error, have to go with deep cloning
307             // via serialization mechanism
308             Throwable error = (Throwable)value;
309             StackTraceElement[] ste = error.getStackTrace();
310             ByteArrayOutputStream bos = new ByteArrayOutputStream(
311                     ste == null ? 512 : (ste.length + 1) * 80);
312             ObjectOutputStream oos = new ObjectOutputStream(bos);
313             oos.writeObject(error);
314             oos.flush();
315             oos.close();
316             ByteArrayInputStream bis = new ByteArrayInputStream(bos
317                     .toByteArray());
318             ObjectInputStream ois = new ObjectInputStream(bis);
319             error = (Throwable)ois.readObject();
320             ois.close();
321 
322             throw error;
323         }
324     }
325 
326     /**
327      * Validates contained value against its member definition
328      * and if ok returns the value.
329      * Otherwise, if the value type mismatches definition
330      * or the value itself describes an error,
331      * throws appropriate exception.
332      * <br> Note, this method may return null if this element was constructed
333      * with such value.
334      *
335      * @see #rethrowError()
336      * @see #copyValue()
337      * @return actual valid value or null if no value
338      */
validateValue()339     public Object validateValue() throws Throwable {
340         if (tag == ERROR) {
341             rethrowError();
342         }
343         if (value == NO_VALUE) {
344             return null;
345         }
346         if (elementType == value.getClass()
347                 || elementType.isInstance(value)) { // nested annotation value
348             return copyValue();
349         } else {
350             throw new AnnotationTypeMismatchException(definingMethod,
351                     value.getClass().getName());
352         }
353 
354     }
355 
356 
357     /**
358      * Provides mutation-safe access to contained value. That is, caller is free
359      * to modify the returned value, it will not affect the contained data value.
360      * @return cloned value if it is mutable or the original immutable value
361      */
copyValue()362     public Object copyValue() throws Throwable
363     {
364         if (tag != ARRAY || Array.getLength(value) == 0) {
365             return value;
366         }
367         Class type = value.getClass();
368         if (type == int[].class) {
369             return ((int[])value).clone();
370         } else if (type == byte[].class) {
371             return ((byte[])value).clone();
372         } else if (type == short[].class) {
373             return ((short[])value).clone();
374         } else if (type == long[].class) {
375             return ((long[])value).clone();
376         } else if (type == char[].class) {
377             return ((char[])value).clone();
378         } else if (type == boolean[].class) {
379             return ((boolean[])value).clone();
380         } else if (type == float[].class) {
381             return ((float[])value).clone();
382         } else if (type == double[].class) {
383             return ((double[])value).clone();
384         }
385         return ((Object[])value).clone();
386     }
387 }
388