diff options
author | Elliott Hughes <enh@google.com> | 2013-07-23 16:59:26 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2013-07-23 16:59:26 -0700 |
commit | 0ee2c0c62ce570b7f2d82c2ba8599a9ebcd440b5 (patch) | |
tree | 5cde86ea19efdb639fb93c021165d1169893cf7d | |
parent | 55e7e8ac6bf34c75461f416b0a8249b023318872 (diff) | |
download | libcore-0ee2c0c62ce570b7f2d82c2ba8599a9ebcd440b5.zip libcore-0ee2c0c62ce570b7f2d82c2ba8599a9ebcd440b5.tar.gz libcore-0ee2c0c62ce570b7f2d82c2ba8599a9ebcd440b5.tar.bz2 |
AnnotationFactory and AnnotationMember are now shared between art and dalvik.
Change-Id: I58311e9b5a6e6963889b47d8b69c3604b2e61f4c
-rw-r--r-- | libart/src/main/java/libcore/reflect/AnnotationFactory.java | 314 | ||||
-rw-r--r-- | libart/src/main/java/libcore/reflect/AnnotationMember.java | 387 |
2 files changed, 0 insertions, 701 deletions
diff --git a/libart/src/main/java/libcore/reflect/AnnotationFactory.java b/libart/src/main/java/libcore/reflect/AnnotationFactory.java deleted file mode 100644 index f14a9e8..0000000 --- a/libart/src/main/java/libcore/reflect/AnnotationFactory.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package libcore.reflect; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.lang.annotation.IncompleteAnnotationException; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * 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 AnnotationMember - * @see java.lang.annotation.Annotation - * - * @author Alexey V. Varlamov, Serguei S. Zapreyev - * @version $Revision$ - */ -@SuppressWarnings({"serial"}) -public final class AnnotationFactory implements InvocationHandler, Serializable { - - private static final transient - Map<Class<? extends Annotation>, AnnotationMember[]> - cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>(); - - /** - * Reflects specified annotation type and returns an array - * of member element definitions with default values. - */ - public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType) { - 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; - } - - /** - * 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 - */ - @SuppressWarnings("unchecked") // newProxyInstance returns the type of its interfaces - public static <A extends Annotation> A createAnnotation( - Class<A> annotationType, AnnotationMember[] elements) { - AnnotationFactory factory = new AnnotationFactory(annotationType, elements); - return (A) Proxy.newProxyInstance( annotationType.getClassLoader(), - new Class[]{annotationType}, factory); - } - - private final Class<? extends Annotation> klazz; - private AnnotationMember[] elements; - - /** - * 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 - */ - private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] 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]; - } - } - } - - /** - * Reads the object, obtains actual member definitions for the annotation type, - * and merges deserialized values with the new definitions. - */ - private void readObject(ObjectInputStream os) throws IOException, - ClassNotFoundException { - 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()]); - } - - /** - * 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. - * <br>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 AnnotationMember#equals(Object) - */ - public boolean equals(Object obj) { - 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 == AnnotationMember.ERROR) { - // undefined value is incomparable (transcendent) - return false; - } - try { - if (!el.definingMethod.isAccessible()) { - el.definingMethod.setAccessible(true); - } - Object otherValue = el.definingMethod.invoke(obj); - if (otherValue != null ) { - if (el.tag == AnnotationMember.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; - } - } - - /** - * Returns a hash code composed as a sum of hash codes of member elements, - * including elements with default values. - * @see AnnotationMember#hashCode() - */ - public int hashCode() { - int hash = 0; - for (AnnotationMember element : elements) { - hash += element.hashCode(); - } - return hash; - } - - /** - * Provides detailed description of this annotation instance, - * including all member name-values pairs. - * @return string representation of this annotation - */ - public String toString() { - StringBuilder result = new StringBuilder(); - result.append('@'); - result.append(klazz.getName()); - result.append('('); - for (int i = 0; i < elements.length; ++i) { - if (i != 0) { - result.append(", "); - } - result.append(elements[i]); - } - result.append(')'); - return result.toString(); - } - - /** - * 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 - */ - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable - { - 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); - } -} diff --git a/libart/src/main/java/libcore/reflect/AnnotationMember.java b/libart/src/main/java/libcore/reflect/AnnotationMember.java deleted file mode 100644 index 7ca4abf..0000000 --- a/libart/src/main/java/libcore/reflect/AnnotationMember.java +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package libcore.reflect; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.annotation.AnnotationTypeMismatchException; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.util.Arrays; - -/** - * This class represents member element of an annotation. - * It consists of name and value, supplemented with element - * definition information (such as declared type of element). - * <br>The value may be one of the following types: - * <ul> - * <li> boxed primitive - * <li> Class - * <li> enum constant - * <li> annotation (nested) - * <li> one-dimensional array of the above - * <li> Throwable - * </ul> - * The last type is specific for this implementation; a Throwable value - * means that the error occured during parsing or resolution of corresponding - * class-data structures and throwing is delayed until the element - * is requested for value. - * - * @see AnnotationFactory - * - * @author Alexey V. Varlamov, Serguei S. Zapreyev - * @version $Revision$ - */ -@SuppressWarnings({"serial"}) -public final class AnnotationMember implements Serializable { - - /** - * Tag description of a Throwable value type. - */ - protected static final char ERROR = '!'; - - /** - * Tag description of an array value type. - */ - protected static final char ARRAY = '['; - - /** - * Tag description of all value types except arrays and Throwables. - */ - protected static final char OTHER = '*'; - -// public static final char INT = 'I'; -// public static final char CHAR = 'C'; -// public static final char DOUBLE = 'D'; -// public static final char FLOAT = 'F'; -// public static final char BYTE = 'B'; -// public static final char LONG = 'J'; -// public static final char SHORT = 'S'; -// public static final char BOOL = 'Z'; -// public static final char CLASS = 'c'; -// public static final char ENUM = 'e'; -// public static final char ANTN = '@'; - - private enum DefaultValues {NO_VALUE} - - /** - * Singleton representing missing element value. - */ - protected static final Object NO_VALUE = DefaultValues.NO_VALUE; - - protected final String name; - protected final Object value; // a primitive value is wrapped to the corresponding wrapper class - protected final char tag; - // no sense to serialize definition info as it can be changed arbitrarily - protected transient Class<?> elementType; - protected transient Method definingMethod; - - - /** - * Creates a new element with specified name and value. - * Definition info will be provided later when this - * element becomes actual annotation member. - * @param name element name, must not be null - * @param val element value, should be of addmissible type, - * as specified in the description of this class - * - * @see #setDefinition(AnnotationMember) - */ - public AnnotationMember(String name, Object val) { - this.name = name; - value = val == null ? NO_VALUE : val; - if (value instanceof Throwable) { - tag = ERROR; - } else if (value.getClass().isArray()) { - tag = ARRAY; - } else { - tag = OTHER; - } - } - - /** - * Creates the completely defined element. - * @param name element name, must not be null - * @param value element value, should be of addmissible type, - * as specified in the description of this class - * @param m element-defining method, reflected on the annotation type - * @param type declared type of this element - * (return type of the defining method) - */ - public AnnotationMember(String name, Object val, Class type, Method m) { - this(name, val); - - definingMethod = m; - - if (type == int.class) { - elementType = Integer.class; - } else if (type == boolean.class) { - elementType = Boolean.class; - } else if (type == char.class) { - elementType = Character.class; - } else if (type == float.class) { - elementType = Float.class; - } else if (type == double.class) { - elementType = Double.class; - } else if (type == long.class) { - elementType = Long.class; - } else if (type == short.class) { - elementType = Short.class; - } else if (type == byte.class) { - elementType = Byte.class; - } else { - elementType = type; - } - } - - /** - * Fills in element's definition info and returns this. - */ - protected AnnotationMember setDefinition(AnnotationMember copy) { - definingMethod = copy.definingMethod; - elementType = copy.elementType; - return this; - } - - /** - * Returns readable description of this annotation value. - */ - public String toString() { - if (tag == ARRAY) { - StringBuilder sb = new StringBuilder(80); - sb.append(name).append("=["); - int len = Array.getLength(value); - for (int i = 0; i < len; i++) { - if (i != 0) sb.append(", "); - sb.append(Array.get(value, i)); - } - return sb.append("]").toString(); - } else { - return name+ "=" +value; - } - } - - /** - * Returns true if the specified object represents equal element - * (equivalent name-value pair). - * <br> A special case is the contained Throwable value; it is considered - * transcendent so no other element would be equal. - * @return true if passed object is equivalent element representation, - * false otherwise - * @see #equalArrayValue(Object) - * @see java.lang.annotation.Annotation#equals(Object) - */ - public boolean equals(Object obj) { - if (obj == this) { - // not a mere optimization, - // this is needed for consistency with hashCode() - return true; - } - if (obj instanceof AnnotationMember) { - AnnotationMember that = (AnnotationMember)obj; - if (name.equals(that.name) && tag == that.tag) { - if (tag == ARRAY) { - return equalArrayValue(that.value); - } else if (tag == ERROR) { - // undefined value is incomparable (transcendent) - return false; - } else { - return value.equals(that.value); - } - } - } - return false; - } - - /** - * Returns true if the contained value and a passed object are equal arrays, - * false otherwise. Appropriate overloaded method of Arrays.equals() - * is used for equality testing. - * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[]) - * @return true if the value is array and is equal to specified object, - * false otherwise - */ - public boolean equalArrayValue(Object otherValue) { - if (value instanceof Object[] && otherValue instanceof Object[]) { - return Arrays.equals((Object[])value, (Object[])otherValue); - } - Class type = value.getClass(); - if (type != otherValue.getClass()) { - return false; - } - if (type == int[].class) { - return Arrays.equals((int[])value, (int[])otherValue); - } else if (type == byte[].class) { - return Arrays.equals((byte[])value, (byte[])otherValue); - } else if (type == short[].class) { - return Arrays.equals((short[])value, (short[])otherValue); - } else if (type == long[].class) { - return Arrays.equals((long[])value, (long[])otherValue); - } else if (type == char[].class) { - return Arrays.equals((char[])value, (char[])otherValue); - } else if (type == boolean[].class) { - return Arrays.equals((boolean[])value, (boolean[])otherValue); - } else if (type == float[].class) { - return Arrays.equals((float[])value, (float[])otherValue); - } else if (type == double[].class) { - return Arrays.equals((double[])value, (double[])otherValue); - } - return false; - } - - /** - * Computes hash code of this element. The formula is as follows: - * <code> (name.hashCode() * 127) ^ value.hashCode() </code> - * <br>If value is an array, one of overloaded Arrays.hashCode() - * methods is used. - * @return the hash code - * @see java.util.Arrays#hashCode(java.lang.Object[]) - * @see java.lang.annotation.Annotation#hashCode() - */ - public int hashCode() { - int hash = name.hashCode() * 127; - if (tag == ARRAY) { - Class type = value.getClass(); - if (type == int[].class) { - return hash ^ Arrays.hashCode((int[])value); - } else if (type == byte[].class) { - return hash ^ Arrays.hashCode((byte[])value); - } else if (type == short[].class) { - return hash ^ Arrays.hashCode((short[])value); - } else if (type == long[].class) { - return hash ^ Arrays.hashCode((long[])value); - } else if (type == char[].class) { - return hash ^ Arrays.hashCode((char[])value); - } else if (type == boolean[].class) { - return hash ^ Arrays.hashCode((boolean[])value); - } else if (type == float[].class) { - return hash ^ Arrays.hashCode((float[])value); - } else if (type == double[].class) { - return hash ^ Arrays.hashCode((double[])value); - } - return hash ^ Arrays.hashCode((Object[])value); - } else { - return hash ^ value.hashCode(); - } - } - - /** - * Throws contained error (if any) with a renewed stack trace. - */ - public void rethrowError() throws Throwable { - if (tag == ERROR) { - // need to throw cloned exception for thread safety - // besides it is better to provide actual stack trace - // rather than recorded during parsing - - // first check for expected types - if (value instanceof TypeNotPresentException) { - TypeNotPresentException tnpe = (TypeNotPresentException)value; - throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause()); - } else if (value instanceof EnumConstantNotPresentException) { - EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value; - throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName()); - } else if (value instanceof ArrayStoreException) { - ArrayStoreException ase = (ArrayStoreException)value; - throw new ArrayStoreException(ase.getMessage()); - } - // got some other error, have to go with deep cloning - // via serialization mechanism - Throwable error = (Throwable)value; - StackTraceElement[] ste = error.getStackTrace(); - ByteArrayOutputStream bos = new ByteArrayOutputStream( - ste == null ? 512 : (ste.length + 1) * 80); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(error); - oos.flush(); - oos.close(); - ByteArrayInputStream bis = new ByteArrayInputStream(bos - .toByteArray()); - ObjectInputStream ois = new ObjectInputStream(bis); - error = (Throwable)ois.readObject(); - ois.close(); - - throw error; - } - } - - /** - * Validates contained value against its member definition - * and if ok returns the value. - * Otherwise, if the value type mismatches definition - * or the value itself describes an error, - * throws appropriate exception. - * <br> Note, this method may return null if this element was constructed - * with such value. - * - * @see #rethrowError() - * @see #copyValue() - * @return actual valid value or null if no value - */ - public Object validateValue() throws Throwable { - if (tag == ERROR) { - rethrowError(); - } - if (value == NO_VALUE) { - return null; - } - if (elementType == value.getClass() - || elementType.isInstance(value)) { // nested annotation value - return copyValue(); - } else { - throw new AnnotationTypeMismatchException(definingMethod, - value.getClass().getName()); - } - - } - - - /** - * Provides mutation-safe access to contained value. That is, caller is free - * to modify the returned value, it will not affect the contained data value. - * @return cloned value if it is mutable or the original immutable value - */ - public Object copyValue() throws Throwable - { - if (tag != ARRAY || Array.getLength(value) == 0) { - return value; - } - Class type = value.getClass(); - if (type == int[].class) { - return ((int[])value).clone(); - } else if (type == byte[].class) { - return ((byte[])value).clone(); - } else if (type == short[].class) { - return ((short[])value).clone(); - } else if (type == long[].class) { - return ((long[])value).clone(); - } else if (type == char[].class) { - return ((char[])value).clone(); - } else if (type == boolean[].class) { - return ((boolean[])value).clone(); - } else if (type == float[].class) { - return ((float[])value).clone(); - } else if (type == double[].class) { - return ((double[])value).clone(); - } - return ((Object[])value).clone(); - } -} |