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
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
221
222 String expression = getExpressionAsString();
223
224 if (expression.indexOf('(') > 0) {
225
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
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