Home | History | Annotate | Download | only in annotation
      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 org.apache.harmony.lang.annotation;
     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 import static org.apache.harmony.lang.annotation.AnnotationMember.ARRAY;
     33 import static org.apache.harmony.lang.annotation.AnnotationMember.ERROR;
     34 
     35 /**
     36  * The annotation implementation based on dynamically generated proxy instances.
     37  * It conforms to all requirements stated in public APIs, see in particular
     38  * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement}
     39  * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}.
     40  * Namely, annotation instances are immutable and serializable; they provide
     41  * conforming access to annotation member values and required implementations of
     42  * methods declared in Annotation interface.
     43  *
     44  * @see android.lang.annotation.AnnotationMember
     45  * @see java.lang.annotation.Annotation
     46  *
     47  * @author Alexey V. Varlamov, Serguei S. Zapreyev
     48  * @version $Revision$
     49  */
     50 @SuppressWarnings({"serial"})
     51 public final class AnnotationFactory implements InvocationHandler, Serializable {
     52 
     53     private static final transient
     54     Map<Class<? extends Annotation>, AnnotationMember[]>
     55     cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>();
     56 
     57     /**
     58      * Reflects specified annotation type and returns an array
     59      * of member element definitions with default values.
     60      */
     61     public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType ) {
     62         AnnotationMember[] desc = cache.get(annotationType);
     63         if (desc == null) {
     64             if (!annotationType.isAnnotation()) {
     65                 throw new IllegalArgumentException("Type is not annotation: "
     66                         + annotationType.getName());
     67             }
     68             Method[] m = annotationType.getDeclaredMethods();
     69             desc = new AnnotationMember[m.length];
     70             int idx = 0;
     71             for (Method element : m) {
     72                 String name = element.getName();
     73                 Class<?> type = element.getReturnType();
     74                 try {
     75                     desc[idx] = new AnnotationMember(name,
     76                             element.getDefaultValue(), type, element);
     77                 } catch (Throwable t) {
     78                     desc[idx] = new AnnotationMember(name, t, type, element);
     79                 }
     80                 idx++;
     81             }
     82             cache.put(annotationType, desc);
     83         }
     84         return desc;
     85     }
     86 
     87     /**
     88      * Provides a new annotation instance.
     89      * @param annotationType the annotation type definition
     90      * @param elements name-value pairs representing elements of the annotation
     91      * @return a new annotation instance
     92      */
     93     public static Annotation createAnnotation(
     94             Class<? extends Annotation> annotationType,
     95             AnnotationMember[] elements)
     96     {
     97         AnnotationFactory antn = new AnnotationFactory(annotationType, elements);
     98         return (Annotation)Proxy.newProxyInstance( annotationType.getClassLoader(),
     99                 new Class[]{annotationType}, antn);
    100     }
    101 
    102     private final Class<? extends Annotation> klazz;
    103     private AnnotationMember[] elements;
    104 
    105     /**
    106      * New instances should not be created directly, use factory method
    107      * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()}
    108      * instead.
    109      *
    110      * @param klzz class defining the annotation type
    111      * @param values actual element values
    112      */
    113     private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) {
    114         klazz = klzz;
    115         AnnotationMember[] defs = getElementsDescription(klazz);
    116         if (values == null) {
    117             elements = defs;
    118         } else {
    119             //merge default and actual values
    120             elements = new AnnotationMember[defs.length];
    121             next: for (int i = elements.length - 1; i >= 0; i-- ){
    122                 for (AnnotationMember val : values){
    123                     if (val.name.equals(defs[i].name)) {
    124                         elements[i] = val.setDefinition(defs[i]);
    125                         continue next;
    126                     }
    127                 }
    128                 elements[i] = defs[i];
    129             }
    130         }
    131     }
    132 
    133     /**
    134      * Reads the object, obtains actual member definitions for the annotation type,
    135      * and merges deserialized values with the new definitions.
    136      */
    137     private void readObject(ObjectInputStream os) throws IOException,
    138     ClassNotFoundException {
    139         os.defaultReadObject();
    140         // Annotation type members can be changed arbitrarily
    141         // So there may be zombi elements from the previous life;
    142         // they hardly fit into this new annotation's incarnation,
    143         // as we have no defining methods for them.
    144         // Reasonably just drop such elements,
    145         // but seems better to keep them for compatibility
    146         AnnotationMember[] defs = getElementsDescription(klazz);
    147         AnnotationMember[] old = elements;
    148         List<AnnotationMember> merged = new ArrayList<AnnotationMember>(
    149                 defs.length + old.length);
    150         nextOld: for (AnnotationMember el1 : old) {
    151             for (AnnotationMember el2 : defs) {
    152                 if (el2.name.equals(el1.name)) {
    153                     continue nextOld;
    154                 }
    155             }
    156             merged.add(el1); //phantom element
    157         }
    158         nextNew: for (AnnotationMember def : defs){
    159             for (AnnotationMember val : old){
    160                 if (val.name.equals(def.name)) {
    161                     // nothing to do about cached errors (if any)
    162                     // anyway they remain relevant to values
    163                     merged.add(val.setDefinition(def));
    164                     continue nextNew;
    165                 }
    166             }
    167             merged.add(def); // brand new element
    168         }
    169         elements = merged.toArray(new AnnotationMember[merged.size()]);
    170     }
    171 
    172     /**
    173      * Returns true if the specified object represents the same annotation instance.
    174      * That is, if it implements the same annotation type and
    175      * returns the same element values.
    176      * <br>Note, actual underlying implementation mechanism does not matter - it may
    177      * differ completely from this class.
    178      * @return true if the passed object is equivalent annotation instance,
    179      * false otherwise.
    180      * @see android.lang.annotation.AnnotationMember#equals(Object)
    181      */
    182     public boolean equals(Object obj) {
    183         if (obj == this) {
    184             return true;
    185         }
    186         if (!klazz.isInstance(obj)) {
    187             return false;
    188         }
    189         Object handler = null;
    190         if (Proxy.isProxyClass(obj.getClass())
    191                 && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) {
    192             AnnotationFactory other = (AnnotationFactory) handler;
    193             if (elements.length != other.elements.length) {
    194                 return false;
    195             }
    196             next: for (AnnotationMember el1 : elements){
    197                 for (AnnotationMember el2 : other.elements) {
    198                     if (el1.equals(el2)) {
    199                         continue next;
    200                     }
    201                 }
    202                 return false;
    203             }
    204             return true;
    205         } else {
    206             // encountered foreign annotation implementaton
    207             // so have to obtain element values via invocation
    208             // of corresponding methods
    209             for (final AnnotationMember el : elements) {
    210                 if (el.tag == ERROR) {
    211                     // undefined value is incomparable (transcendent)
    212                     return false;
    213                 }
    214                 try {
    215                     if (!el.definingMethod.isAccessible()) {
    216                         el.definingMethod.setAccessible(true);
    217                     }
    218                     Object otherValue = el.definingMethod.invoke(obj);
    219                     if (otherValue != null ) {
    220                         if (el.tag == ARRAY) {
    221                             if (!el.equalArrayValue(otherValue)) {
    222                                 return false;
    223                             }
    224                         } else {
    225                             if (!el.value.equals(otherValue)) {
    226                                 return false;
    227                             }
    228                         }
    229                     } else if (el.value != AnnotationMember.NO_VALUE) {
    230                         return false;
    231                     }
    232                 } catch (Throwable e) {
    233                     return false;
    234                 }
    235             }
    236             return true;
    237         }
    238     }
    239 
    240     /**
    241      * Returns a hash code composed as a sum of hash codes of member elements,
    242      * including elements with default values.
    243      * @see android.lang.annotation.AnnotationMember#hashCode()
    244      */
    245     public int hashCode() {
    246         int hash = 0;
    247         for (AnnotationMember element : elements) {
    248             hash += element.hashCode();
    249         }
    250         return hash;
    251     }
    252 
    253     /**
    254      * Provides detailed description of this annotation instance,
    255      * including all member name-values pairs.
    256      * @return string representation of this annotation
    257      */
    258     public String toString() {
    259         StringBuilder result = new StringBuilder();
    260         result.append('@');
    261         result.append(klazz.getName());
    262         result.append('(');
    263         for (int i = 0; i < elements.length; ++i) {
    264             if (i != 0) {
    265                 result.append(", ");
    266             }
    267             result.append(elements[i]);
    268         }
    269         result.append(')');
    270         return result.toString();
    271     }
    272 
    273     /**
    274      * Processes a method invocation request to this annotation instance.
    275      * Recognizes the methods declared in the
    276      * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}
    277      * interface, and member-defining methods of the implemented annotation type.
    278      * @throws IllegalArgumentException If the specified method is none of the above
    279      * @return the invocation result
    280      */
    281     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    282     {
    283         String name = method.getName();
    284         Class[] params = method.getParameterTypes();
    285         if (params.length == 0) {
    286             if ("annotationType".equals(name)) {
    287                 return klazz;
    288             } else if ("toString".equals(name)) {
    289                 return toString();
    290             } else if ("hashCode".equals(name)) {
    291                 return hashCode();
    292             }
    293 
    294             // this must be element value request
    295             AnnotationMember element = null;
    296             for (AnnotationMember el : elements) {
    297                 if (name.equals(el.name)) {
    298                     element = el;
    299                     break;
    300                 }
    301             }
    302             if (element == null || !method.equals(element.definingMethod)) {
    303                 throw new IllegalArgumentException(method.toString());
    304             } else {
    305                 Object value = element.validateValue();
    306                 if (value == null) {
    307                     throw new IncompleteAnnotationException(klazz, name);
    308                 }
    309                 return value;
    310             }
    311         } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){
    312             return Boolean.valueOf(equals(args[0]));
    313         }
    314         throw new IllegalArgumentException(
    315                 "Invalid method for annotation type: " + method);
    316     }
    317 }
    318