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
186
187 String expression = toString();
188
189 if (expression.indexOf('(') > 0) {
190
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
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