1 | |
2 | package net.sourceforge.retroweaver.runtime.java.lang.annotation; |
3 | |
4 | import java.lang.reflect.InvocationHandler; |
5 | import java.lang.reflect.Method; |
6 | import java.lang.reflect.Proxy; |
7 | import java.util.HashMap; |
8 | import java.util.Map; |
9 | |
10 | /** |
11 | * The implementation of the Annotation interface, which gets returned from various points |
12 | * in java.lang.Class and java.lang.reflect.* classes. |
13 | * |
14 | * @author Toby Reyelts |
15 | * Date: Feb 20, 2005 |
16 | * Time: 11:37:18 PM |
17 | */ |
18 | public class AnnotationImpl implements InvocationHandler, Annotation { |
19 | |
20 | private final Class<? extends Annotation> annotationType_; |
21 | private final Map<String,Object> attributes; |
22 | |
23 | /** |
24 | * Called from generated bytecode to instantiate a new Annotation. |
25 | * Specifically, a new dynamic proxy is created with a type annotationType, |
26 | * and this class as the InvocationHandler. |
27 | * |
28 | * @param annotationType - The Annotation class that this AnnotationImpl should represent |
29 | * @param attributes - The attributes for the Annotation |
30 | * |
31 | * For example, for @Persistable, Persistable.class |
32 | */ |
33 | public static Annotation createAnnotation(final Class<? extends Annotation> annotationType, final Map<String,Object> attributes) { |
34 | try { |
35 | return (Annotation) Proxy.newProxyInstance(AnnotationImpl.class.getClassLoader(), new Class[] { Annotation.class, annotationType }, new AnnotationImpl(annotationType, attributes)); |
36 | } |
37 | catch (IllegalArgumentException e) { |
38 | throw new AnnotationFormatError("Unexpected exception while trying to create an annotation of type: " + annotationType, e); |
39 | } |
40 | } |
41 | |
42 | private AnnotationImpl(final Class<? extends Annotation> annotationType, final Map<String,Object> attributes) { |
43 | this.attributes = new HashMap<String,Object>(attributes); |
44 | this.annotationType_ = annotationType; |
45 | } |
46 | |
47 | private static final Method cloneMethod; |
48 | |
49 | static { |
50 | try { |
51 | cloneMethod = Object.class.getDeclaredMethod("clone", new Class[0]); |
52 | cloneMethod.setAccessible(true); |
53 | } catch (NoSuchMethodException e) { |
54 | throw new RuntimeException(e.getMessage()); |
55 | } |
56 | } |
57 | |
58 | public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { |
59 | // If it's declared in the annotationType, it's an attribute |
60 | if (method.getDeclaringClass().equals(annotationType_)) { |
61 | final String name = method.getName(); |
62 | if (attributes.containsKey(name)) { |
63 | Object o = attributes.get(name); |
64 | if (o == null) { |
65 | return null; |
66 | } else if (o.getClass().isArray()) { |
67 | o = cloneMethod.invoke(o); |
68 | } |
69 | return o; |
70 | } |
71 | throw new IncompleteAnnotationException(annotationType_, name); |
72 | } |
73 | |
74 | // Otherwise it's something we handle innately |
75 | return method.invoke(this, args); |
76 | } |
77 | |
78 | // Returns the annotation type of this annotation. |
79 | public Class<? extends Annotation> annotationType() { |
80 | return annotationType_; |
81 | } |
82 | |
83 | // Returns true if the specified object represents an annotation that is logically equivalent to this one. |
84 | public boolean equals(final Object obj) { |
85 | return (obj instanceof AnnotationImpl && ((AnnotationImpl) obj).annotationType_ == annotationType_); |
86 | } |
87 | |
88 | // Returns the hash code of this annotation, as defined below: |
89 | public int hashCode() { |
90 | // TODO: Implement this according to spec. |
91 | //System.out.println("Warning: Annotation.hashCode() has yet to be implemented according to spec."); |
92 | //return annotationType.hashCode(); |
93 | return toString().hashCode(); |
94 | } |
95 | |
96 | // Returns a string representation of this annotation. |
97 | public String toString() { |
98 | // TODO: Implement this according to spec. |
99 | |
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 | |