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