001
002 package net.sourceforge.retroweaver.runtime.java.lang.annotation;
003
004 import java.lang.reflect.InvocationHandler;
005 import java.lang.reflect.Method;
006 import java.lang.reflect.Proxy;
007 import java.util.HashMap;
008 import java.util.Map;
009
010 /**
011 * The implementation of the Annotation interface, which gets returned from various points
012 * in java.lang.Class and java.lang.reflect.* classes.
013 *
014 * @author Toby Reyelts
015 * Date: Feb 20, 2005
016 * Time: 11:37:18 PM
017 */
018 public class AnnotationImpl implements InvocationHandler, Annotation {
019
020 private final Class<? extends Annotation> annotationType_;
021 private final Map<String,Object> attributes;
022
023 /**
024 * Called from generated bytecode to instantiate a new Annotation.
025 * Specifically, a new dynamic proxy is created with a type annotationType,
026 * and this class as the InvocationHandler.
027 *
028 * @param annotationType - The Annotation class that this AnnotationImpl should represent
029 * @param attributes - The attributes for the Annotation
030 *
031 * For example, for @Persistable, Persistable.class
032 */
033 public static Annotation createAnnotation(final Class<? extends Annotation> annotationType, final Map<String,Object> attributes) {
034 try {
035 return (Annotation) Proxy.newProxyInstance(AnnotationImpl.class.getClassLoader(), new Class[] { Annotation.class, annotationType }, new AnnotationImpl(annotationType, attributes));
036 }
037 catch (IllegalArgumentException e) {
038 throw new AnnotationFormatError("Unexpected exception while trying to create an annotation of type: " + annotationType, e);
039 }
040 }
041
042 private AnnotationImpl(final Class<? extends Annotation> annotationType, final Map<String,Object> attributes) {
043 this.attributes = new HashMap<String,Object>(attributes);
044 this.annotationType_ = annotationType;
045 }
046
047 private static final Method cloneMethod;
048
049 static {
050 try {
051 cloneMethod = Object.class.getDeclaredMethod("clone", new Class[0]);
052 cloneMethod.setAccessible(true);
053 } catch (NoSuchMethodException e) {
054 throw new RuntimeException(e.getMessage());
055 }
056 }
057
058 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
059 // If it's declared in the annotationType, it's an attribute
060 if (method.getDeclaringClass().equals(annotationType_)) {
061 final String name = method.getName();
062 if (attributes.containsKey(name)) {
063 Object o = attributes.get(name);
064 if (o == null) {
065 return null;
066 } else if (o.getClass().isArray()) {
067 o = cloneMethod.invoke(o);
068 }
069 return o;
070 }
071 throw new IncompleteAnnotationException(annotationType_, name);
072 }
073
074 // Otherwise it's something we handle innately
075 return method.invoke(this, args);
076 }
077
078 // Returns the annotation type of this annotation.
079 public Class<? extends Annotation> annotationType() {
080 return annotationType_;
081 }
082
083 // Returns true if the specified object represents an annotation that is logically equivalent to this one.
084 public boolean equals(final Object obj) {
085 return (obj instanceof AnnotationImpl && ((AnnotationImpl) obj).annotationType_ == annotationType_);
086 }
087
088 // Returns the hash code of this annotation, as defined below:
089 public int hashCode() {
090 // TODO: Implement this according to spec.
091 //System.out.println("Warning: Annotation.hashCode() has yet to be implemented according to spec.");
092 //return annotationType.hashCode();
093 return toString().hashCode();
094 }
095
096 // Returns a string representation of this annotation.
097 public String toString() {
098 // TODO: Implement this according to spec.
099
100 final StringBuilder msg = new StringBuilder();
101 msg.append('@').append(annotationType_.getName()).append('(');
102 boolean first = true;
103 for (Map.Entry<String, Object> e: attributes.entrySet()) {
104 if (first) {
105 first = false;
106 } else {
107 msg.append(", ");
108 }
109 msg.append(e.getKey()).append('=');
110 final Object o = e.getValue();
111 if (o.getClass().isArray()) {
112 msg.append(java.util.Arrays.deepToString((Object[])o));
113 } else {
114 msg.append(o);
115 }
116 }
117 msg.append(')');
118 return msg.toString();
119 }
120
121 }
122