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.expression.ast.ASTRoot;
12  import org.codehaus.aspectwerkz.expression.ast.ExpressionParser;
13  import org.codehaus.aspectwerkz.util.SequencedHashMap;
14  import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
15  import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
16  
17  import java.util.Map;
18  import java.util.Set;
19  import java.util.List;
20  import java.util.ArrayList;
21  
22  /***
23   * Abstraction that holds info about the expression and the different visitors.
24   * <br/>
25   * We are using a lazy initialization for m_hasCflowPointcut field to allow to fully resolve each expression (that is f.e. on IBM
26   * compiler, fields are in the reverse order, thus pointcut reference in aspect defined with annotations
27   * may not be resolved until the whole class has been parsed.
28   *
29   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
30   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
31   */
32  public class ExpressionInfo {
33  
34      private final static String FQN_JOIN_POINT_CLASS = JoinPoint.class.getName();
35      private final static String FQN_STATIC_JOIN_POINT_CLASS = StaticJoinPoint.class.getName();
36      private final static String JOINPOINT = "JoinPoint";
37  
38      /***
39       * The sole instance of the parser.
40       */
41      private static final ExpressionParser s_parser = new ExpressionParser(System.in);
42  
43      private final ExpressionVisitor m_expression;
44  
45      private final ArgsIndexVisitor m_argsIndexMapper;
46  
47      private final CflowExpressionVisitor m_cflowExpression;
48  
49      private final CflowExpressionVisitorRuntime m_cflowExpressionRuntime;
50  
51      private final AdvisedClassFilterExpressionVisitor m_advisedClassFilterExpression;
52  
53      private final AdvisedCflowClassFilterExpressionVisitor m_advisedCflowClassFilterExpression;
54  
55      private boolean m_hasCflowPointcut;
56  
57      private boolean m_hasCflowPointcutKnown = false;
58  
59      /***
60       * Ordered map of the pointcut arguments type, indexed by their name.
61       */
62      private final Map m_argsTypeByName = new SequencedHashMap();
63  
64      /***
65       * List<String> of possible arguments names/references that appear in the expression.
66       * This list is lasily populated once using the ExpressionValidateVisitor.
67       * Note that "types" are part of the populated list:
68       * <br/>pointcutRef(x) ==> "x"
69       * <br/>execution(...) && args(x, int) ==> "x", "int"
70       */
71      private List m_possibleArguments = null;
72  
73      /***
74       * Creates a new expression info instance.
75       *
76       * @param expression the expression
77       * @param namespace  the namespace
78       */
79      public ExpressionInfo(final String expression, final String namespace) {
80          try {
81              ASTRoot root = s_parser.parse(expression);
82              m_expression = new ExpressionVisitor(this, expression, namespace, root);
83              m_argsIndexMapper = new ArgsIndexVisitor(this, expression, namespace, root);
84              m_advisedClassFilterExpression = new AdvisedClassFilterExpressionVisitor(expression, namespace, root);
85              m_cflowExpression = new CflowExpressionVisitor(this, expression, namespace, root);
86              m_cflowExpressionRuntime = new CflowExpressionVisitorRuntime(this, expression, namespace, root);
87              m_advisedCflowClassFilterExpression = new AdvisedCflowClassFilterExpressionVisitor(
88                      this,
89                      expression,
90                      namespace,
91                      root
92              );
93          } catch (Throwable e) {
94              throw new DefinitionException("expression is not well-formed [" + expression + "]: " + e.getMessage(), e);
95          }
96      }
97  
98      /***
99       * Returns the expression as string.
100      *
101      * @return the expression as string
102      */
103     public String getExpressionAsString() {
104         return m_expression.toString();
105     }
106 
107     /***
108      * Returns the regular expression.
109      *
110      * @return the regular expression
111      */
112     public ExpressionVisitor getExpression() {
113         return m_expression;
114     }
115 
116     /***
117      * Returns the namespace
118      *
119      * @return
120      */
121     public String getNamespace() {
122         return m_expression.m_namespace;
123     }
124 
125     /***
126      * Returns the regular expression.
127      *
128      * @return the regular expression
129      */
130     public ArgsIndexVisitor getArgsIndexMapper() {
131         return m_argsIndexMapper;
132     }
133 
134     /***
135      * Returns the cflow expression.
136      *
137      * @return the cflow expression
138      */
139     public CflowExpressionVisitor getCflowExpression() {
140         return m_cflowExpression;
141     }
142 
143     /***
144      * Returns the runtime cflow expression.
145      *
146      * @return the cflow expression
147      */
148     public CflowExpressionVisitorRuntime getCflowExpressionRuntime() {
149         return m_cflowExpressionRuntime;
150     }
151 
152     /***
153      * Returns the advised class filter expression.
154      *
155      * @return the advised class filter expression
156      */
157     public AdvisedClassFilterExpressionVisitor getAdvisedClassFilterExpression() {
158         return m_advisedClassFilterExpression;
159     }
160 
161     /***
162      * Returns the advised cflow class filter expression.
163      *
164      * @return the advised cflow class filter expression
165      */
166     public AdvisedCflowClassFilterExpressionVisitor getAdvisedCflowClassFilterExpression() {
167         return m_advisedCflowClassFilterExpression;
168     }
169 
170     /***
171      * Returns the parser.
172      *
173      * @return the parser
174      */
175     public static ExpressionParser getParser() {
176         return s_parser;
177     }
178 
179     /***
180      * Checks if the expression has a cflow pointcut node.
181      *
182      * @return
183      */
184     public boolean hasCflowPointcut() {
185         if (!m_hasCflowPointcutKnown) {
186             String expression = null;
187             try {
188                 expression = getExpressionAsString();
189                 m_hasCflowPointcut =
190                 new CflowPointcutFinderVisitor(expression, m_expression.m_namespace, s_parser.parse(expression)).hasCflowPointcut();
191                 m_hasCflowPointcutKnown = true;
192             } catch (Throwable e) {
193                 // should not happen since the m_expression had been accepted
194                 throw new DefinitionException(
195                         "expression is not well-formed [" + expression + "]: " + e.getMessage(), e
196                 );
197             }
198         }
199         return m_hasCflowPointcut;
200     }
201 
202     /***
203      * Returns the expression as string.
204      *
205      * @return the expression as string
206      */
207     public String toString() {
208         return m_expression.toString();
209     }
210 
211     /***
212      * Add an argument extracted from the call signature of the expression info.
213      * Check is made to ensure that the argument is part of an args(..) or pointcutReference(..) subexpression.
214      * TODO: support this() target()
215      *
216      * @param name
217      * @param className
218      */
219     public void addArgument(final String name, final String className) {
220         //AW-241
221         // Note: we do not check the signature and we ignore JoinPoint parameters types
222         String expression = getExpressionAsString();
223         // fast check if we have a parenthesis
224         if (expression.indexOf('(') > 0) {
225             // fast check if the given argument (that appears in the advice signature) is part of the pointcut expression
226             if (!(FQN_JOIN_POINT_CLASS.equals(className) ||
227                   FQN_STATIC_JOIN_POINT_CLASS.equals(className) ||
228                   JOINPOINT.equals(className))) {
229                 if (getExpressionAsString().indexOf(name) < 0) {
230                     throw new DefinitionException(
231                             "Pointcut is missing a parameter that has been encountered in the Advice: '"
232                             + getExpressionAsString() + "' - '" + name + "' of type '" + className +
233                             "' missing in '" +
234                             getExpression().m_namespace +
235                             "'"
236                     );
237                 } else {
238                     // lazily populate the possible argument list
239                     if (m_possibleArguments == null) {
240                         m_possibleArguments = new ArrayList();
241                         new ExpressionValidateVisitor(getExpressionAsString(), getNamespace(), getExpression().m_root)
242                                 .populate(m_possibleArguments);
243                     }
244                     if (!m_possibleArguments.contains(name)) {
245                         throw new DefinitionException(
246                                 "Pointcut is missing a parameter that has been encountered in the Advice: '"
247                                 + getExpressionAsString() + "' - '" + name + "' of type '" +
248                                 className +
249                                 "' missing in '" +
250                                 getExpression().m_namespace +
251                                 "'"
252                         );
253                     }
254                 }
255             }
256         }
257         m_argsTypeByName.put(name, className);
258     }
259 
260     /***
261      * Returns the argumen type.
262      *
263      * @param parameterName
264      * @return
265      */
266     public String getArgumentType(final String parameterName) {
267         return (String) m_argsTypeByName.get(parameterName);
268     }
269 
270     /***
271      * Returns the argument index.
272      *
273      * @param parameterName
274      * @return
275      */
276     public int getArgumentIndex(final String parameterName) {
277         if (m_argsTypeByName.containsKey(parameterName)) {
278             return ((SequencedHashMap) m_argsTypeByName).indexOf(parameterName);
279         } else {
280             return -1;
281         }
282     }
283 
284     /***
285      * Returns all argument names.
286      *
287      * @return
288      */
289     public Set getArgumentNames() {
290         return m_argsTypeByName.keySet();
291     }
292 
293 }
294