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