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