View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas Bon?r, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.annotation;
9   
10  import org.codehaus.aspectwerkz.annotation.expression.AnnotationVisitor;
11  import org.codehaus.aspectwerkz.util.Strings;
12  
13  import java.lang.reflect.InvocationHandler;
14  import java.lang.reflect.Method;
15  import java.io.Serializable;
16  import java.util.Map;
17  import java.util.HashMap;
18  import java.util.Iterator;
19  
20  /***
21   * A Java 1.3 / 1.4 strongly typed Annotation handler.
22   * This proxy handler gets serialized alongside the annotationInfo within the AnnotationC compiled class.
23   *
24   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
25   */
26  public class Java14AnnotationInvocationHander implements InvocationHandler, Serializable {
27  
28      //TODO calculate
29      private static final long serialVersionUID = 1L;
30  
31      private String m_annotationClassName;
32      private String m_rawAnnotationName;//nickname f.e. @Before in 1.4
33      private String m_rawAnnotationValue;
34      private final boolean m_isUntyped;
35      private final Map m_elements = new HashMap();
36  
37      /***
38       * Constructor that will trigger the parsing if required
39       *
40       * @param annotationInterface
41       * @param rawAnnotationName
42       * @param rawAnnotationValue
43       */
44      public Java14AnnotationInvocationHander(Class annotationInterface, String rawAnnotationName,
45                                              String rawAnnotationValue) {
46          m_annotationClassName = annotationInterface.getName().replace('/', '.');
47          m_rawAnnotationName = rawAnnotationName;
48          m_rawAnnotationValue = rawAnnotationValue;
49  
50          // untyped
51          if (annotationInterface.getName().equals(UntypedAnnotation.class.getName())) {
52              m_isUntyped = true;
53          } else {
54              m_isUntyped = false;
55          }
56  
57          // for @AfterReturning etc, we allow anonymous style but are using typed annotation
58          // hence the @Around pc is a non supported syntax (should be @Around "pc")
59          // but for compatibility purpose we fake it here.
60          if ((m_annotationClassName.equals("org.codehaus.aspectwerkz.annotation.AfterReturning")
61              || m_annotationClassName.equals("org.codehaus.aspectwerkz.annotation.AfterThrowing")
62              || m_annotationClassName.startsWith("org.codehaus.aspectwerkz.annotation.")
63              || isSingleStringValued(annotationInterface))
64                 && !m_isUntyped) {//annotationClassName.equals("org.codehaus.aspectwerkz.annotation.UntypedAnnotation")) {
65              String trimed = m_rawAnnotationValue.trim();
66              if (!isSingleStringValued(annotationInterface) &&
67                  (trimed.startsWith("type")
68                  || trimed.startsWith("pointcut")
69                  || trimed.startsWith("deploymentModel"))) {
70                  ;// not using untyped syntax
71              } else {
72                  if (m_rawAnnotationValue.startsWith("\"") && m_rawAnnotationValue.endsWith("\"")) {
73                      ;
74                  } else {
75                      m_rawAnnotationValue = "\"" + Strings.replaceSubString(m_rawAnnotationValue, "\"", "//\"") + "\"";
76                  }
77              }
78          } else if (m_isUntyped) {
79              if (m_rawAnnotationValue.startsWith("\"") && m_rawAnnotationValue.endsWith("\"")) {
80                  if (m_rawAnnotationValue.length()>2) {
81                      m_rawAnnotationValue = m_rawAnnotationValue.substring(1, m_rawAnnotationValue.length()-1);
82                  }
83              }
84          }
85  
86          // parse the raw representation for typed annotation
87          if (!m_isUntyped) {
88              StringBuffer representation = new StringBuffer("@");
89              representation.append(m_annotationClassName).append('(');
90              if (m_rawAnnotationValue != null) {
91                  // @Aspect perJVM is allowed, while should be @Aspect "perJVM"
92                  // for now patch it here...
93                  // FIXME
94                  if (m_annotationClassName.equals("org.codehaus.aspectwerkz.annotation.Aspect")) {
95                      if (m_rawAnnotationValue.indexOf("name") < 0) {
96                          representation.append(m_rawAnnotationValue);
97                      }
98                  } else {
99                      representation.append(m_rawAnnotationValue);
100                 }
101             }
102             representation.append(')');
103             //TODO support for LazyClass
104             AnnotationVisitor.parse(m_elements, representation.toString(), annotationInterface);
105         }
106     }
107 
108     private static boolean isSingleStringValued(Class annotationInterface) {
109         if (annotationInterface.getDeclaredMethods().length == 1) {
110             Method m = annotationInterface.getDeclaredMethods()[0];
111             return (m.getName().equals("value") && m.getReturnType().equals(String.class));
112         }
113         return false;
114     }
115 
116     /***
117      * Raw constructor that assumes an already analysed annotation instance
118      * Used for nested annotation
119      *
120      * @param annotationInterface
121      * @param elements
122      */
123     public Java14AnnotationInvocationHander(Class annotationInterface, Map elements) {
124         m_annotationClassName = annotationInterface.getName().replace('/', '.');
125         m_rawAnnotationName = m_annotationClassName;
126         m_isUntyped = false;
127         m_rawAnnotationValue = null;
128 
129         m_elements.putAll(elements);
130     }
131 
132     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
133         String methodName = method.getName();
134         Object returned = null;
135         if ("toString".equals(methodName)) {
136             StringBuffer sb = new StringBuffer();
137             sb.append('@').append(m_rawAnnotationName);
138             sb.append("(");
139             String sep = "";
140             for (Iterator iterator = m_elements.keySet().iterator(); iterator.hasNext();) {
141                 String elementName = (String) iterator.next();
142                 AnnotationElement element = (AnnotationElement) m_elements.get(elementName);
143                 sb.append(sep).append(element.name + "=" + element.toString());
144                 sep = ", ";
145             }
146             sb.append(")");
147             returned = sb.toString();
148         } else if ("annotationType".equals(methodName)) {
149             return Class.forName(m_annotationClassName, false, proxy.getClass().getClassLoader());
150         } else if (m_isUntyped) {
151             if ("value".equals(methodName)) {
152                 returned = m_rawAnnotationValue;
153             } else if ("name".equals(methodName)) {
154                 returned = m_rawAnnotationName;
155             } else if ("annotationType".equals(methodName)) {
156                 returned = Class.forName(m_annotationClassName, false, proxy.getClass().getClassLoader());
157             } else {
158                 throw new RuntimeException(
159                         "No such element on Annotation @" + m_annotationClassName + " : " + methodName
160                 );
161             }
162         } else if (m_elements.containsKey(methodName)) {
163             AnnotationElement element = (AnnotationElement) m_elements.get(methodName);
164             Object valueHolder = element.resolveValueHolderFrom(proxy.getClass().getClassLoader());
165             returned = valueHolder;
166         } else {
167             returned = null;
168         }
169 
170         //handle default value for primitive types
171         if (returned == null && method.getReturnType().isPrimitive()) {
172             Class returnedTyped = method.getReturnType();
173             if (boolean.class.equals(returnedTyped)) {
174                 return Boolean.FALSE;
175             } else {
176                 short s0 = 0;
177                 return new Short(s0);
178             }
179         } else {
180             return returned;
181         }
182     }
183 
184 //        private void readObject(final ObjectInputStream stream) throws Exception {
185 //            ObjectInputStream.GetField fields = stream.readFields();
186 //            m_value = (String) fields.get("m_value", null);
187 //            m_name = (String) fields.get("m_name", null);
188 //        }
189 
190 }
191