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