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