Package org.codehaus.aspectwerkz.expression

Source Code of org.codehaus.aspectwerkz.expression.ExpressionVisitor

/**************************************************************************************

* 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.annotation.AnnotationInfo;

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.ASTNot;

import org.codehaus.aspectwerkz.expression.ast.ASTOr;

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.regexp.TypePattern;

import org.codehaus.aspectwerkz.reflect.ClassInfo;

import org.codehaus.aspectwerkz.reflect.ConstructorInfo;

import org.codehaus.aspectwerkz.reflect.FieldInfo;

import org.codehaus.aspectwerkz.reflect.MemberInfo;

import org.codehaus.aspectwerkz.reflect.MethodInfo;

import org.codehaus.aspectwerkz.reflect.ReflectionInfo;

import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;

import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;



import java.lang.reflect.Modifier;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;



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.util.Util;



/**

* The expression visitor.

* If a runtime residual is required (target => instance of check sometimes), Undeterministic matching is used.

*

* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>

* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur </a>

* @author Michael Nascimento

* @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>

*/

public class ExpressionVisitor implements ExpressionParserVisitor {



    protected Node m_root;

    protected String m_expression;

    protected String m_namespace;



    /**

     * The expressionInfo this visitor is built on

     */

    protected ExpressionInfo m_expressionInfo;



    /**

     * Creates a new expression.

     *

     * @param expressionInfo the expressionInfo this visitor is built on for expression with signature

     * @param expression     the expression as a string

     * @param namespace      the namespace

     * @param root           the AST root

     */

    public ExpressionVisitor(final ExpressionInfo expressionInfo,

                             final String expression,

                             final String namespace,

                             final Node root) {

        m_expressionInfo = expressionInfo;

        m_expression = expression;

        m_namespace = namespace;

        m_root = root;

    }



    /**

     * Matches the expression context.

     * If undetermined, assume true.

     * Do not use for poincut reference - see matchUndeterministic

     *

     * @param context

     * @return

     */

    public boolean match(final ExpressionContext context) {

        Boolean match = ((Boolean) visit(m_root, context));

        // undeterministic is assumed to be "true" at this stage

        // since it won't be composed anymore with a NOT (unless

        // thru pointcut reference ie a new visitor)

        return (match != null) ? match.booleanValue() : true;

    }



    protected Boolean matchUndeterministic(final ExpressionContext context) {

        Boolean match = ((Boolean) visit(m_root, context));

        return match;

    }



    // ============ Boot strap =============

    public Object visit(Node node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    public Object visit(SimpleNode node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    public Object visit(ASTRoot node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    public Object visit(ASTExpression node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    // ============ Logical operators =============

    public Object visit(ASTOr node, Object data) {

        // the AND and OR can have more than 2 nodes [see jjt grammar]

        Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);

        Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);

        Boolean intermediate = Undeterministic.or(matchL, matchR);

        for (int i = 2; i < node.jjtGetNumChildren(); i++) {

            Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);

            intermediate = Undeterministic.or(intermediate, matchNext);

        }

        return intermediate;

    }



    public Object visit(ASTAnd node, Object data) {

        // the AND and OR can have more than 2 nodes [see jjt grammar]

        Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);

        Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this, data);

        Boolean intermediate = Undeterministic.and(matchL, matchR);

        for (int i = 2; i < node.jjtGetNumChildren(); i++) {

            Boolean matchNext = (Boolean) node.jjtGetChild(i).jjtAccept(this, data);

            intermediate = Undeterministic.and(intermediate, matchNext);

        }

        return intermediate;

    }



    public Object visit(ASTNot node, Object data) {

        Boolean match = (Boolean) node.jjtGetChild(0).jjtAccept(this, data);

        return Undeterministic.not(match);

    }



    // ============ Pointcut types =============

    public Object visit(ASTPointcutReference node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        ExpressionNamespace namespace = ExpressionNamespace.getNamespace(m_namespace);

        ExpressionVisitor expression = namespace.getExpression(node.getName());

        return expression.matchUndeterministic(context);

    }



    public Object visit(ASTExecution node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasExecutionPointcut() && (context.hasMethodInfo() || context.hasConstructorInfo())) {

            return visitAnnotatedNode(node, context.getReflectionInfo());

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTCall node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasCallPointcut() && (context.hasMethodInfo() || context.hasConstructorInfo())) {

            return visitAnnotatedNode(node, context.getReflectionInfo());

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTSet node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasSetPointcut() && context.hasFieldInfo()) {

            return visitAnnotatedNode(node, context.getReflectionInfo());

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTGet node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasGetPointcut() && context.hasFieldInfo()) {

            return visitAnnotatedNode(node, context.getReflectionInfo());

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTHandler node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasHandlerPointcut() && context.hasClassInfo()) {

            return node.jjtGetChild(0).jjtAccept(this, context.getReflectionInfo());

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTStaticInitialization node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

       

        if (context.hasStaticInitializationPointcut() && context.hasReflectionInfo()) {

          ReflectionInfo reflectInfo = context.getReflectionInfo();

         

          if(reflectInfo instanceof StaticInitializationInfo) {

            ClassInfo declaringClassInfo = ((StaticInitializationInfo) reflectInfo).getDeclaringType();



                // In an annotated subtree, only the last child node may represent 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 annotation if no pattern node or matched already

              boolean matchedAnnotations = visitAttributes(node, declaringClassInfo);

              if (!matchedAnnotations) {

                return Boolean.FALSE;

              } else {

                    return Boolean.TRUE;

                }

          } else {

            return Boolean.FALSE;

          }

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTWithin node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        if (context.hasWithinReflectionInfo()) {

          ReflectionInfo reflectInfo = context.getWithinReflectionInfo();

          ReflectionInfo withinInfo  = null;

         

          if(reflectInfo instanceof MemberInfo) {

            withinInfo = ((MemberInfo) reflectInfo).getDeclaringType();

          } else if(reflectInfo instanceof ClassInfo) {

            withinInfo = reflectInfo;

          } else {

            return Boolean.FALSE;

            }

          return visitAnnotatedNode(

              node,

          withinInfo);

        } else {

            return null;

        }

    }



    public Object visit(ASTWithinCode node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

       

        if(!context.hasWithinReflectionInfo()) {

          return null;

        }

       

        ReflectionInfo reflectInfo = context.getWithinReflectionInfo();

       

        if(node.isStaticInitializer()) {

        if(reflectInfo instanceof StaticInitializationInfo) {

          // Ignore the ASTStaticInitialization node in this context

          SimpleNode staticClinitNode = (SimpleNode) node.jjtGetChild(0);

               ClassInfo declaringClassInfo = ((StaticInitializationInfo) reflectInfo).getDeclaringType();

           

              boolean matchedAnnotations = visitAttributes(staticClinitNode, declaringClassInfo);

              if(!matchedAnnotations) {

                return Boolean.FALSE;

              }

           

              // In an annotated subtree, the last child node represents the pattern

              Node lastNode = staticClinitNode.jjtGetChild(staticClinitNode.jjtGetNumChildren() - 1);

              if(lastNode instanceof ASTAttribute) {

                return Boolean.TRUE;

              } else {

                    return lastNode.jjtAccept(this, reflectInfo);

                }

        } else {

          return Boolean.FALSE;

        }

        } else {

          return visitAnnotatedNode(

              node,

          reflectInfo

          );

        }

    }





    public Object visit(ASTHasMethod node, Object data) {

        ExpressionContext context = (ExpressionContext) data;



        // we are matching on the CALLER info

        // for execution() pointcut, this is equals to CALLEE info

        ReflectionInfo info = context.getWithinReflectionInfo();

        ClassInfo classInfo = info instanceof MemberInfo

        ? ((MemberInfo) info).getDeclaringType()

        : (ClassInfo) info;

       

        Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);

        boolean hasPatternNode = !(patternNode instanceof ASTAttribute);



        MethodInfo[] methodInfos = classInfo.getMethods();

        for (int i = 0; i < methodInfos.length; i++) {

            if (hasPatternNode) {

                if(Boolean.FALSE.equals(patternNode.jjtAccept(this, methodInfos[i]))) {

                    continue;

                }

            }



            boolean matchAnnotations = visitAttributes(node, methodInfos[i]);

            if (matchAnnotations) {

                return Boolean.TRUE;

            }

    }



        ConstructorInfo[] constructorInfos = classInfo.getConstructors();

        for (int i = 0; i < constructorInfos.length; i++) {

            if (hasPatternNode) {

                if(Boolean.FALSE.equals(patternNode.jjtAccept(this, constructorInfos[i]))) {

                    continue;

                }

            }



            boolean matchAnnotations = visitAttributes(node, constructorInfos[i]);

            if (matchAnnotations) {

                return Boolean.TRUE;

            }

        }



        return Boolean.FALSE;

    }



    public Object visit(ASTHasField node, Object data) {

        ExpressionContext context = (ExpressionContext) data;



        // we are matching on the CALLER info

        // for execution() pointcut, this is equals to CALLEE info

        ReflectionInfo info = context.getWithinReflectionInfo();

        ClassInfo classInfo = (info instanceof MemberInfo) ?

                              ((MemberInfo) info).getDeclaringType() : (ClassInfo) info;



        Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);

        boolean hasPatternNode = !(patternNode instanceof ASTAttribute);



        FieldInfo[] fieldInfos = classInfo.getFields();

        for (int i = 0; i < fieldInfos.length; i++) {

            if (hasPatternNode) {

                if (Boolean.FALSE.equals(patternNode.jjtAccept(this, fieldInfos[i]))) {

                    continue;

                }

            }



          boolean matchAnnotations = visitAttributes(node, fieldInfos[i]);

          if (matchAnnotations) {

            return Boolean.TRUE;

          }

        }



        return Boolean.FALSE;

    }



    public Object visit(ASTTarget node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        ReflectionInfo info = context.getReflectionInfo();



//        //TODO - seems to be the case for AJ - not intuitive

//        if (info instanceof ConstructorInfo) {

//            // target(..) does not match for constructors

//            return Boolean.FALSE;

//        }

        ClassInfo declaringType = null;

        if (info instanceof MemberInfo) {

            // if method/field is static, target(..) is evaluated to false

            if (Modifier.isStatic(((MemberInfo) info).getModifiers())) {

                return Boolean.FALSE;

            }



            declaringType = ((MemberInfo) info).getDeclaringType();

        } else if (info instanceof ClassInfo) {

            declaringType = (ClassInfo) info;

        } else {

            return Boolean.FALSE;

        }



        String boundedTypeName = node.getBoundedType(m_expressionInfo);

        // check if the context we match is an interface call, while the bounded type of target(..) is not an

        // interface. In such a case we will need a runtime check

        if (declaringType.isInterface()) {

            // if we are a instanceof (subinterface) of the bounded type, then we don't need a runtime check

            if (ClassInfoHelper.instanceOf(declaringType, boundedTypeName)) {

                return Boolean.TRUE;

            } else {

                //System.out.println("*** RT check for "  + boundedTypeName + " when I am " + declaringType.getName());

                // a runtime check with instance of will be required

                return null;

            }

        } else {

            return Util.booleanValueOf(ClassInfoHelper.instanceOf(declaringType, boundedTypeName));

        }

    }



    public Object visit(ASTThis node, Object data) {

        ExpressionContext context = (ExpressionContext) data;

        // for execution pointcut, this(..) is used to match the callee info

        // and we are assuming here that withinInfo is properly set to reflectionInfo

        if (context.hasWithinReflectionInfo()) {

            ReflectionInfo withinInfo = context.getWithinReflectionInfo();

            if (withinInfo instanceof MemberInfo) {

                // if method is static (callee for execution or caller for call/get/set), this(..) is evaluated to false

                if (Modifier.isStatic(((MemberInfo) withinInfo).getModifiers())) {

                    return Boolean.FALSE;

                }

                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;

    }



    public Object visit(ASTCflow node, Object data) {

        return null;

    }



    public Object visit(ASTCflowBelow node, Object data) {

        return null;

    }



    // ============ Patterns =============

    public Object visit(ASTClassPattern node, Object data) {

        if (data instanceof ClassInfo) {

          ClassInfo classInfo = (ClassInfo) data;

          TypePattern typePattern = node.getTypePattern();



          if (typePattern.matchType(classInfo)

            && visitModifiers(node, classInfo)) {

              return Boolean.TRUE;

          } else {

              return Boolean.FALSE;

          }

        } else if (data instanceof StaticInitializationInfo) {

          ClassInfo classInfo = ((StaticInitializationInfo) data).getDeclaringType();

         

          if(node.getTypePattern().matchType(classInfo)) {

            return Boolean.TRUE;

          } else {

            return Boolean.FALSE;

          }

         

//          return new Boolean(node.getTypePattern().matchType(classInfo));

        }

       

        return Boolean.FALSE;

    }



    public Object visit(ASTMethodPattern node, Object data) {

        if (data instanceof MethodInfo) {

            MethodInfo methodInfo = (MethodInfo) data;

            if (node.getMethodNamePattern().matches(methodInfo.getName())

                && node.getDeclaringTypePattern().matchType(methodInfo.getDeclaringType())

                && node.getReturnTypePattern().matchType(methodInfo.getReturnType())

                && visitModifiers(node, methodInfo)

                && visitParameters(node, methodInfo.getParameterTypes())) {

                return Boolean.TRUE;

            }

        }



        return Boolean.FALSE;

    }



    public Object visit(ASTConstructorPattern node, Object data) {

        if (data instanceof ConstructorInfo) {

            ConstructorInfo constructorMetaData = (ConstructorInfo) data;

            if (node.getDeclaringTypePattern().matchType(constructorMetaData.getDeclaringType())

                && visitModifiers(node, constructorMetaData)

                && visitParameters(node, constructorMetaData.getParameterTypes())) {

                return Boolean.TRUE;

            }

        }

        return Boolean.FALSE;

    }



    public Object visit(ASTFieldPattern node, Object data) {

        if (data instanceof FieldInfo) {

            FieldInfo fieldInfo = (FieldInfo) data;

            if (node.getFieldNamePattern().matches(fieldInfo.getName())

                && node.getDeclaringTypePattern().matchType(fieldInfo.getDeclaringType())

                && node.getFieldTypePattern().matchType(fieldInfo.getType())

                && visitModifiers(node, fieldInfo)) {

                return Boolean.TRUE;

            }

        }

        return Boolean.FALSE;

    }



    public Object visit(ASTParameter node, Object data) {

        ClassInfo parameterType = (ClassInfo) data;

        if (node.getDeclaringClassPattern().matchType(parameterType)) {

            return Boolean.TRUE;

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTArgs node, Object data) {

        ExpressionContext ctx = (ExpressionContext) data;

        if (node.jjtGetNumChildren() <= 0) {

            // args(EMPTY)

            return (getParametersCount(ctx) == 0) ? Boolean.TRUE : Boolean.FALSE;

        } else {

            // check for ".." as first node

            int expressionParameterCount = node.jjtGetNumChildren();// the number of node minus eager one.

            boolean isFirstArgEager = ((ASTArgParameter) node.jjtGetChild(0)).getTypePattern().isEagerWildCard();

            boolean isLastArgEager = ((ASTArgParameter) node.jjtGetChild(node.jjtGetNumChildren() - 1))

                    .getTypePattern().isEagerWildCard();

            // args(..)

            if (isFirstArgEager && expressionParameterCount == 1) {

                return Boolean.TRUE;

            }

            int contextParametersCount = getParametersCount(ctx);

            if (isFirstArgEager && isLastArgEager) {

                expressionParameterCount -= 2;

                if (expressionParameterCount == 0) {

                    // expression is "args(.., ..)"

                    return Boolean.TRUE;

                }

                // we need to find a starting position - args(..,int, bar, ..)

                // foo(int) //int is ok

                // foo(bar,int,bar) //int is ok

                // foo(bar,int,foo,int,bar) // int is ok, but then we fail, so move on to next..

                int matchCount = 0;

                int ictx = 0;

                for (int iexp = 0; iexp < expressionParameterCount; iexp++) {

                    if (ictx >= contextParametersCount) {

                        // too many args in args()

                        matchCount = -1;

                        break;

                    }

                    ctx.setCurrentTargetArgsIndex(ictx);

                    // do we have an eager wildcard in the middle ?

                    boolean isEager = ((ASTArgParameter) node.jjtGetChild(iexp + 1)).getTypePattern().isEagerWildCard();

                    if (isEager) {

                        // TODO - ignore for now, but not really supported - eager in the middle will match one

                    }

                    if (Boolean.TRUE.equals((Boolean) node.jjtGetChild(iexp + 1).jjtAccept(this, ctx))) {

                        matchCount += 1;

                        ictx++;

                    } else {

                        // assume matched by starting ".." and rewind expression index

                        matchCount = 0;

                        ictx++;

                        iexp = -1;

                    }

                }

                if (matchCount == expressionParameterCount) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if (isFirstArgEager) {

                expressionParameterCount--;

                if (contextParametersCount >= expressionParameterCount) {

                    // do a match from last to first, break when args() nodes are exhausted

                    for (int i = 0; (i < contextParametersCount) && (expressionParameterCount - i >= 0); i++) {

                        ctx.setCurrentTargetArgsIndex(contextParametersCount - 1 - i);

                        if (Boolean.TRUE.equals(

                                (Boolean) node.jjtGetChild(expressionParameterCount - i).jjtAccept(

                                        this,

                                        ctx

                                )

                        )) {

                            ;//go on with "next" arg

                        } else {

                            return Boolean.FALSE;

                        }

                    }

                    return Boolean.TRUE;

                } else {

                    //args() as more args than context we try to match

                    return Boolean.FALSE;

                }

            } else if (isLastArgEager) {

                expressionParameterCount--;

                if (contextParametersCount >= expressionParameterCount) {

                    // do a match from first to last, break when args() nodes are exhausted

                    for (int i = 0; (i < contextParametersCount) && (i < expressionParameterCount); i++) {

                        ctx.setCurrentTargetArgsIndex(i);

                        if (Boolean.TRUE.equals((Boolean) node.jjtGetChild(i).jjtAccept(this, ctx))) {

                            ;//go on with next arg

                        } else {

                            return Boolean.FALSE;

                        }

                    }

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else {

                // no eager wildcard in args()

                // check that args length are equals

                if (expressionParameterCount == contextParametersCount) {

                    for (int i = 0; i < node.jjtGetNumChildren(); i++) {

                        ctx.setCurrentTargetArgsIndex(i);

                        if (Boolean.TRUE.equals((Boolean) node.jjtGetChild(i).jjtAccept(this, ctx))) {

                            ;//go on with next arg

                        } else {

                            return Boolean.FALSE;

                        }

                    }

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            }

        }

    }



    public Object visit(ASTArgParameter node, Object data) {

        //TODO we are not doing any hierarchical test when the arg is bound

        // => args(e) and before(Exception e) will not mathch on catch(SubException e) ..

        // is that required ? how AJ syntax behaves ?



        TypePattern typePattern = node.getTypePattern();

        TypePattern realPattern = typePattern;



        // check if the arg is in the pointcut signature. In such a case, use the declared type

        //TODO can we improve that with a lazy attach of the realTypePattern to the node

        // and a method that always return the real pattern

        // It must be lazy since args are not added at info ctor time [can be refactored..]

        // do some filtering first to avoid unnecessary map lookup

       

        int pointcutArgIndex = -1;

        if (typePattern.getPattern().indexOf(".") < 0) {

            String boundedType = m_expressionInfo.getArgumentType(typePattern.getPattern());

            if (boundedType != null) {

                pointcutArgIndex = m_expressionInfo.getArgumentIndex(typePattern.getPattern());

                realPattern = TypePattern.compileTypePattern(boundedType, SubtypePatternType.NOT_HIERARCHICAL);

            }

        }

        // grab parameter from context

        ExpressionContext ctx = (ExpressionContext) data;

        ClassInfo argInfo = null;

        try {

            if (ctx.getReflectionInfo() instanceof MethodInfo) {

                argInfo = ((MethodInfo) ctx.getReflectionInfo()).getParameterTypes()[ctx.getCurrentTargetArgsIndex()];

            } else if (ctx.getReflectionInfo() instanceof ConstructorInfo) {

                argInfo = ((ConstructorInfo) ctx.getReflectionInfo()).getParameterTypes()[ctx

                        .getCurrentTargetArgsIndex()];

            } else if (ctx.getReflectionInfo() instanceof FieldInfo) {

                argInfo = ((FieldInfo) ctx.getReflectionInfo()).getType();

            } else if (ctx.getPointcutType().equals(PointcutType.HANDLER) && ctx.getReflectionInfo() instanceof ClassInfo) {

                argInfo = (ClassInfo) ctx.getReflectionInfo();

            }

        } catch (ArrayIndexOutOfBoundsException e) {

            // ExpressionContext args are exhausted

            return Boolean.FALSE;

        }

        if (realPattern.matchType(argInfo)) {

            return Boolean.TRUE;

        } else {

            return Boolean.FALSE;

        }

    }



    public Object visit(ASTAttribute node, Object data) {

        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) {

        ReflectionInfo refInfo = (ReflectionInfo) data;

        int modifiersToMatch = refInfo.getModifiers();

        int modifierPattern = node.getModifier();

        if (node.isNot()) {

            if ((modifierPattern & Modifier.PUBLIC) != 0) {

                if (((modifiersToMatch & Modifier.PUBLIC) == 0)) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.PROTECTED) != 0) {

                if ((modifiersToMatch & Modifier.PROTECTED) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.PRIVATE) != 0) {

                if ((modifiersToMatch & Modifier.PRIVATE) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.STATIC) != 0) {

                if ((modifiersToMatch & Modifier.STATIC) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.SYNCHRONIZED) != 0) {

                if ((modifiersToMatch & Modifier.SYNCHRONIZED) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.FINAL) != 0) {

                if ((modifiersToMatch & Modifier.FINAL) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.TRANSIENT) != 0) {

                if ((modifiersToMatch & Modifier.TRANSIENT) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.VOLATILE) != 0) {

                if ((modifiersToMatch & Modifier.VOLATILE) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else if ((modifierPattern & Modifier.STRICT) != 0) {

                if ((modifiersToMatch & Modifier.STRICT) == 0) {

                    return Boolean.TRUE;

                } else {

                    return Boolean.FALSE;

                }

            } else {

                return Boolean.FALSE;

            }

        } else {

            if ((modifierPattern & Modifier.PUBLIC) != 0) {

                if (((modifiersToMatch & Modifier.PUBLIC) == 0)) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.PROTECTED) != 0) {

                if ((modifiersToMatch & Modifier.PROTECTED) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.PRIVATE) != 0) {

                if ((modifiersToMatch & Modifier.PRIVATE) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.STATIC) != 0) {

                if ((modifiersToMatch & Modifier.STATIC) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.SYNCHRONIZED) != 0) {

                if ((modifiersToMatch & Modifier.SYNCHRONIZED) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.FINAL) != 0) {

                if ((modifiersToMatch & Modifier.FINAL) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.TRANSIENT) != 0) {

                if ((modifiersToMatch & Modifier.TRANSIENT) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.VOLATILE) != 0) {

                if ((modifiersToMatch & Modifier.VOLATILE) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else if ((modifierPattern & Modifier.STRICT) != 0) {

                if ((modifiersToMatch & Modifier.STRICT) == 0) {

                    return Boolean.FALSE;

                } else {

                    return Boolean.TRUE;

                }

            } else {

                return Boolean.TRUE;

            }

        }

    }



    protected boolean visitAttributes(SimpleNode node, ReflectionInfo refInfo) {

        int nrChildren = node.jjtGetNumChildren();

        if (nrChildren != 0) {

            for (int i = 0; i < nrChildren; i++) {

                Node child = node.jjtGetChild(i);

                if (child instanceof ASTAttribute) {

                    List annotations = refInfo.getAnnotations();

                    if (Boolean.TRUE.equals(child.jjtAccept(this, annotations))) {

                        continue;

                    } else {

                        return false;

                    }

                }

            }

        }

        return true;

    }



    protected boolean visitModifiers(SimpleNode node, ReflectionInfo refInfo) {

        int nrChildren = node.jjtGetNumChildren();

        if (nrChildren != 0) {

            for (int i = 0; i < nrChildren; i++) {

                Node child = node.jjtGetChild(i);

                if (child instanceof ASTModifier) {

                    if (Boolean.TRUE.equals(child.jjtAccept(this, refInfo))) {

                        continue;

                    } else {

                        return false;

                    }

                }

            }

        }

        return true;

    }



    protected boolean visitParameters(SimpleNode node, ClassInfo[] parameterTypes) {

        int nrChildren = node.jjtGetNumChildren();

        if (nrChildren <= 0) {

            return (parameterTypes.length == 0);

        }



        // collect the parameter nodes

        List parameterNodes = new ArrayList();

        for (int i = 0; i < nrChildren; i++) {

            Node child = node.jjtGetChild(i);

            if (child instanceof ASTParameter) {

                parameterNodes.add(child);

            }

        }



        if (parameterNodes.size() <= 0) {

            return (parameterTypes.length == 0);

        }



        //TODO duplicate code with args() match

        //TODO refactor parameterNodes in an array for faster match



        // look for eager pattern at the beginning and end

        int expressionParameterCount = parameterNodes.size();

        boolean isFirstArgEager = ((ASTParameter) parameterNodes.get(0)).getDeclaringClassPattern().isEagerWildCard();

        boolean isLastArgEager = ((ASTParameter) parameterNodes.get(expressionParameterCount - 1)).getDeclaringClassPattern()

                .isEagerWildCard();

        // foo(..)

        if (isFirstArgEager && expressionParameterCount == 1) {

            return true;

        }

        int contextParametersCount = parameterTypes.length;

        if (isFirstArgEager && isLastArgEager) {

            expressionParameterCount -= 2;

            if (expressionParameterCount == 0) {

                // foo(.., ..)

                return true;

            }

            // we need to find a starting position - foo(..,int, bar, ..)

            // foo(int) //int is ok

            // foo(bar,int,bar) //int is ok

            // foo(bar,int,foo,int,bar) // int is ok, but then we fail, so move on to next..

            int matchCount = 0;

            int ictx = 0;

            for (int iexp = 0; iexp < expressionParameterCount; iexp++) {

                if (ictx >= contextParametersCount) {

                    // too many args in foo()

                    matchCount = -1;

                    break;

                }

                // do we have an eager wildcard in the middle ?

                ASTParameter parameterNode = (ASTParameter) parameterNodes.get(iexp + 1);

                boolean isEager = parameterNode.getDeclaringClassPattern().isEagerWildCard();

                if (isEager) {

                    // TODO - ignore for now, but not really supported - eager in the middle will match one

                }

                if (Boolean.TRUE.equals((Boolean) parameterNode.jjtAccept(this, parameterTypes[ictx]))) {

                    matchCount += 1;

                    ictx++;

                } else {

                    // assume matched by starting ".." and rewind expression index

                    matchCount = 0;

                    ictx++;

                    iexp = -1;

                }

            }

            if (matchCount == expressionParameterCount) {

                return true;

            } else {

                return false;

            }

        } else if (isFirstArgEager) {

            expressionParameterCount--;

            if (contextParametersCount >= expressionParameterCount) {

                // do a match from last to first, break when foo() nodes are exhausted

                for (int i = 0; (i < contextParametersCount) && (expressionParameterCount - i >= 0); i++) {

                    ASTParameter parameterNode = (ASTParameter) parameterNodes.get(expressionParameterCount - i);

                    if (Boolean.TRUE.equals(

                            (Boolean) parameterNode.jjtAccept(

                                    this,

                                    parameterTypes[contextParametersCount - 1 - i]

                            )

                    )) {

                        ;//go on with "next" param

                    } else {

                        return false;

                    }

                }

                return true;

            } else {

                //foo() as more param than context we try to match

                return false;

            }

        } else if (isLastArgEager) {

            expressionParameterCount--;

            if (contextParametersCount >= expressionParameterCount) {

                // do a match from first to last, break when foo() nodes are exhausted

                for (int i = 0; (i < contextParametersCount) && (i < expressionParameterCount); i++) {

                    ASTParameter parameterNode = (ASTParameter) parameterNodes.get(i);

                    if (Boolean.TRUE.equals((Boolean) parameterNode.jjtAccept(this, parameterTypes[i]))) {

                        ;//go on with next param

                    } else {

                        return false;

                    }

                }

                return true;

            } else {

                return false;

            }

        } else {

            // no eager wildcard in foo()

            // check that param length are equals

            if (expressionParameterCount == contextParametersCount) {

                for (int i = 0; i < parameterNodes.size(); i++) {

                    ASTParameter parameterNode = (ASTParameter) parameterNodes.get(i);

                    if (Boolean.TRUE.equals((Boolean) parameterNode.jjtAccept(this, parameterTypes[i]))) {

                        ;//go on with next param

                    } else {

                        return false;

                    }

                }

                return true;

            } else {

                return false;

            }

        }

    }



    /**

     * Returns the string representation of the expression.

     *

     * @return

     */

    public String toString() {

        return m_expression;

    }



    /**

     * Returns the number of parameters to the target method/constructor else -1.

     *

     * @param ctx

     * @return

     */

    private int getParametersCount(final ExpressionContext ctx) {

        ReflectionInfo reflectionInfo = ctx.getReflectionInfo();

        if (reflectionInfo instanceof MethodInfo) {

            return ((MethodInfo) reflectionInfo).getParameterTypes().length;

        } else if (reflectionInfo instanceof ConstructorInfo) {

            return ((ConstructorInfo) reflectionInfo).getParameterTypes().length;

        } else if (reflectionInfo instanceof FieldInfo) {

            return 1;//field set support for args()

        } else if (ctx.getPointcutType().equals(PointcutType.HANDLER) && reflectionInfo instanceof ClassInfo) {

            // handler args(e) binding

            return 1;

        } else {

            return -1;

        }

    }



    /**

     * Test the context upon the expression tree, under a node that can

     * contain annotations.

     *

     * @param node  root node of the annotation expression

     * @param reflectInfo context reflection info

     *

     * @return <CODE>Boolean.TRUE</CODE> in case the <tt>reflectInfo</tt> match

     *        the expression subtree, <CODE>Boolean.FALSE</CODE> otherwise.

     */

    protected Object visitAnnotatedNode(SimpleNode node,

                      ReflectionInfo reflectInfo) {

        // In an annotated subtree, only the last child node may represent the pattern

        Node patternNode = node.jjtGetChild(node.jjtGetNumChildren() - 1);

        if (!(patternNode instanceof ASTAttribute)) {

            if (Boolean.FALSE.equals((Boolean)patternNode.jjtAccept(this, reflectInfo))) {

                return Boolean.FALSE;

            }

        }



        boolean matchedAnnotations = visitAttributes(node, reflectInfo);

      if (!matchedAnnotations) {

        return Boolean.FALSE;

      } else {

            return Boolean.TRUE;

        }

    }



    /**

     * Access the ASTRoot we visit

     *

     * @return

     */

    public Node getASTRoot() {

        return m_root;

    }



    /**

     * Access the ExpressionInfo we are build on

     *

     * @return

     */

    public ExpressionInfo getExpressionInfo() {

        return m_expressionInfo;

    }

}

TOP

Related Classes of org.codehaus.aspectwerkz.expression.ExpressionVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.