summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2013-07-23 16:59:26 -0700
committerElliott Hughes <enh@google.com>2013-07-23 16:59:26 -0700
commit0ee2c0c62ce570b7f2d82c2ba8599a9ebcd440b5 (patch)
tree5cde86ea19efdb639fb93c021165d1169893cf7d
parent55e7e8ac6bf34c75461f416b0a8249b023318872 (diff)
downloadlibcore-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.java314
-rw-r--r--libart/src/main/java/libcore/reflect/AnnotationMember.java387
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();
- }
-}