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.ByteArrayInputStream;
     21 import java.io.ByteArrayOutputStream;
     22 import java.io.ObjectInputStream;
     23 import java.io.ObjectOutputStream;
     24 import java.io.Serializable;
     25 import java.lang.annotation.AnnotationTypeMismatchException;
     26 import java.lang.reflect.Array;
     27 import java.lang.reflect.Method;
     28 import java.util.Arrays;
     29 
     30 /**
     31  * This class represents member element of an annotation.
     32  * It consists of name and value, supplemented with element
     33  * definition information (such as declared type of element).
     34  * <br>The value may be one of the following types:
     35  * <ul>
     36  * <li> boxed primitive
     37  * <li> Class
     38  * <li> enum constant
     39  * <li> annotation (nested)
     40  * <li> one-dimensional array of the above
     41  * <li> Throwable
     42  * </ul>
     43  * The last type is specific for this implementation; a Throwable value
     44  * means that the error occured during parsing or resolution of corresponding
     45  * class-data structures and throwing is delayed until the element
     46  * is requested for value.
     47  *
     48  * @see AnnotationFactory
     49  *
     50  * @author Alexey V. Varlamov, Serguei S. Zapreyev
     51  * @version $Revision$
     52  */
     53 @SuppressWarnings({"serial"})
     54 public final class AnnotationMember implements Serializable {
     55 
     56     /**
     57      * Tag description of a Throwable value type.
     58      */
     59     protected static final char ERROR = '!';
     60 
     61     /**
     62      * Tag description of an array value type.
     63      */
     64     protected static final char ARRAY = '[';
     65 
     66     /**
     67      * Tag description of all value types except arrays and Throwables.
     68      */
     69     protected static final char OTHER = '*';
     70 
     71 //    public static final char INT = 'I';
     72 //    public static final char CHAR = 'C';
     73 //    public static final char DOUBLE = 'D';
     74 //    public static final char FLOAT = 'F';
     75 //    public static final char BYTE = 'B';
     76 //    public static final char LONG = 'J';
     77 //    public static final char SHORT = 'S';
     78 //    public static final char BOOL = 'Z';
     79 //    public static final char CLASS = 'c';
     80 //    public static final char ENUM = 'e';
     81 //    public static final char ANTN = '@';
     82 
     83     private enum DefaultValues {NO_VALUE}
     84 
     85     /**
     86      * Singleton representing missing element value.
     87      */
     88     protected static final Object NO_VALUE = DefaultValues.NO_VALUE;
     89 
     90     protected final String name;
     91     protected final Object value; // a primitive value is wrapped to the corresponding wrapper class
     92     protected final char tag;
     93     // no sense to serialize definition info as it can be changed arbitrarily
     94     protected transient Class<?> elementType;
     95     protected transient Method definingMethod;
     96 
     97 
     98     /**
     99      * Creates a new element with specified name and value.
    100      * Definition info will be provided later when this
    101      * element becomes actual annotation member.
    102      * @param name element name, must not be null
    103      * @param val element value, should be of addmissible type,
    104      * as specified in the description of this class
    105      *
    106      * @see #setDefinition(AnnotationMember)
    107      */
    108     public AnnotationMember(String name, Object val) {
    109         this.name = name;
    110         value = val == null ? NO_VALUE : val;
    111         if (value instanceof Throwable) {
    112             tag = ERROR;
    113         } else if (value.getClass().isArray()) {
    114             tag = ARRAY;
    115         } else {
    116             tag = OTHER;
    117         }
    118     }
    119 
    120     /**
    121      * Creates the completely defined element.
    122      * @param name element name, must not be null
    123      * @param value element value, should be of addmissible type,
    124      * as specified in the description of this class
    125      * @param m element-defining method, reflected on the annotation type
    126      * @param type declared type of this element
    127      * (return type of the defining method)
    128      */
    129     public AnnotationMember(String name, Object val, Class type, Method m) {
    130         this(name, val);
    131 
    132         definingMethod = m;
    133 
    134         if (type == int.class) {
    135             elementType = Integer.class;
    136         } else if (type == boolean.class) {
    137             elementType = Boolean.class;
    138         } else if (type == char.class) {
    139             elementType = Character.class;
    140         } else if (type == float.class) {
    141             elementType = Float.class;
    142         } else if (type == double.class) {
    143             elementType = Double.class;
    144         } else if (type == long.class) {
    145             elementType = Long.class;
    146         } else if (type == short.class) {
    147             elementType = Short.class;
    148         } else if (type == byte.class) {
    149             elementType = Byte.class;
    150         } else {
    151             elementType = type;
    152         }
    153     }
    154 
    155     /**
    156      * Fills in element's definition info and returns this.
    157      */
    158     protected AnnotationMember setDefinition(AnnotationMember copy) {
    159         definingMethod = copy.definingMethod;
    160         elementType = copy.elementType;
    161         return this;
    162     }
    163 
    164     /**
    165      * Returns readable description of this annotation value.
    166      */
    167     public String toString() {
    168         if (tag == ARRAY) {
    169             StringBuilder sb = new StringBuilder(80);
    170             sb.append(name).append("=[");
    171             int len = Array.getLength(value);
    172             for (int i = 0; i < len; i++) {
    173                 if (i != 0) sb.append(", ");
    174                 sb.append(Array.get(value, i));
    175             }
    176             return sb.append("]").toString();
    177         } else {
    178             return name+ "=" +value;
    179         }
    180     }
    181 
    182     /**
    183      * Returns true if the specified object represents equal element
    184      * (equivalent name-value pair).
    185      * <br> A special case is the contained Throwable value; it is considered
    186      * transcendent so no other element would be equal.
    187      * @return true if passed object is equivalent element representation,
    188      * false otherwise
    189      * @see #equalArrayValue(Object)
    190      * @see java.lang.annotation.Annotation#equals(Object)
    191      */
    192     public boolean equals(Object obj) {
    193         if (obj == this) {
    194             // not a mere optimization,
    195             // this is needed for consistency with hashCode()
    196             return true;
    197         }
    198         if (obj instanceof AnnotationMember) {
    199             AnnotationMember that = (AnnotationMember)obj;
    200             if (name.equals(that.name) && tag == that.tag) {
    201                 if (tag == ARRAY) {
    202                     return equalArrayValue(that.value);
    203                 } else if (tag == ERROR) {
    204                     // undefined value is incomparable (transcendent)
    205                     return false;
    206                 } else {
    207                     return value.equals(that.value);
    208                 }
    209             }
    210         }
    211         return false;
    212     }
    213 
    214     /**
    215      * Returns true if the contained value and a passed object are equal arrays,
    216      * false otherwise. Appropriate overloaded method of Arrays.equals()
    217      * is used for equality testing.
    218      * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[])
    219      * @return true if the value is array and is equal to specified object,
    220      * false otherwise
    221      */
    222     public boolean equalArrayValue(Object otherValue) {
    223         if (value instanceof Object[] && otherValue instanceof Object[]) {
    224             return Arrays.equals((Object[])value, (Object[])otherValue);
    225         }
    226         Class type = value.getClass();
    227         if (type != otherValue.getClass()) {
    228             return false;
    229         }
    230         if (type == int[].class) {
    231             return Arrays.equals((int[])value, (int[])otherValue);
    232         } else if (type == byte[].class) {
    233             return Arrays.equals((byte[])value, (byte[])otherValue);
    234         } else if (type == short[].class) {
    235             return Arrays.equals((short[])value, (short[])otherValue);
    236         } else if (type == long[].class) {
    237             return Arrays.equals((long[])value, (long[])otherValue);
    238         } else if (type == char[].class) {
    239             return Arrays.equals((char[])value, (char[])otherValue);
    240         } else if (type == boolean[].class) {
    241             return Arrays.equals((boolean[])value, (boolean[])otherValue);
    242         } else if (type == float[].class) {
    243             return Arrays.equals((float[])value, (float[])otherValue);
    244         } else if (type == double[].class) {
    245             return Arrays.equals((double[])value, (double[])otherValue);
    246         }
    247         return false;
    248     }
    249 
    250     /**
    251      * Computes hash code of this element. The formula is as follows:
    252      * <code> (name.hashCode() * 127) ^ value.hashCode() </code>
    253      * <br>If value is an array, one of overloaded Arrays.hashCode()
    254      * methods is used.
    255      * @return the hash code
    256      * @see java.util.Arrays#hashCode(java.lang.Object[])
    257      * @see java.lang.annotation.Annotation#hashCode()
    258      */
    259     public int hashCode() {
    260         int hash = name.hashCode() * 127;
    261         if (tag == ARRAY) {
    262             Class type = value.getClass();
    263             if (type == int[].class) {
    264                 return hash ^ Arrays.hashCode((int[])value);
    265             } else if (type == byte[].class) {
    266                 return hash ^ Arrays.hashCode((byte[])value);
    267             } else if (type == short[].class) {
    268                 return hash ^ Arrays.hashCode((short[])value);
    269             } else if (type == long[].class) {
    270                 return hash ^ Arrays.hashCode((long[])value);
    271             } else if (type == char[].class) {
    272                 return hash ^ Arrays.hashCode((char[])value);
    273             } else if (type == boolean[].class) {
    274                 return hash ^ Arrays.hashCode((boolean[])value);
    275             } else if (type == float[].class) {
    276                 return hash ^ Arrays.hashCode((float[])value);
    277             } else if (type == double[].class) {
    278                 return hash ^ Arrays.hashCode((double[])value);
    279             }
    280             return hash ^ Arrays.hashCode((Object[])value);
    281         } else {
    282             return hash ^ value.hashCode();
    283         }
    284     }
    285 
    286     /**
    287      * Throws contained error (if any) with a renewed stack trace.
    288      */
    289     public void rethrowError() throws Throwable {
    290         if (tag == ERROR) {
    291             // need to throw cloned exception for thread safety
    292             // besides it is better to provide actual stack trace
    293             // rather than recorded during parsing
    294 
    295             // first check for expected types
    296             if (value instanceof TypeNotPresentException) {
    297                 TypeNotPresentException tnpe = (TypeNotPresentException)value;
    298                 throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause());
    299             } else if (value instanceof EnumConstantNotPresentException) {
    300                 EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value;
    301                 throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName());
    302             } else if (value instanceof ArrayStoreException) {
    303                 ArrayStoreException ase = (ArrayStoreException)value;
    304                 throw new ArrayStoreException(ase.getMessage());
    305             }
    306             // got some other error, have to go with deep cloning
    307             // via serialization mechanism
    308             Throwable error = (Throwable)value;
    309             StackTraceElement[] ste = error.getStackTrace();
    310             ByteArrayOutputStream bos = new ByteArrayOutputStream(
    311                     ste == null ? 512 : (ste.length + 1) * 80);
    312             ObjectOutputStream oos = new ObjectOutputStream(bos);
    313             oos.writeObject(error);
    314             oos.flush();
    315             oos.close();
    316             ByteArrayInputStream bis = new ByteArrayInputStream(bos
    317                     .toByteArray());
    318             ObjectInputStream ois = new ObjectInputStream(bis);
    319             error = (Throwable)ois.readObject();
    320             ois.close();
    321 
    322             throw error;
    323         }
    324     }
    325 
    326     /**
    327      * Validates contained value against its member definition
    328      * and if ok returns the value.
    329      * Otherwise, if the value type mismatches definition
    330      * or the value itself describes an error,
    331      * throws appropriate exception.
    332      * <br> Note, this method may return null if this element was constructed
    333      * with such value.
    334      *
    335      * @see #rethrowError()
    336      * @see #copyValue()
    337      * @return actual valid value or null if no value
    338      */
    339     public Object validateValue() throws Throwable {
    340         if (tag == ERROR) {
    341             rethrowError();
    342         }
    343         if (value == NO_VALUE) {
    344             return null;
    345         }
    346         if (elementType == value.getClass()
    347                 || elementType.isInstance(value)) { // nested annotation value
    348             return copyValue();
    349         } else {
    350             throw new AnnotationTypeMismatchException(definingMethod,
    351                     value.getClass().getName());
    352         }
    353 
    354     }
    355 
    356 
    357     /**
    358      * Provides mutation-safe access to contained value. That is, caller is free
    359      * to modify the returned value, it will not affect the contained data value.
    360      * @return cloned value if it is mutable or the original immutable value
    361      */
    362     public Object copyValue() throws Throwable
    363     {
    364         if (tag != ARRAY || Array.getLength(value) == 0) {
    365             return value;
    366         }
    367         Class type = value.getClass();
    368         if (type == int[].class) {
    369             return ((int[])value).clone();
    370         } else if (type == byte[].class) {
    371             return ((byte[])value).clone();
    372         } else if (type == short[].class) {
    373             return ((short[])value).clone();
    374         } else if (type == long[].class) {
    375             return ((long[])value).clone();
    376         } else if (type == char[].class) {
    377             return ((char[])value).clone();
    378         } else if (type == boolean[].class) {
    379             return ((boolean[])value).clone();
    380         } else if (type == float[].class) {
    381             return ((float[])value).clone();
    382         } else if (type == double[].class) {
    383             return ((double[])value).clone();
    384         }
    385         return ((Object[])value).clone();
    386     }
    387 }
    388