001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.expression;
008:
009: import org.codehaus.aspectwerkz.exception.DefinitionException;
010: import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
011: import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
012: import org.codehaus.aspectwerkz.expression.ast.ExpressionParser;
013: import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
014: import org.codehaus.aspectwerkz.expression.ast.Node;
015: import org.codehaus.aspectwerkz.expression.regexp.Pattern;
016: import org.codehaus.aspectwerkz.util.SequencedHashMap;
017: import org.codehaus.aspectwerkz.util.ContextClassLoader;
018: import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
019: import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
020: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
021: import org.codehaus.aspectwerkz.reflect.ClassInfo;
022: import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
023: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
024: import org.codehaus.aspectwerkz.cflow.CflowAspectExpressionVisitor;
025:
026: import java.util.Map;
027: import java.util.Set;
028: import java.util.List;
029: import java.util.ArrayList;
030:
031: /**
032: * Abstraction that holds info about the expression and the different visitors.
033: * <br/>
034: * We are using a lazy initialization for m_hasCflowPointcut field to allow to fully resolve each expression (that is f.e. on IBM
035: * compiler, fields are in the reverse order, thus pointcut reference in aspect defined with annotations
036: * may not be resolved until the whole class has been parsed.
037: *
038: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
039: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
040: */
041: public class ExpressionInfo {
042:
043: public final static String JOINPOINT_CLASS_NAME = JoinPoint.class
044: .getName();
045: public final static String STATIC_JOINPOINT_CLASS_NAME = StaticJoinPoint.class
046: .getName();
047: public final static String JOINPOINT_ABBREVIATION = "JoinPoint";
048: public final static String STATIC_JOINPOINT_ABBREVIATION = "StaticJoinPoint";
049: public final static String RTTI_ABBREVIATION = "Rtti";
050:
051: /**
052: * The sole instance of the parser.
053: */
054: private static final ExpressionParser s_parser = new ExpressionParser(
055: System.in);
056:
057: private final ExpressionVisitor m_expression;
058:
059: private final AdvisedClassFilterExpressionVisitor m_advisedClassFilterExpression;
060:
061: private final CflowAspectExpressionVisitor m_cflowAspectExpression;
062:
063: /**
064: * Ordered map of the pointcut arguments type, indexed by their name.
065: */
066: private Map m_argsTypeByName = new SequencedHashMap();
067:
068: /**
069: * List<String> of possible arguments names/references that appear in the expression.
070: * Note that afterReturning/Throwing binding will not appear here (not composable).
071: * This list is lasily populated once using the ExpressionValidateVisitor.
072: * Note that "types" are part of the populated list:
073: * <br/>pointcutRef(x) ==> "x"
074: * <br/>execution(...) && args(x, int) ==> "x", "int"
075: * <br/>this(..), target(..)
076: */
077: private List m_possibleArguments = null;
078:
079: /**
080: * Name of the special argument for an afterReturning/Throwing when this one is bounded.
081: */
082: private String m_specialArgumentName = null;
083:
084: /**
085: * Creates a new expression info instance from its string representation
086: *
087: * @param expression the expression
088: * @param namespace the namespace
089: */
090: public ExpressionInfo(final String expression,
091: final String namespace) {
092: try {
093: Node root = s_parser.parse(expression);
094: m_expression = new ExpressionVisitor(this , expression,
095: namespace, root);
096: m_advisedClassFilterExpression = new AdvisedClassFilterExpressionVisitor(
097: this , expression, namespace, root);
098: m_cflowAspectExpression = new CflowAspectExpressionVisitor(
099: this , root, namespace);
100: } catch (Throwable e) {
101: throw new DefinitionException(
102: "expression is not well-formed [" + expression
103: + "]: " + e.getMessage(), e);
104: }
105: }
106:
107: /**
108: * Creates a new expression info from an already parsed node
109: * This is usefull when extracting cflow sub expressions.
110: *
111: * Some attached visitor will be wrong since the string representation
112: * of the expression is not available.
113: *
114: * @param subExpression the sub expression node
115: * @param namespace the namespace
116: */
117: public ExpressionInfo(final Node subExpression,
118: final String namespace) {
119: try {
120: m_expression = new ExpressionVisitor(this , "N/A",
121: namespace, subExpression);
122: m_advisedClassFilterExpression = new AdvisedClassFilterExpressionVisitor(
123: this , "N/A", namespace, subExpression);
124: m_cflowAspectExpression = new CflowAspectExpressionVisitor(
125: this , subExpression, namespace);
126: } catch (Throwable e) {
127: throw new DefinitionException(
128: "sub expression is not well-formed from ["
129: + subExpression + "]: " + e.getMessage(), e);
130: }
131: }
132:
133: /**
134: * Returns the regular expression.
135: *
136: * @return the regular expression
137: */
138: public ExpressionVisitor getExpression() {
139: return m_expression;
140: }
141:
142: /**
143: * Returns the namespace
144: *
145: * @return
146: */
147: public String getNamespace() {
148: return m_expression.m_namespace;
149: }
150:
151: /**
152: * Returns the cflow aspect expression.
153: *
154: * @return the cflow aspect expression
155: */
156: public CflowAspectExpressionVisitor getCflowAspectExpression() {
157: return m_cflowAspectExpression;
158: }
159:
160: /**
161: * Returns the advised class filter expression.
162: *
163: * @return the advised class filter expression
164: */
165: public AdvisedClassFilterExpressionVisitor getAdvisedClassFilterExpression() {
166: return m_advisedClassFilterExpression;
167: }
168:
169: /**
170: * Returns the parser.
171: *
172: * @return the parser
173: */
174: public static ExpressionParser getParser() {
175: return s_parser;
176: }
177:
178: /**
179: * Returns the expression as string.
180: *
181: * @return the expression as string
182: */
183: public String toString() {
184: return m_expression.toString();
185: }
186:
187: /**
188: * Add an argument extracted from the call signature of the expression info.
189: * Check is made to ensure that the argument is part of an args(..) or pointcutReference(..) subexpression.
190: * Note that specialArgument for afterReturning/Throwing is handled in a different way.
191: *
192: * @param name
193: * @param className
194: * @param loader
195: */
196: public void addArgument(final String name, final String className,
197: final ClassLoader loader) {
198: //AW-241
199: // Note: we do not check the signature and we ignore JoinPoint parameters types
200: String expression = toString();
201: // fast check if we have a parenthesis
202: if (expression.indexOf('(') > 0) {
203: // fast check if the given argument (that appears in the advice signature) is part of the pointcut expression
204: if (!isJoinPointOrRtti(className, loader)) {
205: if (toString().indexOf(name) < 0) {
206: throw new DefinitionException(
207: "pointcut expression is missing a parameter that has been encountered in the advice: '"
208: + toString()
209: + "' - '"
210: + name
211: + "' of type '"
212: + className
213: + "' missing in '"
214: + getExpression().m_namespace + "'");
215: } else {
216: // lazily populate the possible argument list
217: if (m_possibleArguments == null) {
218: m_possibleArguments = new ArrayList();
219: new ExpressionValidateVisitor(toString(),
220: getNamespace(), getExpression().m_root)
221: .populate(m_possibleArguments);
222: }
223: if (!m_possibleArguments.contains(name)) {
224: throw new DefinitionException(
225: "pointcut expression is missing a parameter that has been encountered in the advice: '"
226: + toString()
227: + "' - '"
228: + name
229: + "' of type '"
230: + className
231: + "' missing in '"
232: + getExpression().m_namespace
233: + "'");
234: }
235: }
236: }
237: }
238: m_argsTypeByName.put(name, className);
239: }
240:
241: /**
242: * Set the bounded name of the special argument for afterReturning/Throwing binding
243: *
244: * @param specialArgumentName
245: */
246: public void setSpecialArgumentName(String specialArgumentName) {
247: m_specialArgumentName = specialArgumentName;
248: }
249:
250: /**
251: * Get the bounded name of the special argument for afterReturning/Throwing binding
252: *
253: * @return
254: */
255: public String getSpecialArgumentName() {
256: return m_specialArgumentName;
257: }
258:
259: /**
260: * Returns the argumen type.
261: *
262: * @param parameterName
263: * @return
264: */
265: public String getArgumentType(final String parameterName) {
266: return (String) m_argsTypeByName.get(parameterName);
267: }
268:
269: /**
270: * Returns the argument index.
271: *
272: * @param parameterName
273: * @return
274: */
275: public int getArgumentIndex(final String parameterName) {
276: if (m_argsTypeByName.containsKey(parameterName)) {
277: return ((SequencedHashMap) m_argsTypeByName)
278: .indexOf(parameterName);
279: } else {
280: return -1;
281: }
282: }
283:
284: /**
285: * Returns the argument at the given index.
286: *
287: * @param index
288: * @return paramName
289: */
290: public String getArgumentNameAtIndex(final int index) {
291: if (index >= m_argsTypeByName.size()) {
292: throw new ArrayIndexOutOfBoundsException(
293: "cannot get argument at index " + index + " in "
294: + m_expression.toString());
295: }
296: return (String) m_argsTypeByName.keySet().toArray()[index];
297: }
298:
299: /**
300: * Returns all argument names.
301: *
302: * @return
303: */
304: public Set getArgumentNames() {
305: return m_argsTypeByName.keySet();
306: }
307:
308: /**
309: * Check if the given className is one of the know argument: JoinPoint, StaticJoinPoint, Rtti
310: * <p/>
311: * className can be not qualified (for XML def simplification)
312: *
313: * @param className
314: * @param loader
315: * @return true if so
316: */
317: private boolean isJoinPointOrRtti(String className,
318: final ClassLoader loader) {
319: if (JOINPOINT_CLASS_NAME.equals(className)
320: || STATIC_JOINPOINT_CLASS_NAME.equals(className)
321: || JOINPOINT_ABBREVIATION.equals(className)
322: || STATIC_JOINPOINT_ABBREVIATION.equals(className)
323: || RTTI_ABBREVIATION.equals(className)) {
324: return true;
325: }
326: if (className.equals("int") || className.equals("long")
327: || className.equals("short")
328: || className.equals("float")
329: || className.equals("double")
330: || className.equals("boolean")
331: || className.equals("byte") || className.equals("char")
332: || className.endsWith("]")
333: || className.startsWith("java.")) {
334: return false;
335: }
336: try {
337: String fullClassName = (String) Pattern.ABBREVIATIONS
338: .get(className);
339: if (fullClassName != null) {
340: className = fullClassName;
341: }
342: if (className.startsWith("java.")) {
343: return false;
344: }
345: ClassInfo classInfo = AsmClassInfo.getClassInfo(className,
346: loader);
347: if (ClassInfoHelper.implements Interface(classInfo,
348: JOINPOINT_CLASS_NAME)
349: || ClassInfoHelper.implements Interface(classInfo,
350: STATIC_JOINPOINT_CLASS_NAME)) {
351: return true;
352: }
353: } catch (Throwable e) {
354: throw new WrappedRuntimeException(e);
355: }
356: return false;
357: }
358:
359: public void inheritPossibleArgumentFrom(
360: ExpressionInfo expressionInfo) {
361: m_specialArgumentName = expressionInfo.m_specialArgumentName;
362: m_possibleArguments = expressionInfo.m_possibleArguments;
363: m_argsTypeByName = expressionInfo.m_argsTypeByName;
364: }
365: }
|