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