FileDocCategorySizeDatePackage
AnnotationFactory.javaAPI DocAndroid 1.5 API12965Wed May 06 22:41:04 BST 2009org.apache.harmony.lang.annotation

AnnotationFactory

public final class AnnotationFactory extends Object implements Serializable, InvocationHandler
The annotation implementation based on dynamically generated proxy instances. It conforms to all requirements stated in public APIs, see in particular {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement} and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}. Namely, annotation instances are immutable and serializable; they provide conforming access to annotation member values and required implementations of methods declared in Annotation interface.
see
android.lang.annotation.AnnotationMember
see
java.lang.annotation.Annotation
author
Alexey V. Varlamov, Serguei S. Zapreyev
version
$Revision$

Fields Summary
private static final transient Map
cache
private final Class
klazz
private AnnotationMember[]
elements
Constructors Summary
private AnnotationFactory(Class klzz, AnnotationMember[] values)
New instances should not be created directly, use factory method {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()} instead.

param
klzz class defining the annotation type
param
values actual element values

        klazz = klzz;
        AnnotationMember[] defs = getElementsDescription(klazz);
        if (values == null) {
            elements = defs;
        } else {
            //merge default and actual values
            elements = new AnnotationMember[defs.length];
            next: for (int i = elements.length - 1; i >= 0; i-- ){
                for (AnnotationMember val : values){
                    if (val.name.equals(defs[i].name)) {
                        elements[i] = val.setDefinition(defs[i]);
                        continue next;
                    }
                }
                elements[i] = defs[i];
            }
        }
    
Methods Summary
public static java.lang.annotation.AnnotationcreateAnnotation(java.lang.Class annotationType, AnnotationMember[] elements)
Provides a new annotation instance.

param
annotationType the annotation type definition
param
elements name-value pairs representing elements of the annotation
return
a new annotation instance

        AnnotationFactory antn = new AnnotationFactory(annotationType, elements); 
        return (Annotation)Proxy.newProxyInstance( annotationType.getClassLoader(), 
                new Class[]{annotationType}, antn);
    
public booleanequals(java.lang.Object obj)
Returns true if the specified object represents the same annotation instance. That is, if it implements the same annotation type and returns the same element values.
Note, actual underlying implementation mechanism does not matter - it may differ completely from this class.

return
true if the passed object is equivalent annotation instance, false otherwise.
see
android.lang.annotation.AnnotationMember#equals(Object)

        if (obj == this) {
            return true;
        }
        if (!klazz.isInstance(obj)) {
            return false;
        }
        Object handler = null;
        if (Proxy.isProxyClass(obj.getClass()) 
                && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) {
            AnnotationFactory other = (AnnotationFactory) handler;
            if (elements.length != other.elements.length) {
                return false;
            }
            next: for (AnnotationMember el1 : elements){
                for (AnnotationMember el2 : other.elements) {
                    if (el1.equals(el2)) {
                        continue next;
                    }
                }
                return false;
            }
            return true;
        } else {
            // encountered foreign annotation implementaton
            // so have to obtain element values via invocation 
            // of corresponding methods
            for (final AnnotationMember el : elements) {
                if (el.tag == ERROR) {
                    // undefined value is incomparable (transcendent)
                    return false;
                }
                try {
                    if (!el.definingMethod.isAccessible()) {
                        AccessController.doPrivileged(new PrivilegedAction<Object>(){
                            public Object run() {
                                try {
                                    el.definingMethod.setAccessible(true);
                                } catch (Exception ignore) {}
                                return null;
                            }
                        });
                    }
                    Object otherValue = el.definingMethod.invoke(obj);
                    if (otherValue != null ) {
                        if (el.tag == ARRAY) { 
                            if (!el.equalArrayValue(otherValue)) {
                                return false;
                            }
                        } else {
                            if (!el.value.equals(otherValue)) {
                                return false;
                            }
                        }
                    } else if (el.value != AnnotationMember.NO_VALUE) {
                        return false;
                    }
                } catch (Throwable e) {
                    return false;
                }
            }
            return true;
        }
    
public static AnnotationMember[]getElementsDescription(java.lang.Class annotationType)
Reflects specified annotation type and returns an array of member element definitions with default values.

    
                         
            
        AnnotationMember[] desc = cache.get(annotationType);
        if (desc == null) {
            if (!annotationType.isAnnotation()) {
                throw new IllegalArgumentException("Type is not annotation: " 
                        + annotationType.getName());
            }
            Method[] m = annotationType.getDeclaredMethods();
            desc = new AnnotationMember[m.length];
            int idx = 0;
            for(Method element : m) {
                String name = element.getName();
                Class<?> type = element.getReturnType();
                try {
                    desc[idx] = new AnnotationMember(name, 
                            element.getDefaultValue(), type, element);
                } catch (Throwable t) {
                    desc[idx] = new AnnotationMember(name, t, type, element);
                }
                idx++;
            }
            cache.put(annotationType, desc);
        }
        return desc;
    
public inthashCode()
Returns a hash code composed as a sum of hash codes of member elements, including elements with default values.

see
android.lang.annotation.AnnotationMember#hashCode()

        int hash = 0;
        for (AnnotationMember element : elements) {
            hash += element.hashCode();
        }
        return hash;
    
public java.lang.Objectinvoke(java.lang.Object proxy, java.lang.reflect.Method method, java.lang.Object[] args)
Processes a method invocation request to this annotation instance. Recognizes the methods declared in the {@link java.lang.annotation.Annotation java.lang.annotation.Annotation} interface, and member-defining methods of the implemented annotation type.

throws
IllegalArgumentException If the specified method is none of the above
return
the invocation result

        String name = method.getName();
        Class[] params = method.getParameterTypes();
        if (params.length == 0) {
            if ("annotationType".equals(name)) {
                return klazz;
            } else if ("toString".equals(name)) {
                return toString();
            } else if ("hashCode".equals(name)) {
                return hashCode();
            }
            
            // this must be element value request
            AnnotationMember element = null;
            for (AnnotationMember el : elements) {
                if (name.equals(el.name)) {
                    element = el;
                    break;
                }                
            }
            if (element == null || !method.equals(element.definingMethod)) {
                throw new IllegalArgumentException(method.toString());
            } else {
                Object value = element.validateValue();
                if (value == null) {
                    throw new IncompleteAnnotationException(klazz, name);
                }
                return value;
            }
        } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){
            return Boolean.valueOf(equals(args[0]));
        }
        throw new IllegalArgumentException(
                "Invalid method for annotation type: " + method);
    
private voidreadObject(java.io.ObjectInputStream os)
Reads the object, obtains actual member definitions for the annotation type, and merges deserialized values with the new definitions.

        os.defaultReadObject();
        // Annotation type members can be changed arbitrarily
        // So there may be zombi elements from the previous life; 
        // they hardly fit into this new annotation's incarnation, 
        // as we have no defining methods for them.
        // Reasonably just drop such elements, 
        // but seems better to keep them for compatibility 
        AnnotationMember[] defs = getElementsDescription(klazz);
        AnnotationMember[] old = elements;
        List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
                defs.length + old.length);
        nextOld: for (AnnotationMember el1 : old) {
            for (AnnotationMember el2 : defs) {
                if (el2.name.equals(el1.name)) {
                    continue nextOld;
                }
            }
            merged.add(el1); //phantom element
        }
        nextNew: for (AnnotationMember def : defs){
            for (AnnotationMember val : old){
                if (val.name.equals(def.name)) {
                    // nothing to do about cached errors (if any)
                    // anyway they remain relevant to values
                    merged.add(val.setDefinition(def));
                    continue nextNew;
                }
            }
            merged.add(def); // brand new element
        }  
        elements = merged.toArray(new AnnotationMember[merged.size()]);
    
public java.lang.StringtoString()
Provides detailed description of this annotation instance, including all member name-values pairs.

return
string representation of this annotation

        String res = "@" + klazz.getName() + "(";
        for(int i = 0; i < elements.length; i++) {
            if ( i != 0 ) {
                res += ", ";    
            }
            res += elements[i].toString();;
        }
        return res + ")";