/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.expression; import org.codehaus.aspectwerkz.exception.DefinitionException; import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; import org.codehaus.aspectwerkz.expression.ast.ASTRoot; import org.codehaus.aspectwerkz.expression.ast.ExpressionParser; import org.codehaus.aspectwerkz.expression.ast.SimpleNode; import org.codehaus.aspectwerkz.expression.ast.Node; import org.codehaus.aspectwerkz.expression.regexp.Pattern; import org.codehaus.aspectwerkz.util.SequencedHashMap; import org.codehaus.aspectwerkz.util.ContextClassLoader; import org.codehaus.aspectwerkz.joinpoint.JoinPoint; import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint; import org.codehaus.aspectwerkz.reflect.ClassInfoHelper; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo; import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo; import org.codehaus.aspectwerkz.cflow.CflowAspectExpressionVisitor; import java.util.Map; import java.util.Set; import java.util.List; import java.util.ArrayList; /** * Abstraction that holds info about the expression and the different visitors. * <br/> * We are using a lazy initialization for m_hasCflowPointcut field to allow to fully resolve each expression (that is f.e. on IBM * compiler, fields are in the reverse order, thus pointcut reference in aspect defined with annotations * may not be resolved until the whole class has been parsed. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a> */ public class ExpressionInfo { public final static String JOINPOINT_CLASS_NAME = JoinPoint.class.getName(); public final static String STATIC_JOINPOINT_CLASS_NAME = StaticJoinPoint.class.getName(); public final static String JOINPOINT_ABBREVIATION = "JoinPoint"; public final static String STATIC_JOINPOINT_ABBREVIATION = "StaticJoinPoint"; public final static String RTTI_ABBREVIATION = "Rtti"; /** * The sole instance of the parser. */ private static final ExpressionParser s_parser = new ExpressionParser(System.in); private final ExpressionVisitor m_expression; private final AdvisedClassFilterExpressionVisitor m_advisedClassFilterExpression; private final CflowAspectExpressionVisitor m_cflowAspectExpression; /** * Ordered map of the pointcut arguments type, indexed by their name. */ private Map m_argsTypeByName = new SequencedHashMap(); /** * List<String> of possible arguments names/references that appear in the expression. * Note that afterReturning/Throwing binding will not appear here (not composable). * This list is lasily populated once using the ExpressionValidateVisitor. * Note that "types" are part of the populated list: * <br/>pointcutRef(x) ==> "x" * <br/>execution(...) && args(x, int) ==> "x", "int" * <br/>this(..), target(..) */ private List m_possibleArguments = null; /** * Name of the special argument for an afterReturning/Throwing when this one is bounded. */ private String m_specialArgumentName = null; /** * Creates a new expression info instance from its string representation * * @param expression the expression * @param namespace the namespace */ public ExpressionInfo(final String expression, final String namespace) { try { Node root = s_parser.parse(expression); m_expression = new ExpressionVisitor(this, expression, namespace, root); m_advisedClassFilterExpression = new AdvisedClassFilterExpressionVisitor(this, expression, namespace, root); m_cflowAspectExpression = new CflowAspectExpressionVisitor(this, root, namespace); } catch (Throwable e) { throw new DefinitionException("expression is not well-formed [" + expression + "]: " + e.getMessage(), e); } } /** * Creates a new expression info from an already parsed node * This is usefull when extracting cflow sub expressions. * * Some attached visitor will be wrong since the string representation * of the expression is not available. * * @param subExpression the sub expression node * @param namespace the namespace */ public ExpressionInfo(final Node subExpression, final String namespace) { try { m_expression = new ExpressionVisitor(this, "N/A", namespace, subExpression); m_advisedClassFilterExpression = new AdvisedClassFilterExpressionVisitor(this, "N/A", namespace, subExpression); m_cflowAspectExpression = new CflowAspectExpressionVisitor(this, subExpression, namespace); } catch (Throwable e) { throw new DefinitionException("sub expression is not well-formed from [" + subExpression+ "]: " + e.getMessage(), e); } } /** * Returns the regular expression. * * @return the regular expression */ public ExpressionVisitor getExpression() { return m_expression; } /** * Returns the namespace * * @return */ public String getNamespace() { return m_expression.m_namespace; } /** * Returns the cflow aspect expression. * * @return the cflow aspect expression */ public CflowAspectExpressionVisitor getCflowAspectExpression() { return m_cflowAspectExpression; } /** * Returns the advised class filter expression. * * @return the advised class filter expression */ public AdvisedClassFilterExpressionVisitor getAdvisedClassFilterExpression() { return m_advisedClassFilterExpression; } /** * Returns the parser. * * @return the parser */ public static ExpressionParser getParser() { return s_parser; } /** * Returns the expression as string. * * @return the expression as string */ public String toString() { return m_expression.toString(); } /** * Add an argument extracted from the call signature of the expression info. * Check is made to ensure that the argument is part of an args(..) or pointcutReference(..) subexpression. * Note that specialArgument for afterReturning/Throwing is handled in a different way. * * @param name * @param className * @param loader */ public void addArgument(final String name, final String className, final ClassLoader loader) { //AW-241 // Note: we do not check the signature and we ignore JoinPoint parameters types String expression = toString(); // fast check if we have a parenthesis if (expression.indexOf('(') > 0) { // fast check if the given argument (that appears in the advice signature) is part of the pointcut expression if (!isJoinPointOrRtti(className, loader)) { if (toString().indexOf(name) < 0) { throw new DefinitionException( "pointcut expression is missing a parameter that has been encountered in the advice: '" + toString() + "' - '" + name + "' of type '" + className + "' missing in '" + getExpression().m_namespace + "'" ); } else { // lazily populate the possible argument list if (m_possibleArguments == null) { m_possibleArguments = new ArrayList(); new ExpressionValidateVisitor(toString(), getNamespace(), getExpression().m_root) .populate(m_possibleArguments); } if (!m_possibleArguments.contains(name)) { throw new DefinitionException( "pointcut expression is missing a parameter that has been encountered in the advice: '" + toString() + "' - '" + name + "' of type '" + className + "' missing in '" + getExpression().m_namespace + "'" ); } } } } m_argsTypeByName.put(name, className); } /** * Set the bounded name of the special argument for afterReturning/Throwing binding * * @param specialArgumentName */ public void setSpecialArgumentName(String specialArgumentName) { m_specialArgumentName = specialArgumentName; } /** * Get the bounded name of the special argument for afterReturning/Throwing binding * * @return */ public String getSpecialArgumentName() { return m_specialArgumentName; } /** * Returns the argumen type. * * @param parameterName * @return */ public String getArgumentType(final String parameterName) { return (String) m_argsTypeByName.get(parameterName); } /** * Returns the argument index. * * @param parameterName * @return */ public int getArgumentIndex(final String parameterName) { if (m_argsTypeByName.containsKey(parameterName)) { return ((SequencedHashMap) m_argsTypeByName).indexOf(parameterName); } else { return -1; } } /** * Returns the argument at the given index. * * @param index * @return paramName */ public String getArgumentNameAtIndex(final int index) { if (index >= m_argsTypeByName.size()) { throw new ArrayIndexOutOfBoundsException( "cannot get argument at index " + index + " in " + m_expression.toString() ); } return (String) m_argsTypeByName.keySet().toArray()[index]; } /** * Returns all argument names. * * @return */ public Set getArgumentNames() { return m_argsTypeByName.keySet(); } /** * Check if the given className is one of the know argument: JoinPoint, StaticJoinPoint, Rtti * <p/> * className can be not qualified (for XML def simplification) * * @param className * @param loader * @return true if so */ private boolean isJoinPointOrRtti(String className, final ClassLoader loader) { if (JOINPOINT_CLASS_NAME.equals(className) || STATIC_JOINPOINT_CLASS_NAME.equals(className) || JOINPOINT_ABBREVIATION.equals(className) || STATIC_JOINPOINT_ABBREVIATION.equals(className) || RTTI_ABBREVIATION.equals(className)) { return true; } if (className.equals("int") || className.equals("long") || className.equals("short") || className.equals("float") || className.equals("double") || className.equals("boolean") || className.equals("byte") || className.equals("char") || className.endsWith("]") || className.startsWith("java.")) { return false; } try { String fullClassName = (String) Pattern.ABBREVIATIONS.get(className); if (fullClassName != null) { className = fullClassName; } if (className.startsWith("java.")) { return false; } ClassInfo classInfo = AsmClassInfo.getClassInfo(className, loader); if (ClassInfoHelper.implementsInterface(classInfo, JOINPOINT_CLASS_NAME) || ClassInfoHelper.implementsInterface(classInfo, STATIC_JOINPOINT_CLASS_NAME)) { return true; } } catch (Throwable e) { throw new WrappedRuntimeException(e); } return false; } public void inheritPossibleArgumentFrom(ExpressionInfo expressionInfo) { m_specialArgumentName = expressionInfo.m_specialArgumentName; m_possibleArguments = expressionInfo.m_possibleArguments; m_argsTypeByName = expressionInfo.m_argsTypeByName; } }