/**************************************************************************************
* 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.expression.ast.ASTAnd;
import org.codehaus.aspectwerkz.expression.ast.ASTAttribute;
import org.codehaus.aspectwerkz.expression.ast.ASTCall;
import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
import org.codehaus.aspectwerkz.expression.ast.ASTCflowBelow;
import org.codehaus.aspectwerkz.expression.ast.ASTClassPattern;
import org.codehaus.aspectwerkz.expression.ast.ASTConstructorPattern;
import org.codehaus.aspectwerkz.expression.ast.ASTExecution;
import org.codehaus.aspectwerkz.expression.ast.ASTExpression;
import org.codehaus.aspectwerkz.expression.ast.ASTFieldPattern;
import org.codehaus.aspectwerkz.expression.ast.ASTGet;
import org.codehaus.aspectwerkz.expression.ast.ASTHandler;
import org.codehaus.aspectwerkz.expression.ast.ASTMethodPattern;
import org.codehaus.aspectwerkz.expression.ast.ASTModifier;
import org.codehaus.aspectwerkz.expression.ast.ASTParameter;
import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
import org.codehaus.aspectwerkz.expression.ast.ASTSet;
import org.codehaus.aspectwerkz.expression.ast.ASTStaticInitialization;
import org.codehaus.aspectwerkz.expression.ast.ASTWithin;
import org.codehaus.aspectwerkz.expression.ast.ASTWithinCode;
import org.codehaus.aspectwerkz.expression.ast.ExpressionParserVisitor;
import org.codehaus.aspectwerkz.expression.ast.Node;
import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
import org.codehaus.aspectwerkz.expression.ast.ASTHasField;
import org.codehaus.aspectwerkz.expression.ast.ASTHasMethod;
import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
import org.codehaus.aspectwerkz.expression.ast.ASTThis;
import org.codehaus.aspectwerkz.expression.ast.ASTNot;
import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
import org.codehaus.aspectwerkz.reflect.ClassInfo;
import org.codehaus.aspectwerkz.reflect.MemberInfo;
import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
import org.codehaus.aspectwerkz.reflect.MethodInfo;
import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
import org.codehaus.aspectwerkz.reflect.FieldInfo;
import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;
import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
import org.codehaus.aspectwerkz.util.Util;
import java.util.List;
import java.util.Iterator;
/**
* The advised class filter visitor.
* <p/>
* Visit() methods are returning Boolean.TRUE/FALSE or null when decision cannot be taken.
* Using null allow composition of OR/AND with NOT in the best way.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
* @author Michael Nascimento
* @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
*/
public class AdvisedClassFilterExpressionVisitor extends ExpressionVisitor implements ExpressionParserVisitor {
/**
* Creates a new expression.
*
* @param expression the expression as a string
* @param namespace the namespace
* @param root the AST root
*/
public AdvisedClassFilterExpressionVisitor(final ExpressionInfo expressionInfo, final String expression,
final String namespace, final Node root) {
super(expressionInfo, expression, namespace, root);
}
// ============ Boot strap =============
public Object visit(SimpleNode node, Object data) {
return node.jjtGetChild(0).jjtAccept(this, data);
}
public Object visit(ASTRoot node, Object data) {
Node child = node.jjtGetChild(0);
Boolean match = (Boolean) child.jjtAccept(this, data);
return match;
}
public Object visit(ASTExpression node, Object data) {
Node child = node.jjtGetChild(0);
Boolean match = (Boolean) child.jjtAccept(this, data);
return match;
}
public Object visit(ASTNot node, Object data) {
return super.visit(node,data);
}
// ============ Pointcut types =============
public Object visit(ASTPointcutReference node, Object data) {
ExpressionContext context = (ExpressionContext) data;
ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);
AdvisedClassFilterExpressionVisitor expression = namespace.getAdvisedClassExpression(node.getName());
return expression.matchUndeterministic(context);
}
public Object visit(ASTExecution node, Object data) {
ExpressionContext context = (ExpressionContext) data;
// only the last node might be the pattern, others are annotations
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
boolean checkPattern = !(patternNode instanceof ASTAttribute);
if(checkPattern) {
if (context.hasWithinPointcut() || context.hasExecutionPointcut()) {
if (context.hasExecutionPointcut()) {
// reflectionInfo was given
return patternNode.jjtAccept(this, context.getReflectionInfo());
} else {
// only withinInfo was given
return patternNode.jjtAccept(this, context.getWithinReflectionInfo());
}
} else {
return Boolean.FALSE;
}
} else {
return null;
}
}
public Object visit(ASTCall node, Object data) {
ExpressionContext context = (ExpressionContext) data;
// only the last node might be the pattern, others are annotations
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
boolean checkPattern = !(patternNode instanceof ASTAttribute);
if(checkPattern) {
if (context.hasWithinPointcut() || context.hasCallPointcut()) {
if (context.hasReflectionInfo()) {
return patternNode.jjtAccept(this, context.getReflectionInfo());
} else {
return null;
}
} else {
return Boolean.FALSE;
}
} else {
return null;
}
}
public Object visit(ASTSet node, Object data) {
ExpressionContext context = (ExpressionContext) data;
// only the last node might be the pattern, others are annotations
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
boolean checkPattern = !(patternNode instanceof ASTAttribute);
// for set evaluation, the reflection info may be null at the early matching phase
// when we will allow for field interception within non declaring class
if(checkPattern) {
if (context.hasWithinPointcut() || context.hasSetPointcut()) {
if (context.hasReflectionInfo()) {
return patternNode.jjtAccept(this, context.getReflectionInfo());
} else {
return null;
}
} else {
return Boolean.FALSE;
}
} else {
return null;
}
}
public Object visit(ASTGet node, Object data) {
ExpressionContext context = (ExpressionContext) data;
// only the last node might be the pattern, others are annotations
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
boolean checkPattern = !(patternNode instanceof ASTAttribute);
// for get evaluation, the reflection info may be null at the early matching phase
// since we allow for field interception within non declaring class
if(checkPattern) {
if (context.hasWithinPointcut() || context.hasGetPointcut()) {
if (context.hasReflectionInfo()) {
return patternNode.jjtAccept(this, context.getReflectionInfo());
} else {
return null;
}
} else {
return Boolean.FALSE;
}
} else {
return null;
}
}
public Object visit(ASTHandler node, Object data) {
return null;
}
public Object visit(ASTStaticInitialization node, Object data) {
ExpressionContext context = (ExpressionContext) data;
if (context.hasStaticInitializationPointcut() && context.hasWithinReflectionInfo()) {
ReflectionInfo reflectInfo = context.getWithinReflectionInfo();
if (reflectInfo instanceof StaticInitializationInfo) {
reflectInfo = ((StaticInitializationInfo) reflectInfo).getDeclaringType();
}
if (reflectInfo instanceof ClassInfo) {
// In an annotated subtree, the last child node represents the pattern
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
if (!(patternNode instanceof ASTAttribute)) {
Boolean matchPattern = (Boolean) patternNode.jjtAccept(this, reflectInfo);
if (Boolean.FALSE.equals(matchPattern)) {
return Boolean.FALSE;
}
}
// match on the annotations since the pattern was not there or matched
boolean matchedAnnotations = visitAttributes(node, reflectInfo);
if (!matchedAnnotations) {
return Boolean.FALSE;
} else {
return null;//match but early phase
}
} else {
return Boolean.FALSE;
}
} else {
return Boolean.FALSE;
}
}
public Object visit(ASTWithinCode node, Object data) {
ExpressionContext context = (ExpressionContext) data;
ReflectionInfo withinInfo = context.getWithinReflectionInfo();
if (node.isStaticInitializer()) {
// transform the node in a within node to do an exact match on the within info
//TODO would be worth to do the fastNode creation only once somewhere
ASTWithin fastNode = new ASTWithin(0);
for (int i = 0; i < node.jjtGetChild(0).jjtGetNumChildren(); i++) {
fastNode.jjtAddChild(node.jjtGetChild(0).jjtGetChild(i), i);
}
return super.visit(fastNode, data);
} else {
Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);
boolean checkPattern = !(patternNode instanceof ASTAttribute);
if (checkPattern) {
if (withinInfo instanceof MemberInfo) {
return patternNode.jjtAccept(this, withinInfo);
} else if (withinInfo instanceof ClassInfo) {
Boolean matchDeclaringType = (Boolean) patternNode.jjtAccept(this, withinInfo);
if (Boolean.FALSE.equals(matchDeclaringType)) {
return Boolean.FALSE;
} else {
// may be we match now but not later?
return null;
}
} else {
return null;
}
} else {
return null;
}
}
}
public Object visit(ASTCflow node, Object data) {
return null;
}
public Object visit(ASTCflowBelow node, Object data) {
return null;
}
public Object visit(ASTArgs node, Object data) {
return null;
}
public Object visit(ASTTarget node, Object data) {
return null;// is that good enough ? For execution PC we would optimize some
}
public Object visit(ASTThis node, Object data) {
ExpressionContext context = (ExpressionContext) data;
if (context.hasWithinReflectionInfo()) {
ReflectionInfo withinInfo = context.getWithinReflectionInfo();
if (withinInfo instanceof MemberInfo) {
return Util.booleanValueOf(
ClassInfoHelper.instanceOf(
((MemberInfo) withinInfo).getDeclaringType(),
node.getBoundedType(m_expressionInfo)
)
);
} else if (withinInfo instanceof ClassInfo) {
return Util.booleanValueOf(
ClassInfoHelper.instanceOf((ClassInfo) withinInfo, node.getBoundedType(m_expressionInfo))
);
}
}
return Boolean.FALSE;
}
// ============ Patterns =============
// public Object visit(ASTClassPattern node, Object data) {
// if(null == data) {
// return null;
// } else if( !(data instanceof ClassInfo) ) {
// return Boolean.FALSE;
// }
//
// ClassInfo classInfo = (ClassInfo) data;
// if (node.getTypePattern().matchType(classInfo) && visitAttributes(node, classInfo)) {
// return Boolean.TRUE;
// } else {
// return Boolean.FALSE;
// }
// }
public Object visit(ASTMethodPattern node, Object data) {
if (data instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) data;
if (node.getDeclaringTypePattern().matchType(classInfo)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
} else if (data instanceof MethodInfo) {
MethodInfo methodInfo = (MethodInfo) data;
if (node.getDeclaringTypePattern().matchType(methodInfo.getDeclaringType())) {
return null;// it might not match further because of modifiers etc
}
return Boolean.FALSE;
}
return Boolean.FALSE;
}
public Object visit(ASTConstructorPattern node, Object data) {
if (data instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) data;
if (node.getDeclaringTypePattern().matchType(classInfo)) {
// we matched but the actual match result may be false
return Boolean.TRUE;
}
} else if (data instanceof ConstructorInfo) {
ConstructorInfo constructorInfo = (ConstructorInfo) data;
if (node.getDeclaringTypePattern().matchType(constructorInfo.getDeclaringType())) {
return null;// it might not match further because of modifiers etc
}
return Boolean.FALSE;
}
return Boolean.FALSE;
}
public Object visit(ASTFieldPattern node, Object data) {
if (data instanceof ClassInfo) {
ClassInfo classInfo = (ClassInfo) data;
if (node.getDeclaringTypePattern().matchType(classInfo)) {
// we matched but the actual match result may be false
return Boolean.TRUE;
}
} else if (data instanceof FieldInfo) {
FieldInfo fieldInfo = (FieldInfo) data;
if (node.getDeclaringTypePattern().matchType(fieldInfo.getDeclaringType())) {
return null;// it might not match further because of modifiers etc
}
return Boolean.FALSE;
}
return Boolean.FALSE;
}
public Object visit(ASTParameter node, Object data) {
ClassInfo parameterType = (ClassInfo) data;
if (node.getDeclaringClassPattern().matchType(parameterType)) {
// we matched but the actual match result may be false
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
public Object visit(ASTArgParameter node, Object data) {
// never called
return Boolean.TRUE;
}
public Object visit(ASTAttribute node, Object data) {
// called for class level annotation matching f.e. in a within context
boolean matchAnnotation = false;
List annotations = (List) data;
for (Iterator it = annotations.iterator(); it.hasNext();) {
AnnotationInfo annotation = (AnnotationInfo) it.next();
if (annotation.getName().equals(node.getName())) {
matchAnnotation = true;
}
}
if (node.isNot()) {
return Util.booleanValueOf(!matchAnnotation);
} else {
return Util.booleanValueOf(matchAnnotation);
}
}
public Object visit(ASTModifier node, Object data) {
// TODO
return null;
}
/**
* Returns the string representation of the AST.
*
* @return
*/
public String toString() {
return m_expression;
}
}