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.expression;
    9
    10  import org.codehaus.aspectwerkz.exception.DefinitionException;
    11  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
    12  import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
    13  import org.codehaus.aspectwerkz.expression.ast.ExpressionParser;
    14  import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
    15  import org.codehaus.aspectwerkz.expression.ast.Node;
    16  import org.codehaus.aspectwerkz.expression.regexp.Pattern;
    17  import org.codehaus.aspectwerkz.util.SequencedHashMap;
    18  import org.codehaus.aspectwerkz.util.ContextClassLoader;
    19  import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
    20  import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
    21  import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
    22  import org.codehaus.aspectwerkz.reflect.ClassInfo;
    23  import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
    24  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
    25  import org.codehaus.aspectwerkz.cflow.CflowAspectExpressionVisitor;
    26
    27  import java.util.Map;
    28  import java.util.Set;
    29  import java.util.List;
    30  import java.util.ArrayList;
    31
    32  /***
    33   * Abstraction that holds info about the expression and the different visitors.
    34   * <br/>
    35   * We are using a lazy initialization for m_hasCflowPointcut field to allow to fully resolve each expression (that is f.e. on IBM
    36   * compiler, fields are in the reverse order, thus pointcut reference in aspect defined with annotations
    37   * may not be resolved until the whole class has been parsed.
    38   *
    39   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    40   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    41   */
    42  public class ExpressionInfo {
    43
    44      public final static String JOINPOINT_CLASS_NAME = JoinPoint.class.getName();
    45      public final static String STATIC_JOINPOINT_CLASS_NAME = StaticJoinPoint.class.getName();
    46      public final static String JOINPOINT_ABBREVIATION = "JoinPoint";
    47      public final static String STATIC_JOINPOINT_ABBREVIATION = "StaticJoinPoint";
    48      public final static String RTTI_ABBREVIATION = "Rtti";
    49
    50      /***
    51       * The sole instance of the parser.
    52       */
    53      private static final ExpressionParser s_parser = new ExpressionParser(System.in);
    54
    55      private final ExpressionVisitor m_expression;
    56
    57      private final AdvisedClassFilterExpressionVisitor m_advisedClassFilterExpression;
    58
    59      private final CflowAspectExpressionVisitor m_cflowAspectExpression;
    60
    61      /***
    62       * Ordered map of the pointcut arguments type, indexed by their name.
    63       */
    64      private Map m_argsTypeByName = new SequencedHashMap();
    65
    66      /***
    67       * List<String> of possible arguments names/references that appear in the expression.
    68       * Note that afterReturning/Throwing binding will not appear here (not composable).
    69       * This list is lasily populated once using the ExpressionValidateVisitor.
    70       * Note that "types" are part of the populated list:
    71       * <br/>pointcutRef(x) ==> "x"
    72       * <br/>execution(...) && args(x, int) ==> "x", "int"
    73       * <br/>this(..), target(..)
    74       */
    75      private List m_possibleArguments = null;
    76
    77      /***
    78       * Name of the special argument for an afterReturning/Throwing when this one is bounded.
    79       */
    80      private String m_specialArgumentName = null;
    81
    82      /***
    83       * Creates a new expression info instance from its string representation
    84       *
    85       * @param expression the expression
    86       * @param namespace  the namespace
    87       */
    88      public ExpressionInfo(final String expression, final String namespace) {
    89          try {
    90              Node root = s_parser.parse(expression);
    91              m_expression = new ExpressionVisitor(this, expression, namespace, root);
    92              m_advisedClassFilterExpression =
    93                  new AdvisedClassFilterExpressionVisitor(this, expression, namespace, root);
    94              m_cflowAspectExpression = new CflowAspectExpressionVisitor(this, root, namespace);
    95          } catch (Throwable e) {
    96              throw new DefinitionException("expression is not well-formed [" + expression + "]: " + e.getMessage(), e);
    97          }
    98      }
    99
    100     /***
    101      * Creates a new expression info from an already parsed node
    102      * This is usefull when extracting cflow sub expressions.
    103      *
    104      * Some attached visitor will be wrong since the string representation
    105      * of the expression is not available.
    106      *
    107      * @param subExpression the sub expression node
    108      * @param namespace  the namespace
    109      */
    110     public ExpressionInfo(final Node subExpression, final String namespace) {
    111         try {
    112             m_expression = new ExpressionVisitor(this, "N/A", namespace, subExpression);
    113             m_advisedClassFilterExpression =
    114                 new AdvisedClassFilterExpressionVisitor(this, "N/A", namespace, subExpression);
    115             m_cflowAspectExpression = new CflowAspectExpressionVisitor(this, subExpression, namespace);
    116         } catch (Throwable e) {
    117             throw new DefinitionException("sub expression is not well-formed from [" + subExpression+ "]: " + e.getMessage(), e);
    118         }
    119     }
    120
    121     /***
    122      * Returns the regular expression.
    123      *
    124      * @return the regular expression
    125      */
    126     public ExpressionVisitor getExpression() {
    127         return m_expression;
    128     }
    129
    130     /***
    131      * Returns the namespace
    132      *
    133      * @return
    134      */
    135     public String getNamespace() {
    136         return m_expression.m_namespace;
    137     }
    138
    139     /***
    140      * Returns the cflow aspect expression.
    141      *
    142      * @return the cflow aspect expression
    143      */
    144     public CflowAspectExpressionVisitor getCflowAspectExpression() {
    145         return m_cflowAspectExpression;
    146     }
    147
    148     /***
    149      * Returns the advised class filter expression.
    150      *
    151      * @return the advised class filter expression
    152      */
    153     public AdvisedClassFilterExpressionVisitor getAdvisedClassFilterExpression() {
    154         return m_advisedClassFilterExpression;
    155     }
    156
    157     /***
    158      * Returns the parser.
    159      *
    160      * @return the parser
    161      */
    162     public static ExpressionParser getParser() {
    163         return s_parser;
    164     }
    165
    166     /***
    167      * Returns the expression as string.
    168      *
    169      * @return the expression as string
    170      */
    171     public String toString() {
    172         return m_expression.toString();
    173     }
    174
    175     /***
    176      * Add an argument extracted from the call signature of the expression info.
    177      * Check is made to ensure that the argument is part of an args(..) or pointcutReference(..) subexpression.
    178      * Note that specialArgument for afterReturning/Throwing is handled in a different way.
    179      *
    180      * @param name
    181      * @param className
    182      * @param loader
    183      */
    184     public void addArgument(final String name, final String className, final ClassLoader loader) {
    185         //AW-241
    186         // Note: we do not check the signature and we ignore JoinPoint parameters types
    187         String expression = toString();
    188         // fast check if we have a parenthesis
    189         if (expression.indexOf('(') > 0) {
    190             // fast check if the given argument (that appears in the advice signature) is part of the pointcut expression
    191             if (!isJoinPointOrRtti(className, loader)) {
    192                 if (toString().indexOf(name) < 0) {
    193                     throw new DefinitionException(
    194                             "pointcut expression is missing a parameter that has been encountered in the advice: '"
    195                             + toString() + "' - '" + name + "' of type '" + className +
    196                             "' missing in '" +
    197                             getExpression().m_namespace +
    198                             "'"
    199                     );
    200                 } else {
    201                     // lazily populate the possible argument list
    202                     if (m_possibleArguments == null) {
    203                         m_possibleArguments = new ArrayList();
    204                         new ExpressionValidateVisitor(toString(), getNamespace(), getExpression().m_root)
    205                                 .populate(m_possibleArguments);
    206                     }
    207                     if (!m_possibleArguments.contains(name)) {
    208                         throw new DefinitionException(
    209                                 "pointcut expression is missing a parameter that has been encountered in the advice: '"
    210                                 + toString() + "' - '" + name + "' of type '" +
    211                                 className +
    212                                 "' missing in '" +
    213                                 getExpression().m_namespace +
    214                                 "'"
    215                         );
    216                     }
    217                 }
    218             }
    219         }
    220         m_argsTypeByName.put(name, className);
    221     }
    222
    223     /***
    224      * Set the bounded name of the special argument for afterReturning/Throwing binding
    225      *
    226      * @param specialArgumentName
    227      */
    228     public void setSpecialArgumentName(String specialArgumentName) {
    229         m_specialArgumentName = specialArgumentName;
    230     }
    231
    232     /***
    233      * Get the bounded name of the special argument for afterReturning/Throwing binding
    234      *
    235      * @return
    236      */
    237     public String getSpecialArgumentName() {
    238         return m_specialArgumentName;
    239     }
    240
    241     /***
    242      * Returns the argumen type.
    243      *
    244      * @param parameterName
    245      * @return
    246      */
    247     public String getArgumentType(final String parameterName) {
    248         return (String) m_argsTypeByName.get(parameterName);
    249     }
    250
    251     /***
    252      * Returns the argument index.
    253      *
    254      * @param parameterName
    255      * @return
    256      */
    257     public int getArgumentIndex(final String parameterName) {
    258         if (m_argsTypeByName.containsKey(parameterName)) {
    259             return ((SequencedHashMap) m_argsTypeByName).indexOf(parameterName);
    260         } else {
    261             return -1;
    262         }
    263     }
    264
    265     /***
    266      * Returns the argument at the given index.
    267      *
    268      * @param index
    269      * @return paramName
    270      */
    271     public String getArgumentNameAtIndex(final int index) {
    272         if (index >= m_argsTypeByName.size()) {
    273             throw new ArrayIndexOutOfBoundsException(
    274                     "cannot get argument at index " +
    275                     index + " in " + m_expression.toString()
    276             );
    277         }
    278         return (String) m_argsTypeByName.keySet().toArray()[index];
    279     }
    280
    281     /***
    282      * Returns all argument names.
    283      *
    284      * @return
    285      */
    286     public Set getArgumentNames() {
    287         return m_argsTypeByName.keySet();
    288     }
    289
    290     /***
    291      * Check if the given className is one of the know argument: JoinPoint, StaticJoinPoint, Rtti
    292      * <p/>
    293      * className can be not qualified (for XML def simplification)
    294      *
    295      * @param className
    296      * @param loader
    297      * @return true if so
    298      */
    299     private boolean isJoinPointOrRtti(String className, final ClassLoader loader) {
    300         if (JOINPOINT_CLASS_NAME.equals(className)
    301             || STATIC_JOINPOINT_CLASS_NAME.equals(className)
    302             || JOINPOINT_ABBREVIATION.equals(className)
    303             || STATIC_JOINPOINT_ABBREVIATION.equals(className)
    304             || RTTI_ABBREVIATION.equals(className)) {
    305             return true;
    306         }
    307         if (className.equals("int") ||
    308             className.equals("long") ||
    309             className.equals("short") ||
    310             className.equals("float") ||
    311             className.equals("double") ||
    312             className.equals("boolean") ||
    313             className.equals("byte") ||
    314             className.equals("char") ||
    315             className.endsWith("]") ||
    316             className.startsWith("java.")) {
    317             return false;
    318         }
    319         try {
    320             String fullClassName = (String) Pattern.ABBREVIATIONS.get(className);
    321             if (fullClassName != null) {
    322                 className = fullClassName;
    323             }
    324             if (className.startsWith("java.")) {
    325                 return false;
    326             }
    327             ClassInfo classInfo = AsmClassInfo.getClassInfo(className, loader);
    328             if (ClassInfoHelper.implementsInterface(classInfo, JOINPOINT_CLASS_NAME) ||
    329                 ClassInfoHelper.implementsInterface(classInfo, STATIC_JOINPOINT_CLASS_NAME)) {
    330                 return true;
    331             }
    332         } catch (Throwable e) {
    333             throw new WrappedRuntimeException(e);
    334         }
    335         return false;
    336     }
    337
    338     public void inheritPossibleArgumentFrom(ExpressionInfo expressionInfo) {
    339         m_specialArgumentName = expressionInfo.m_specialArgumentName;
    340         m_possibleArguments = expressionInfo.m_possibleArguments;
    341         m_argsTypeByName = expressionInfo.m_argsTypeByName;
    342     }
    343 }
    344