001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.lang.annotation;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.ByteArrayOutputStream;
022: import java.io.ObjectInputStream;
023: import java.io.ObjectOutputStream;
024: import java.io.Serializable;
025: import java.lang.annotation.AnnotationTypeMismatchException;
026: import java.lang.reflect.Array;
027: import java.lang.reflect.Method;
028: import java.util.Arrays;
029:
030: /**
031: * This class represents member element of an annotation.
032: * It consists of name and value, supplemented with element
033: * definition information (such as declared type of element).
034: * <br>The value may be one of the following types:
035: * <ul>
036: * <li> boxed primitive
037: * <li> Class
038: * <li> enum constant
039: * <li> annotation (nested)
040: * <li> one-dimensional array of the above
041: * <li> Throwable
042: * </ul>
043: * The last type is specific for this implementation; a Throwable value
044: * means that the error occured during parsing or resolution of corresponding
045: * class-data structures and throwing is delayed until the element
046: * is requested for value.
047: *
048: * @see org.apache.harmony.lang.annotation.AnnotationFactory
049: *
050: * @author Alexey V. Varlamov, Serguei S. Zapreyev
051: * @version $Revision$
052: */
053: @SuppressWarnings({"serial"})
054: public class AnnotationMember implements Serializable {
055:
056: /**
057: * Tag description of a Throwable value type.
058: */
059: protected static final char ERROR = '!';
060:
061: /**
062: * Tag description of an array value type.
063: */
064: protected static final char ARRAY = '[';
065:
066: /**
067: * Tag description of all value types except arrays and Throwables.
068: */
069: protected static final char OTHER = '*';
070:
071: // public static final char INT = 'I';
072: // public static final char CHAR = 'C';
073: // public static final char DOUBLE = 'D';
074: // public static final char FLOAT = 'F';
075: // public static final char BYTE = 'B';
076: // public static final char LONG = 'J';
077: // public static final char SHORT = 'S';
078: // public static final char BOOL = 'Z';
079: // public static final char CLASS = 'c';
080: // public static final char ENUM = 'e';
081: // public static final char ANTN = '@';
082:
083: private enum DefaultValues {
084: NO_VALUE
085: }
086:
087: /**
088: * Singleton representing missing element value.
089: */
090: protected static final Object NO_VALUE = DefaultValues.NO_VALUE;
091:
092: protected final String name;
093: protected final Object value; // a primitive value is wrapped to the corresponding wrapper class
094: protected final char tag;
095: // no sense to serialize definition info as it can be changed arbitrarily
096: protected transient Class<?> elementType;
097: protected transient Method definingMethod;
098:
099: /**
100: * Creates a new element with specified name and value.
101: * Definition info will be provided later when this
102: * element becomes actual annotation member.
103: * @param name element name, must not be null
104: * @param val element value, should be of addmissible type,
105: * as specified in the description of this class
106: *
107: * @see #setDefinition(AnnotationMember)
108: */
109: public AnnotationMember(String name, Object val) {
110: this .name = name;
111: value = val == null ? NO_VALUE : val;
112: if (value instanceof Throwable) {
113: tag = ERROR;
114: } else if (value.getClass().isArray()) {
115: tag = ARRAY;
116: } else {
117: tag = OTHER;
118: }
119: }
120:
121: /**
122: * Creates the completely defined element.
123: * @param name element name, must not be null
124: * @param value element value, should be of addmissible type,
125: * as specified in the description of this class
126: * @param m element-defining method, reflected on the annotation type
127: * @param type declared type of this element
128: * (return type of the defining method)
129: */
130: public AnnotationMember(String name, Object val, Class type,
131: Method m) {
132: this (name, val);
133:
134: definingMethod = m;
135:
136: if (type == int.class) {
137: elementType = Integer.class;
138: } else if (type == boolean.class) {
139: elementType = Boolean.class;
140: } else if (type == char.class) {
141: elementType = Character.class;
142: } else if (type == float.class) {
143: elementType = Float.class;
144: } else if (type == double.class) {
145: elementType = Double.class;
146: } else if (type == long.class) {
147: elementType = Long.class;
148: } else if (type == short.class) {
149: elementType = Short.class;
150: } else if (type == byte.class) {
151: elementType = Byte.class;
152: } else {
153: elementType = type;
154: }
155: }
156:
157: /**
158: * Fills in element's definition info and returns this.
159: */
160: protected AnnotationMember setDefinition(AnnotationMember copy) {
161: definingMethod = copy.definingMethod;
162: elementType = copy.elementType;
163: return this ;
164: }
165:
166: /**
167: * Returns readable description of this annotation value.
168: */
169: public String toString() {
170: if (tag == ARRAY) {
171: StringBuilder sb = new StringBuilder(80);
172: sb.append(name).append("=[");
173: int len = Array.getLength(value);
174: for (int i = 0; i < len; i++) {
175: if (i != 0)
176: sb.append(", ");
177: sb.append(Array.get(value, i));
178: }
179: return sb.append("]").toString();
180: } else {
181: return name + "=" + value;
182: }
183: }
184:
185: /**
186: * Returns true if the specified object represents equal element
187: * (equivalent name-value pair).
188: * <br> A special case is the contained Throwable value; it is considered
189: * transcendent so no other element would be equal.
190: * @return true if passed object is equivalent element representation,
191: * false otherwise
192: * @see #equalArrayValue(Object)
193: * @see java.lang.annotation.Annotation#equals(Object)
194: */
195: public boolean equals(Object obj) {
196: if (obj == this ) {
197: // not a mere optimization,
198: // this is needed for consistency with hashCode()
199: return true;
200: }
201: if (obj instanceof AnnotationMember) {
202: AnnotationMember that = (AnnotationMember) obj;
203: if (name.equals(that.name) && tag == that.tag) {
204: if (tag == ARRAY) {
205: return equalArrayValue(that.value);
206: } else if (tag == ERROR) {
207: // undefined value is incomparable (transcendent)
208: return false;
209: } else {
210: return value.equals(that.value);
211: }
212: }
213: }
214: return false;
215: }
216:
217: /**
218: * Returns true if the contained value and a passed object are equal arrays,
219: * false otherwise. Appropriate overloaded method of Arrays.equals()
220: * is used for equality testing.
221: * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[])
222: * @return true if the value is array and is equal to specified object,
223: * false otherwise
224: */
225: public boolean equalArrayValue(Object otherValue) {
226: if (value instanceof Object[] && otherValue instanceof Object[]) {
227: return Arrays.equals((Object[]) value,
228: (Object[]) otherValue);
229: }
230: Class type = value.getClass();
231: if (type != otherValue.getClass()) {
232: return false;
233: }
234: if (type == int[].class) {
235: return Arrays.equals((int[]) value, (int[]) otherValue);
236: } else if (type == byte[].class) {
237: return Arrays.equals((byte[]) value, (byte[]) otherValue);
238: } else if (type == short[].class) {
239: return Arrays.equals((short[]) value, (short[]) otherValue);
240: } else if (type == long[].class) {
241: return Arrays.equals((long[]) value, (long[]) otherValue);
242: } else if (type == char[].class) {
243: return Arrays.equals((char[]) value, (char[]) otherValue);
244: } else if (type == boolean[].class) {
245: return Arrays.equals((boolean[]) value,
246: (boolean[]) otherValue);
247: } else if (type == float[].class) {
248: return Arrays.equals((float[]) value, (float[]) otherValue);
249: } else if (type == double[].class) {
250: return Arrays.equals((double[]) value,
251: (double[]) otherValue);
252: }
253: return false;
254: }
255:
256: /**
257: * Computes hash code of this element. The formula is as follows:
258: * <code> (name.hashCode() * 127) ^ value.hashCode() </code>
259: * <br>If value is an array, one of overloaded Arrays.hashCode()
260: * methods is used.
261: * @return the hash code
262: * @see java.util.Arrays#hashCode(java.lang.Object[])
263: * @see java.lang.annotation.Annotation#hashCode()
264: */
265: public int hashCode() {
266: int hash = name.hashCode() * 127;
267: if (tag == ARRAY) {
268: Class type = value.getClass();
269: if (type == int[].class) {
270: return hash ^ Arrays.hashCode((int[]) value);
271: } else if (type == byte[].class) {
272: return hash ^ Arrays.hashCode((byte[]) value);
273: } else if (type == short[].class) {
274: return hash ^ Arrays.hashCode((short[]) value);
275: } else if (type == long[].class) {
276: return hash ^ Arrays.hashCode((long[]) value);
277: } else if (type == char[].class) {
278: return hash ^ Arrays.hashCode((char[]) value);
279: } else if (type == boolean[].class) {
280: return hash ^ Arrays.hashCode((boolean[]) value);
281: } else if (type == float[].class) {
282: return hash ^ Arrays.hashCode((float[]) value);
283: } else if (type == double[].class) {
284: return hash ^ Arrays.hashCode((double[]) value);
285: }
286: return hash ^ Arrays.hashCode((Object[]) value);
287: } else {
288: return hash ^ value.hashCode();
289: }
290: }
291:
292: /**
293: * Throws contained error (if any) with a renewed stack trace.
294: */
295: public void rethrowError() throws Throwable {
296: if (tag == ERROR) {
297: // need to throw cloned exception for thread safety
298: // besides it is better to provide actual stack trace
299: // rather than recorded during parsing
300:
301: // first check for expected types
302: if (value instanceof TypeNotPresentException) {
303: TypeNotPresentException tnpe = (TypeNotPresentException) value;
304: throw new TypeNotPresentException(tnpe.typeName(), tnpe
305: .getCause());
306: } else if (value instanceof EnumConstantNotPresentException) {
307: EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException) value;
308: throw new EnumConstantNotPresentException(ecnpe
309: .enumType(), ecnpe.constantName());
310: } else if (value instanceof ArrayStoreException) {
311: ArrayStoreException ase = (ArrayStoreException) value;
312: throw new ArrayStoreException(ase.getMessage());
313: }
314: // got some other error, have to go with deep cloning
315: // via serialization mechanism
316: Throwable error = (Throwable) value;
317: StackTraceElement[] ste = error.getStackTrace();
318: ByteArrayOutputStream bos = new ByteArrayOutputStream(
319: ste == null ? 512 : (ste.length + 1) * 80);
320: ObjectOutputStream oos = new ObjectOutputStream(bos);
321: oos.writeObject(error);
322: oos.flush();
323: oos.close();
324: ByteArrayInputStream bis = new ByteArrayInputStream(bos
325: .toByteArray());
326: ObjectInputStream ois = new ObjectInputStream(bis);
327: error = (Throwable) ois.readObject();
328: ois.close();
329:
330: throw error;
331: }
332: }
333:
334: /**
335: * Validates contained value against its member definition
336: * and if ok returns the value.
337: * Otherwise, if the value type mismatches definition
338: * or the value itself describes an error,
339: * throws appropriate exception.
340: * <br> Note, this method may return null if this element was constructed
341: * with such value.
342: *
343: * @see #rethrowError()
344: * @see #copyValue()
345: * @return actual valid value or null if no value
346: */
347: public Object validateValue() throws Throwable {
348: if (tag == ERROR) {
349: rethrowError();
350: }
351: if (value == NO_VALUE) {
352: return null;
353: }
354: if (elementType == value.getClass()
355: || elementType.isInstance(value)) { // nested annotation value
356: return copyValue();
357: } else {
358: throw new AnnotationTypeMismatchException(definingMethod,
359: value.getClass().getName());
360: }
361:
362: }
363:
364: /**
365: * Provides mutation-safe access to contained value. That is, caller is free
366: * to modify the returned value, it will not affect the contained data value.
367: * @return cloned value if it is mutable or the original immutable value
368: */
369: public Object copyValue() throws Throwable {
370: if (tag != ARRAY || Array.getLength(value) == 0) {
371: return value;
372: }
373: Class type = value.getClass();
374: if (type == int[].class) {
375: return ((int[]) value).clone();
376: } else if (type == byte[].class) {
377: return ((byte[]) value).clone();
378: } else if (type == short[].class) {
379: return ((short[]) value).clone();
380: } else if (type == long[].class) {
381: return ((long[]) value).clone();
382: } else if (type == char[].class) {
383: return ((char[]) value).clone();
384: } else if (type == boolean[].class) {
385: return ((boolean[]) value).clone();
386: } else if (type == float[].class) {
387: return ((float[]) value).clone();
388: } else if (type == double[].class) {
389: return ((double[]) value).clone();
390: }
391: return ((Object[]) value).clone();
392: }
393: }
|