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.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.Serializable;
023: import java.lang.annotation.Annotation;
024: import java.lang.annotation.IncompleteAnnotationException;
025: import java.security.AccessController;
026: import java.security.PrivilegedAction;
027: import java.util.ArrayList;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.WeakHashMap;
031: import java.lang.reflect.InvocationHandler;
032: import java.lang.reflect.Method;
033: import java.lang.reflect.Proxy;
034:
035: import static org.apache.harmony.lang.annotation.AnnotationMember.ARRAY;
036: import static org.apache.harmony.lang.annotation.AnnotationMember.ERROR;
037:
038: /**
039: * The annotation implementation based on dynamically generated proxy instances.
040: * It conforms to all requirements stated in public APIs, see in particular
041: * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
042: * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
043: * Namely, annotation instances are immutable and serializable; they provide
044: * conforming access to annotation member values and required implementations of
045: * methods declared in Annotation interface.
046: *
047: * @see org.apache.harmony.lang.annotation.AnnotationMember
048: * @see java.lang.annotation.Annotation
049: *
050: * @author Alexey V. Varlamov, Serguei S. Zapreyev
051: * @version $Revision$
052: */
053: @SuppressWarnings({"serial"})
054: public final class AnnotationFactory implements InvocationHandler,
055: Serializable {
056:
057: private static final transient Map<Class<? extends Annotation>, AnnotationMember[]> cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>();
058:
059: /**
060: * Reflects specified annotation type and returns an array
061: * of member element definitions with default values.
062: */
063: public static AnnotationMember[] getElementsDescription(
064: Class<? extends Annotation> annotationType) {
065: AnnotationMember[] desc = cache.get(annotationType);
066: if (desc == null) {
067: if (!annotationType.isAnnotation()) {
068: throw new IllegalArgumentException(
069: "Type is not annotation: "
070: + annotationType.getName());
071: }
072: Method[] m = annotationType.getDeclaredMethods();
073: desc = new AnnotationMember[m.length];
074: int idx = 0;
075: for (Method element : m) {
076: String name = element.getName();
077: Class<?> type = element.getReturnType();
078: try {
079: desc[idx] = new AnnotationMember(name, element
080: .getDefaultValue(), type, element);
081: } catch (Throwable t) {
082: desc[idx] = new AnnotationMember(name, t, type,
083: element);
084: }
085: idx++;
086: }
087: cache.put(annotationType, desc);
088: }
089: return desc;
090: }
091:
092: /**
093: * Provides a new annotation instance.
094: * @param annotationType the annotation type definition
095: * @param elements name-value pairs representing elements of the annotation
096: * @return a new annotation instance
097: */
098: public static Annotation createAnnotation(
099: Class<? extends Annotation> annotationType,
100: AnnotationMember[] elements) {
101: AnnotationFactory antn = new AnnotationFactory(annotationType,
102: elements);
103: return (Annotation) Proxy
104: .newProxyInstance(annotationType.getClassLoader(),
105: new Class[] { annotationType }, antn);
106: }
107:
108: private final Class<? extends Annotation> klazz;
109: private AnnotationMember[] elements;
110:
111: /**
112: * New instances should not be created directly, use factory method
113: * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
114: * instead.
115: *
116: * @param klzz class defining the annotation type
117: * @param values actual element values
118: */
119: private AnnotationFactory(Class<? extends Annotation> klzz,
120: AnnotationMember[] values) {
121: klazz = klzz;
122: AnnotationMember[] defs = getElementsDescription(klazz);
123: if (values == null) {
124: elements = defs;
125: } else {
126: //merge default and actual values
127: elements = new AnnotationMember[defs.length];
128: next: for (int i = elements.length - 1; i >= 0; i--) {
129: for (AnnotationMember val : values) {
130: if (val.name.equals(defs[i].name)) {
131: elements[i] = val.setDefinition(defs[i]);
132: continue next;
133: }
134: }
135: elements[i] = defs[i];
136: }
137: }
138: }
139:
140: /**
141: * Reads the object, obtains actual member definitions for the annotation type,
142: * and merges deserialized values with the new definitions.
143: */
144: private void readObject(ObjectInputStream os) throws IOException,
145: ClassNotFoundException {
146: os.defaultReadObject();
147: // Annotation type members can be changed arbitrarily
148: // So there may be zombi elements from the previous life;
149: // they hardly fit into this new annotation's incarnation,
150: // as we have no defining methods for them.
151: // Reasonably just drop such elements,
152: // but seems better to keep them for compatibility
153: AnnotationMember[] defs = getElementsDescription(klazz);
154: AnnotationMember[] old = elements;
155: List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
156: defs.length + old.length);
157: nextOld: for (AnnotationMember el1 : old) {
158: for (AnnotationMember el2 : defs) {
159: if (el2.name.equals(el1.name)) {
160: continue nextOld;
161: }
162: }
163: merged.add(el1); //phantom element
164: }
165: nextNew: for (AnnotationMember def : defs) {
166: for (AnnotationMember val : old) {
167: if (val.name.equals(def.name)) {
168: // nothing to do about cached errors (if any)
169: // anyway they remain relevant to values
170: merged.add(val.setDefinition(def));
171: continue nextNew;
172: }
173: }
174: merged.add(def); // brand new element
175: }
176: elements = merged.toArray(new AnnotationMember[merged.size()]);
177: }
178:
179: /**
180: * Returns true if the specified object represents the same annotation instance.
181: * That is, if it implements the same annotation type and
182: * returns the same element values.
183: * <br>Note, actual underlying implementation mechanism does not matter - it may
184: * differ completely from this class.
185: * @return true if the passed object is equivalent annotation instance,
186: * false otherwise.
187: * @see org.apache.harmony.lang.annotation.AnnotationMember#equals(Object)
188: */
189: public boolean equals(Object obj) {
190: if (obj == this ) {
191: return true;
192: }
193: if (!klazz.isInstance(obj)) {
194: return false;
195: }
196: Object handler = null;
197: if (Proxy.isProxyClass(obj.getClass())
198: && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory) {
199: AnnotationFactory other = (AnnotationFactory) handler;
200: if (elements.length != other.elements.length) {
201: return false;
202: }
203: next: for (AnnotationMember el1 : elements) {
204: for (AnnotationMember el2 : other.elements) {
205: if (el1.equals(el2)) {
206: continue next;
207: }
208: }
209: return false;
210: }
211: return true;
212: } else {
213: // encountered foreign annotation implementaton
214: // so have to obtain element values via invocation
215: // of corresponding methods
216: for (final AnnotationMember el : elements) {
217: if (el.tag == ERROR) {
218: // undefined value is incomparable (transcendent)
219: return false;
220: }
221: try {
222: if (!el.definingMethod.isAccessible()) {
223: AccessController
224: .doPrivileged(new PrivilegedAction<Object>() {
225: public Object run() {
226: try {
227: el.definingMethod
228: .setAccessible(true);
229: } catch (Exception ignore) {
230: }
231: return null;
232: }
233: });
234: }
235: Object otherValue = el.definingMethod.invoke(obj);
236: if (otherValue != null) {
237: if (el.tag == ARRAY) {
238: if (!el.equalArrayValue(otherValue)) {
239: return false;
240: }
241: } else {
242: if (!el.value.equals(otherValue)) {
243: return false;
244: }
245: }
246: } else if (el.value != AnnotationMember.NO_VALUE) {
247: return false;
248: }
249: } catch (Throwable e) {
250: return false;
251: }
252: }
253: return true;
254: }
255: }
256:
257: /**
258: * Returns a hash code composed as a sum of hash codes of member elements,
259: * including elements with default values.
260: * @see org.apache.harmony.lang.annotation.AnnotationMember#hashCode()
261: */
262: public int hashCode() {
263: int hash = 0;
264: for (AnnotationMember element : elements) {
265: hash += element.hashCode();
266: }
267: return hash;
268: }
269:
270: /**
271: * Provides detailed description of this annotation instance,
272: * including all member name-values pairs.
273: * @return string representation of this annotation
274: */
275: public String toString() {
276: String res = "@" + klazz.getName() + "(";
277: for (int i = 0; i < elements.length; i++) {
278: if (i != 0) {
279: res += ", ";
280: }
281: res += elements[i].toString();
282: ;
283: }
284: return res + ")";
285: }
286:
287: /**
288: * Processes a method invocation request to this annotation instance.
289: * Recognizes the methods declared in the
290: * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
291: * interface, and member-defining methods of the implemented annotation type.
292: * @throws IllegalArgumentException If the specified method is none of the above
293: * @return the invocation result
294: */
295: public Object invoke(Object proxy, Method method, Object[] args)
296: throws Throwable {
297: String name = method.getName();
298: Class[] params = method.getParameterTypes();
299: if (params.length == 0) {
300: if ("annotationType".equals(name)) {
301: return klazz;
302: } else if ("toString".equals(name)) {
303: return toString();
304: } else if ("hashCode".equals(name)) {
305: return hashCode();
306: }
307:
308: // this must be element value request
309: AnnotationMember element = null;
310: for (AnnotationMember el : elements) {
311: if (name.equals(el.name)) {
312: element = el;
313: break;
314: }
315: }
316: if (element == null
317: || !method.equals(element.definingMethod)) {
318: throw new IllegalArgumentException(method.toString());
319: } else {
320: Object value = element.validateValue();
321: if (value == null) {
322: throw new IncompleteAnnotationException(klazz, name);
323: }
324: return value;
325: }
326: } else if (params.length == 1 && params[0] == Object.class
327: && "equals".equals(name)) {
328: return Boolean.valueOf(equals(args[0]));
329: }
330: throw new IllegalArgumentException(
331: "Invalid method for annotation type: " + method);
332: }
333: }
|