0001: /**************************************************************************************
0002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
0003: * http://aspectwerkz.codehaus.org *
0004: * ---------------------------------------------------------------------------------- *
0005: * The software in this package is published under the terms of the LGPL license *
0006: * a copy of which has been included with this distribution in the license.txt file. *
0007: **************************************************************************************/package org.codehaus.aspectwerkz.expression;
0008:
0009: import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
0010: import org.codehaus.aspectwerkz.expression.ast.ASTAnd;
0011: import org.codehaus.aspectwerkz.expression.ast.ASTAttribute;
0012: import org.codehaus.aspectwerkz.expression.ast.ASTCall;
0013: import org.codehaus.aspectwerkz.expression.ast.ASTCflow;
0014: import org.codehaus.aspectwerkz.expression.ast.ASTCflowBelow;
0015: import org.codehaus.aspectwerkz.expression.ast.ASTClassPattern;
0016: import org.codehaus.aspectwerkz.expression.ast.ASTConstructorPattern;
0017: import org.codehaus.aspectwerkz.expression.ast.ASTExecution;
0018: import org.codehaus.aspectwerkz.expression.ast.ASTExpression;
0019: import org.codehaus.aspectwerkz.expression.ast.ASTFieldPattern;
0020: import org.codehaus.aspectwerkz.expression.ast.ASTGet;
0021: import org.codehaus.aspectwerkz.expression.ast.ASTHandler;
0022: import org.codehaus.aspectwerkz.expression.ast.ASTMethodPattern;
0023: import org.codehaus.aspectwerkz.expression.ast.ASTModifier;
0024: import org.codehaus.aspectwerkz.expression.ast.ASTNot;
0025: import org.codehaus.aspectwerkz.expression.ast.ASTOr;
0026: import org.codehaus.aspectwerkz.expression.ast.ASTParameter;
0027: import org.codehaus.aspectwerkz.expression.ast.ASTPointcutReference;
0028: import org.codehaus.aspectwerkz.expression.ast.ASTRoot;
0029: import org.codehaus.aspectwerkz.expression.ast.ASTSet;
0030: import org.codehaus.aspectwerkz.expression.ast.ASTStaticInitialization;
0031: import org.codehaus.aspectwerkz.expression.ast.ASTWithin;
0032: import org.codehaus.aspectwerkz.expression.ast.ASTWithinCode;
0033: import org.codehaus.aspectwerkz.expression.ast.ExpressionParserVisitor;
0034: import org.codehaus.aspectwerkz.expression.ast.Node;
0035: import org.codehaus.aspectwerkz.expression.ast.SimpleNode;
0036: import org.codehaus.aspectwerkz.expression.ast.ASTArgs;
0037: import org.codehaus.aspectwerkz.expression.ast.ASTArgParameter;
0038: import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
0039: import org.codehaus.aspectwerkz.reflect.ClassInfo;
0040: import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
0041: import org.codehaus.aspectwerkz.reflect.FieldInfo;
0042: import org.codehaus.aspectwerkz.reflect.MemberInfo;
0043: import org.codehaus.aspectwerkz.reflect.MethodInfo;
0044: import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
0045: import org.codehaus.aspectwerkz.reflect.ClassInfoHelper;
0046: import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;
0047:
0048: import java.lang.reflect.Modifier;
0049: import java.util.ArrayList;
0050: import java.util.Iterator;
0051: import java.util.List;
0052:
0053: import org.codehaus.aspectwerkz.expression.ast.ASTHasField;
0054: import org.codehaus.aspectwerkz.expression.ast.ASTHasMethod;
0055: import org.codehaus.aspectwerkz.expression.ast.ASTTarget;
0056: import org.codehaus.aspectwerkz.expression.ast.ASTThis;
0057: import org.codehaus.aspectwerkz.util.Util;
0058:
0059: /**
0060: * The expression visitor.
0061: * If a runtime residual is required (target => instance of check sometimes), Undeterministic matching is used.
0062: *
0063: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
0064: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur </a>
0065: * @author Michael Nascimento
0066: * @author <a href="mailto:the_mindstorm@evolva.ro">Alex Popescu</a>
0067: */
0068: public class ExpressionVisitor implements ExpressionParserVisitor {
0069:
0070: protected Node m_root;
0071: protected String m_expression;
0072: protected String m_namespace;
0073:
0074: /**
0075: * The expressionInfo this visitor is built on
0076: */
0077: protected ExpressionInfo m_expressionInfo;
0078:
0079: /**
0080: * Creates a new expression.
0081: *
0082: * @param expressionInfo the expressionInfo this visitor is built on for expression with signature
0083: * @param expression the expression as a string
0084: * @param namespace the namespace
0085: * @param root the AST root
0086: */
0087: public ExpressionVisitor(final ExpressionInfo expressionInfo,
0088: final String expression, final String namespace,
0089: final Node root) {
0090: m_expressionInfo = expressionInfo;
0091: m_expression = expression;
0092: m_namespace = namespace;
0093: m_root = root;
0094: }
0095:
0096: /**
0097: * Matches the expression context.
0098: * If undetermined, assume true.
0099: * Do not use for poincut reference - see matchUndeterministic
0100: *
0101: * @param context
0102: * @return
0103: */
0104: public boolean match(final ExpressionContext context) {
0105: Boolean match = ((Boolean) visit(m_root, context));
0106: // undeterministic is assumed to be "true" at this stage
0107: // since it won't be composed anymore with a NOT (unless
0108: // thru pointcut reference ie a new visitor)
0109: return (match != null) ? match.booleanValue() : true;
0110: }
0111:
0112: protected Boolean matchUndeterministic(
0113: final ExpressionContext context) {
0114: Boolean match = ((Boolean) visit(m_root, context));
0115: return match;
0116: }
0117:
0118: // ============ Boot strap =============
0119: public Object visit(Node node, Object data) {
0120: return node.jjtGetChild(0).jjtAccept(this , data);
0121: }
0122:
0123: public Object visit(SimpleNode node, Object data) {
0124: return node.jjtGetChild(0).jjtAccept(this , data);
0125: }
0126:
0127: public Object visit(ASTRoot node, Object data) {
0128: return node.jjtGetChild(0).jjtAccept(this , data);
0129: }
0130:
0131: public Object visit(ASTExpression node, Object data) {
0132: return node.jjtGetChild(0).jjtAccept(this , data);
0133: }
0134:
0135: // ============ Logical operators =============
0136: public Object visit(ASTOr node, Object data) {
0137: // the AND and OR can have more than 2 nodes [see jjt grammar]
0138: Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this ,
0139: data);
0140: Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this ,
0141: data);
0142: Boolean intermediate = Undeterministic.or(matchL, matchR);
0143: for (int i = 2; i < node.jjtGetNumChildren(); i++) {
0144: Boolean matchNext = (Boolean) node.jjtGetChild(i)
0145: .jjtAccept(this , data);
0146: intermediate = Undeterministic.or(intermediate, matchNext);
0147: }
0148: return intermediate;
0149: }
0150:
0151: public Object visit(ASTAnd node, Object data) {
0152: // the AND and OR can have more than 2 nodes [see jjt grammar]
0153: Boolean matchL = (Boolean) node.jjtGetChild(0).jjtAccept(this ,
0154: data);
0155: Boolean matchR = (Boolean) node.jjtGetChild(1).jjtAccept(this ,
0156: data);
0157: Boolean intermediate = Undeterministic.and(matchL, matchR);
0158: for (int i = 2; i < node.jjtGetNumChildren(); i++) {
0159: Boolean matchNext = (Boolean) node.jjtGetChild(i)
0160: .jjtAccept(this , data);
0161: intermediate = Undeterministic.and(intermediate, matchNext);
0162: }
0163: return intermediate;
0164: }
0165:
0166: public Object visit(ASTNot node, Object data) {
0167: Boolean match = (Boolean) node.jjtGetChild(0).jjtAccept(this ,
0168: data);
0169: return Undeterministic.not(match);
0170: }
0171:
0172: // ============ Pointcut types =============
0173: public Object visit(ASTPointcutReference node, Object data) {
0174: ExpressionContext context = (ExpressionContext) data;
0175: ExpressionNamespace namespace = ExpressionNamespace
0176: .getNamespace(m_namespace);
0177: ExpressionVisitor expression = namespace.getExpression(node
0178: .getName());
0179: return expression.matchUndeterministic(context);
0180: }
0181:
0182: public Object visit(ASTExecution node, Object data) {
0183: ExpressionContext context = (ExpressionContext) data;
0184: if (context.hasExecutionPointcut()
0185: && (context.hasMethodInfo() || context
0186: .hasConstructorInfo())) {
0187: return visitAnnotatedNode(node, context.getReflectionInfo());
0188: } else {
0189: return Boolean.FALSE;
0190: }
0191: }
0192:
0193: public Object visit(ASTCall node, Object data) {
0194: ExpressionContext context = (ExpressionContext) data;
0195: if (context.hasCallPointcut()
0196: && (context.hasMethodInfo() || context
0197: .hasConstructorInfo())) {
0198: return visitAnnotatedNode(node, context.getReflectionInfo());
0199: } else {
0200: return Boolean.FALSE;
0201: }
0202: }
0203:
0204: public Object visit(ASTSet node, Object data) {
0205: ExpressionContext context = (ExpressionContext) data;
0206: if (context.hasSetPointcut() && context.hasFieldInfo()) {
0207: return visitAnnotatedNode(node, context.getReflectionInfo());
0208: } else {
0209: return Boolean.FALSE;
0210: }
0211: }
0212:
0213: public Object visit(ASTGet node, Object data) {
0214: ExpressionContext context = (ExpressionContext) data;
0215: if (context.hasGetPointcut() && context.hasFieldInfo()) {
0216: return visitAnnotatedNode(node, context.getReflectionInfo());
0217: } else {
0218: return Boolean.FALSE;
0219: }
0220: }
0221:
0222: public Object visit(ASTHandler node, Object data) {
0223: ExpressionContext context = (ExpressionContext) data;
0224: if (context.hasHandlerPointcut() && context.hasClassInfo()) {
0225: return node.jjtGetChild(0).jjtAccept(this ,
0226: context.getReflectionInfo());
0227: } else {
0228: return Boolean.FALSE;
0229: }
0230: }
0231:
0232: public Object visit(ASTStaticInitialization node, Object data) {
0233: ExpressionContext context = (ExpressionContext) data;
0234:
0235: if (context.hasStaticInitializationPointcut()
0236: && context.hasReflectionInfo()) {
0237: ReflectionInfo reflectInfo = context.getReflectionInfo();
0238:
0239: if (reflectInfo instanceof StaticInitializationInfo) {
0240: ClassInfo declaringClassInfo = ((StaticInitializationInfo) reflectInfo)
0241: .getDeclaringType();
0242:
0243: // In an annotated subtree, only the last child node may represent the pattern
0244: Node patternNode = node.jjtGetChild(node
0245: .jjtGetNumChildren() - 1);
0246: if (!(patternNode instanceof ASTAttribute)) {
0247: Boolean matchPattern = (Boolean) patternNode
0248: .jjtAccept(this , reflectInfo);
0249: if (Boolean.FALSE.equals(matchPattern)) {
0250: return Boolean.FALSE;
0251: }
0252: }
0253:
0254: // match on annotation if no pattern node or matched already
0255: boolean matchedAnnotations = visitAttributes(node,
0256: declaringClassInfo);
0257: if (!matchedAnnotations) {
0258: return Boolean.FALSE;
0259: } else {
0260: return Boolean.TRUE;
0261: }
0262: } else {
0263: return Boolean.FALSE;
0264: }
0265: } else {
0266: return Boolean.FALSE;
0267: }
0268: }
0269:
0270: public Object visit(ASTWithin node, Object data) {
0271: ExpressionContext context = (ExpressionContext) data;
0272: if (context.hasWithinReflectionInfo()) {
0273: ReflectionInfo reflectInfo = context
0274: .getWithinReflectionInfo();
0275: ReflectionInfo withinInfo = null;
0276:
0277: if (reflectInfo instanceof MemberInfo) {
0278: withinInfo = ((MemberInfo) reflectInfo)
0279: .getDeclaringType();
0280: } else if (reflectInfo instanceof ClassInfo) {
0281: withinInfo = reflectInfo;
0282: } else {
0283: return Boolean.FALSE;
0284: }
0285: return visitAnnotatedNode(node, withinInfo);
0286: } else {
0287: return null;
0288: }
0289: }
0290:
0291: public Object visit(ASTWithinCode node, Object data) {
0292: ExpressionContext context = (ExpressionContext) data;
0293:
0294: if (!context.hasWithinReflectionInfo()) {
0295: return null;
0296: }
0297:
0298: ReflectionInfo reflectInfo = context.getWithinReflectionInfo();
0299:
0300: if (node.isStaticInitializer()) {
0301: if (reflectInfo instanceof StaticInitializationInfo) {
0302: // Ignore the ASTStaticInitialization node in this context
0303: SimpleNode staticClinitNode = (SimpleNode) node
0304: .jjtGetChild(0);
0305: ClassInfo declaringClassInfo = ((StaticInitializationInfo) reflectInfo)
0306: .getDeclaringType();
0307:
0308: boolean matchedAnnotations = visitAttributes(
0309: staticClinitNode, declaringClassInfo);
0310: if (!matchedAnnotations) {
0311: return Boolean.FALSE;
0312: }
0313:
0314: // In an annotated subtree, the last child node represents the pattern
0315: Node lastNode = staticClinitNode
0316: .jjtGetChild(staticClinitNode
0317: .jjtGetNumChildren() - 1);
0318: if (lastNode instanceof ASTAttribute) {
0319: return Boolean.TRUE;
0320: } else {
0321: return lastNode.jjtAccept(this , reflectInfo);
0322: }
0323: } else {
0324: return Boolean.FALSE;
0325: }
0326: } else {
0327: return visitAnnotatedNode(node, reflectInfo);
0328: }
0329: }
0330:
0331: public Object visit(ASTHasMethod node, Object data) {
0332: ExpressionContext context = (ExpressionContext) data;
0333:
0334: // we are matching on the CALLER info
0335: // for execution() pointcut, this is equals to CALLEE info
0336: ReflectionInfo info = context.getWithinReflectionInfo();
0337: ClassInfo classInfo = info instanceof MemberInfo ? ((MemberInfo) info)
0338: .getDeclaringType()
0339: : (ClassInfo) info;
0340:
0341: Node patternNode = node
0342: .jjtGetChild(node.jjtGetNumChildren() - 1);
0343: boolean hasPatternNode = !(patternNode instanceof ASTAttribute);
0344:
0345: MethodInfo[] methodInfos = classInfo.getMethods();
0346: for (int i = 0; i < methodInfos.length; i++) {
0347: if (hasPatternNode) {
0348: if (Boolean.FALSE.equals(patternNode.jjtAccept(this ,
0349: methodInfos[i]))) {
0350: continue;
0351: }
0352: }
0353:
0354: boolean matchAnnotations = visitAttributes(node,
0355: methodInfos[i]);
0356: if (matchAnnotations) {
0357: return Boolean.TRUE;
0358: }
0359: }
0360:
0361: ConstructorInfo[] constructorInfos = classInfo
0362: .getConstructors();
0363: for (int i = 0; i < constructorInfos.length; i++) {
0364: if (hasPatternNode) {
0365: if (Boolean.FALSE.equals(patternNode.jjtAccept(this ,
0366: constructorInfos[i]))) {
0367: continue;
0368: }
0369: }
0370:
0371: boolean matchAnnotations = visitAttributes(node,
0372: constructorInfos[i]);
0373: if (matchAnnotations) {
0374: return Boolean.TRUE;
0375: }
0376: }
0377:
0378: return Boolean.FALSE;
0379: }
0380:
0381: public Object visit(ASTHasField node, Object data) {
0382: ExpressionContext context = (ExpressionContext) data;
0383:
0384: // we are matching on the CALLER info
0385: // for execution() pointcut, this is equals to CALLEE info
0386: ReflectionInfo info = context.getWithinReflectionInfo();
0387: ClassInfo classInfo = (info instanceof MemberInfo) ? ((MemberInfo) info)
0388: .getDeclaringType()
0389: : (ClassInfo) info;
0390:
0391: Node patternNode = node
0392: .jjtGetChild(node.jjtGetNumChildren() - 1);
0393: boolean hasPatternNode = !(patternNode instanceof ASTAttribute);
0394:
0395: FieldInfo[] fieldInfos = classInfo.getFields();
0396: for (int i = 0; i < fieldInfos.length; i++) {
0397: if (hasPatternNode) {
0398: if (Boolean.FALSE.equals(patternNode.jjtAccept(this ,
0399: fieldInfos[i]))) {
0400: continue;
0401: }
0402: }
0403:
0404: boolean matchAnnotations = visitAttributes(node,
0405: fieldInfos[i]);
0406: if (matchAnnotations) {
0407: return Boolean.TRUE;
0408: }
0409: }
0410:
0411: return Boolean.FALSE;
0412: }
0413:
0414: public Object visit(ASTTarget node, Object data) {
0415: ExpressionContext context = (ExpressionContext) data;
0416: ReflectionInfo info = context.getReflectionInfo();
0417:
0418: // //TODO - seems to be the case for AJ - not intuitive
0419: // if (info instanceof ConstructorInfo) {
0420: // // target(..) does not match for constructors
0421: // return Boolean.FALSE;
0422: // }
0423: ClassInfo declaringType = null;
0424: if (info instanceof MemberInfo) {
0425: // if method/field is static, target(..) is evaluated to false
0426: if (Modifier.isStatic(((MemberInfo) info).getModifiers())) {
0427: return Boolean.FALSE;
0428: }
0429:
0430: declaringType = ((MemberInfo) info).getDeclaringType();
0431: } else if (info instanceof ClassInfo) {
0432: declaringType = (ClassInfo) info;
0433: } else {
0434: return Boolean.FALSE;
0435: }
0436:
0437: String boundedTypeName = node.getBoundedType(m_expressionInfo);
0438: // check if the context we match is an interface call, while the bounded type of target(..) is not an
0439: // interface. In such a case we will need a runtime check
0440: if (declaringType.isInterface()) {
0441: // if we are a instanceof (subinterface) of the bounded type, then we don't need a runtime check
0442: if (ClassInfoHelper.instanceOf(declaringType,
0443: boundedTypeName)) {
0444: return Boolean.TRUE;
0445: } else {
0446: //System.out.println("*** RT check for " + boundedTypeName + " when I am " + declaringType.getName());
0447: // a runtime check with instance of will be required
0448: return null;
0449: }
0450: } else {
0451: return Util.booleanValueOf(ClassInfoHelper.instanceOf(
0452: declaringType, boundedTypeName));
0453: }
0454: }
0455:
0456: public Object visit(ASTThis node, Object data) {
0457: ExpressionContext context = (ExpressionContext) data;
0458: // for execution pointcut, this(..) is used to match the callee info
0459: // and we are assuming here that withinInfo is properly set to reflectionInfo
0460: if (context.hasWithinReflectionInfo()) {
0461: ReflectionInfo withinInfo = context
0462: .getWithinReflectionInfo();
0463: if (withinInfo instanceof MemberInfo) {
0464: // if method is static (callee for execution or caller for call/get/set), this(..) is evaluated to false
0465: if (Modifier.isStatic(((MemberInfo) withinInfo)
0466: .getModifiers())) {
0467: return Boolean.FALSE;
0468: }
0469: return Util.booleanValueOf(ClassInfoHelper.instanceOf(
0470: ((MemberInfo) withinInfo).getDeclaringType(),
0471: node.getBoundedType(m_expressionInfo)));
0472: } else if (withinInfo instanceof ClassInfo) {
0473: return Util.booleanValueOf(ClassInfoHelper.instanceOf(
0474: (ClassInfo) withinInfo, node
0475: .getBoundedType(m_expressionInfo)));
0476: }
0477: }
0478: return Boolean.FALSE;
0479: }
0480:
0481: public Object visit(ASTCflow node, Object data) {
0482: return null;
0483: }
0484:
0485: public Object visit(ASTCflowBelow node, Object data) {
0486: return null;
0487: }
0488:
0489: // ============ Patterns =============
0490: public Object visit(ASTClassPattern node, Object data) {
0491: if (data instanceof ClassInfo) {
0492: ClassInfo classInfo = (ClassInfo) data;
0493: TypePattern typePattern = node.getTypePattern();
0494:
0495: if (typePattern.matchType(classInfo)
0496: && visitModifiers(node, classInfo)) {
0497: return Boolean.TRUE;
0498: } else {
0499: return Boolean.FALSE;
0500: }
0501: } else if (data instanceof StaticInitializationInfo) {
0502: ClassInfo classInfo = ((StaticInitializationInfo) data)
0503: .getDeclaringType();
0504:
0505: if (node.getTypePattern().matchType(classInfo)) {
0506: return Boolean.TRUE;
0507: } else {
0508: return Boolean.FALSE;
0509: }
0510:
0511: // return new Boolean(node.getTypePattern().matchType(classInfo));
0512: }
0513:
0514: return Boolean.FALSE;
0515: }
0516:
0517: public Object visit(ASTMethodPattern node, Object data) {
0518: if (data instanceof MethodInfo) {
0519: MethodInfo methodInfo = (MethodInfo) data;
0520: if (node.getMethodNamePattern().matches(
0521: methodInfo.getName())
0522: && node.getDeclaringTypePattern().matchType(
0523: methodInfo.getDeclaringType())
0524: && node.getReturnTypePattern().matchType(
0525: methodInfo.getReturnType())
0526: && visitModifiers(node, methodInfo)
0527: && visitParameters(node, methodInfo
0528: .getParameterTypes())) {
0529: return Boolean.TRUE;
0530: }
0531: }
0532:
0533: return Boolean.FALSE;
0534: }
0535:
0536: public Object visit(ASTConstructorPattern node, Object data) {
0537: if (data instanceof ConstructorInfo) {
0538: ConstructorInfo constructorMetaData = (ConstructorInfo) data;
0539: if (node.getDeclaringTypePattern().matchType(
0540: constructorMetaData.getDeclaringType())
0541: && visitModifiers(node, constructorMetaData)
0542: && visitParameters(node, constructorMetaData
0543: .getParameterTypes())) {
0544: return Boolean.TRUE;
0545: }
0546: }
0547: return Boolean.FALSE;
0548: }
0549:
0550: public Object visit(ASTFieldPattern node, Object data) {
0551: if (data instanceof FieldInfo) {
0552: FieldInfo fieldInfo = (FieldInfo) data;
0553: if (node.getFieldNamePattern().matches(fieldInfo.getName())
0554: && node.getDeclaringTypePattern().matchType(
0555: fieldInfo.getDeclaringType())
0556: && node.getFieldTypePattern().matchType(
0557: fieldInfo.getType())
0558: && visitModifiers(node, fieldInfo)) {
0559: return Boolean.TRUE;
0560: }
0561: }
0562: return Boolean.FALSE;
0563: }
0564:
0565: public Object visit(ASTParameter node, Object data) {
0566: ClassInfo parameterType = (ClassInfo) data;
0567: if (node.getDeclaringClassPattern().matchType(parameterType)) {
0568: return Boolean.TRUE;
0569: } else {
0570: return Boolean.FALSE;
0571: }
0572: }
0573:
0574: public Object visit(ASTArgs node, Object data) {
0575: ExpressionContext ctx = (ExpressionContext) data;
0576: if (node.jjtGetNumChildren() <= 0) {
0577: // args(EMPTY)
0578: return (getParametersCount(ctx) == 0) ? Boolean.TRUE
0579: : Boolean.FALSE;
0580: } else {
0581: // check for ".." as first node
0582: int expressionParameterCount = node.jjtGetNumChildren();// the number of node minus eager one.
0583: boolean isFirstArgEager = ((ASTArgParameter) node
0584: .jjtGetChild(0)).getTypePattern().isEagerWildCard();
0585: boolean isLastArgEager = ((ASTArgParameter) node
0586: .jjtGetChild(node.jjtGetNumChildren() - 1))
0587: .getTypePattern().isEagerWildCard();
0588: // args(..)
0589: if (isFirstArgEager && expressionParameterCount == 1) {
0590: return Boolean.TRUE;
0591: }
0592: int contextParametersCount = getParametersCount(ctx);
0593: if (isFirstArgEager && isLastArgEager) {
0594: expressionParameterCount -= 2;
0595: if (expressionParameterCount == 0) {
0596: // expression is "args(.., ..)"
0597: return Boolean.TRUE;
0598: }
0599: // we need to find a starting position - args(..,int, bar, ..)
0600: // foo(int) //int is ok
0601: // foo(bar,int,bar) //int is ok
0602: // foo(bar,int,foo,int,bar) // int is ok, but then we fail, so move on to next..
0603: int matchCount = 0;
0604: int ictx = 0;
0605: for (int iexp = 0; iexp < expressionParameterCount; iexp++) {
0606: if (ictx >= contextParametersCount) {
0607: // too many args in args()
0608: matchCount = -1;
0609: break;
0610: }
0611: ctx.setCurrentTargetArgsIndex(ictx);
0612: // do we have an eager wildcard in the middle ?
0613: boolean isEager = ((ASTArgParameter) node
0614: .jjtGetChild(iexp + 1)).getTypePattern()
0615: .isEagerWildCard();
0616: if (isEager) {
0617: // TODO - ignore for now, but not really supported - eager in the middle will match one
0618: }
0619: if (Boolean.TRUE.equals((Boolean) node.jjtGetChild(
0620: iexp + 1).jjtAccept(this , ctx))) {
0621: matchCount += 1;
0622: ictx++;
0623: } else {
0624: // assume matched by starting ".." and rewind expression index
0625: matchCount = 0;
0626: ictx++;
0627: iexp = -1;
0628: }
0629: }
0630: if (matchCount == expressionParameterCount) {
0631: return Boolean.TRUE;
0632: } else {
0633: return Boolean.FALSE;
0634: }
0635: } else if (isFirstArgEager) {
0636: expressionParameterCount--;
0637: if (contextParametersCount >= expressionParameterCount) {
0638: // do a match from last to first, break when args() nodes are exhausted
0639: for (int i = 0; (i < contextParametersCount)
0640: && (expressionParameterCount - i >= 0); i++) {
0641: ctx
0642: .setCurrentTargetArgsIndex(contextParametersCount
0643: - 1 - i);
0644: if (Boolean.TRUE.equals((Boolean) node
0645: .jjtGetChild(
0646: expressionParameterCount - i)
0647: .jjtAccept(this , ctx))) {
0648: ;//go on with "next" arg
0649: } else {
0650: return Boolean.FALSE;
0651: }
0652: }
0653: return Boolean.TRUE;
0654: } else {
0655: //args() as more args than context we try to match
0656: return Boolean.FALSE;
0657: }
0658: } else if (isLastArgEager) {
0659: expressionParameterCount--;
0660: if (contextParametersCount >= expressionParameterCount) {
0661: // do a match from first to last, break when args() nodes are exhausted
0662: for (int i = 0; (i < contextParametersCount)
0663: && (i < expressionParameterCount); i++) {
0664: ctx.setCurrentTargetArgsIndex(i);
0665: if (Boolean.TRUE.equals((Boolean) node
0666: .jjtGetChild(i).jjtAccept(this , ctx))) {
0667: ;//go on with next arg
0668: } else {
0669: return Boolean.FALSE;
0670: }
0671: }
0672: return Boolean.TRUE;
0673: } else {
0674: return Boolean.FALSE;
0675: }
0676: } else {
0677: // no eager wildcard in args()
0678: // check that args length are equals
0679: if (expressionParameterCount == contextParametersCount) {
0680: for (int i = 0; i < node.jjtGetNumChildren(); i++) {
0681: ctx.setCurrentTargetArgsIndex(i);
0682: if (Boolean.TRUE.equals((Boolean) node
0683: .jjtGetChild(i).jjtAccept(this , ctx))) {
0684: ;//go on with next arg
0685: } else {
0686: return Boolean.FALSE;
0687: }
0688: }
0689: return Boolean.TRUE;
0690: } else {
0691: return Boolean.FALSE;
0692: }
0693: }
0694: }
0695: }
0696:
0697: public Object visit(ASTArgParameter node, Object data) {
0698: //TODO we are not doing any hierarchical test when the arg is bound
0699: // => args(e) and before(Exception e) will not mathch on catch(SubException e) ..
0700: // is that required ? how AJ syntax behaves ?
0701:
0702: TypePattern typePattern = node.getTypePattern();
0703: TypePattern realPattern = typePattern;
0704:
0705: // check if the arg is in the pointcut signature. In such a case, use the declared type
0706: //TODO can we improve that with a lazy attach of the realTypePattern to the node
0707: // and a method that always return the real pattern
0708: // It must be lazy since args are not added at info ctor time [can be refactored..]
0709: // do some filtering first to avoid unnecessary map lookup
0710:
0711: int pointcutArgIndex = -1;
0712: if (typePattern.getPattern().indexOf(".") < 0) {
0713: String boundedType = m_expressionInfo
0714: .getArgumentType(typePattern.getPattern());
0715: if (boundedType != null) {
0716: pointcutArgIndex = m_expressionInfo
0717: .getArgumentIndex(typePattern.getPattern());
0718: realPattern = TypePattern.compileTypePattern(
0719: boundedType,
0720: SubtypePatternType.NOT_HIERARCHICAL);
0721: }
0722: }
0723: // grab parameter from context
0724: ExpressionContext ctx = (ExpressionContext) data;
0725: ClassInfo argInfo = null;
0726: try {
0727: if (ctx.getReflectionInfo() instanceof MethodInfo) {
0728: argInfo = ((MethodInfo) ctx.getReflectionInfo())
0729: .getParameterTypes()[ctx
0730: .getCurrentTargetArgsIndex()];
0731: } else if (ctx.getReflectionInfo() instanceof ConstructorInfo) {
0732: argInfo = ((ConstructorInfo) ctx.getReflectionInfo())
0733: .getParameterTypes()[ctx
0734: .getCurrentTargetArgsIndex()];
0735: } else if (ctx.getReflectionInfo() instanceof FieldInfo) {
0736: argInfo = ((FieldInfo) ctx.getReflectionInfo())
0737: .getType();
0738: } else if (ctx.getPointcutType().equals(
0739: PointcutType.HANDLER)
0740: && ctx.getReflectionInfo() instanceof ClassInfo) {
0741: argInfo = (ClassInfo) ctx.getReflectionInfo();
0742: }
0743: } catch (ArrayIndexOutOfBoundsException e) {
0744: // ExpressionContext args are exhausted
0745: return Boolean.FALSE;
0746: }
0747: if (realPattern.matchType(argInfo)) {
0748: return Boolean.TRUE;
0749: } else {
0750: return Boolean.FALSE;
0751: }
0752: }
0753:
0754: public Object visit(ASTAttribute node, Object data) {
0755: boolean matchAnnotation = false;
0756: List annotations = (List) data;
0757: for (Iterator it = annotations.iterator(); it.hasNext();) {
0758: AnnotationInfo annotation = (AnnotationInfo) it.next();
0759: if (annotation.getName().equals(node.getName())) {
0760: matchAnnotation = true;
0761: }
0762: }
0763: if (node.isNot()) {
0764: return Util.booleanValueOf(!matchAnnotation);
0765: } else {
0766: return Util.booleanValueOf(matchAnnotation);
0767: }
0768: }
0769:
0770: public Object visit(ASTModifier node, Object data) {
0771: ReflectionInfo refInfo = (ReflectionInfo) data;
0772: int modifiersToMatch = refInfo.getModifiers();
0773: int modifierPattern = node.getModifier();
0774: if (node.isNot()) {
0775: if ((modifierPattern & Modifier.PUBLIC) != 0) {
0776: if (((modifiersToMatch & Modifier.PUBLIC) == 0)) {
0777: return Boolean.TRUE;
0778: } else {
0779: return Boolean.FALSE;
0780: }
0781: } else if ((modifierPattern & Modifier.PROTECTED) != 0) {
0782: if ((modifiersToMatch & Modifier.PROTECTED) == 0) {
0783: return Boolean.TRUE;
0784: } else {
0785: return Boolean.FALSE;
0786: }
0787: } else if ((modifierPattern & Modifier.PRIVATE) != 0) {
0788: if ((modifiersToMatch & Modifier.PRIVATE) == 0) {
0789: return Boolean.TRUE;
0790: } else {
0791: return Boolean.FALSE;
0792: }
0793: } else if ((modifierPattern & Modifier.STATIC) != 0) {
0794: if ((modifiersToMatch & Modifier.STATIC) == 0) {
0795: return Boolean.TRUE;
0796: } else {
0797: return Boolean.FALSE;
0798: }
0799: } else if ((modifierPattern & Modifier.SYNCHRONIZED) != 0) {
0800: if ((modifiersToMatch & Modifier.SYNCHRONIZED) == 0) {
0801: return Boolean.TRUE;
0802: } else {
0803: return Boolean.FALSE;
0804: }
0805: } else if ((modifierPattern & Modifier.FINAL) != 0) {
0806: if ((modifiersToMatch & Modifier.FINAL) == 0) {
0807: return Boolean.TRUE;
0808: } else {
0809: return Boolean.FALSE;
0810: }
0811: } else if ((modifierPattern & Modifier.TRANSIENT) != 0) {
0812: if ((modifiersToMatch & Modifier.TRANSIENT) == 0) {
0813: return Boolean.TRUE;
0814: } else {
0815: return Boolean.FALSE;
0816: }
0817: } else if ((modifierPattern & Modifier.VOLATILE) != 0) {
0818: if ((modifiersToMatch & Modifier.VOLATILE) == 0) {
0819: return Boolean.TRUE;
0820: } else {
0821: return Boolean.FALSE;
0822: }
0823: } else if ((modifierPattern & Modifier.STRICT) != 0) {
0824: if ((modifiersToMatch & Modifier.STRICT) == 0) {
0825: return Boolean.TRUE;
0826: } else {
0827: return Boolean.FALSE;
0828: }
0829: } else {
0830: return Boolean.FALSE;
0831: }
0832: } else {
0833: if ((modifierPattern & Modifier.PUBLIC) != 0) {
0834: if (((modifiersToMatch & Modifier.PUBLIC) == 0)) {
0835: return Boolean.FALSE;
0836: } else {
0837: return Boolean.TRUE;
0838: }
0839: } else if ((modifierPattern & Modifier.PROTECTED) != 0) {
0840: if ((modifiersToMatch & Modifier.PROTECTED) == 0) {
0841: return Boolean.FALSE;
0842: } else {
0843: return Boolean.TRUE;
0844: }
0845: } else if ((modifierPattern & Modifier.PRIVATE) != 0) {
0846: if ((modifiersToMatch & Modifier.PRIVATE) == 0) {
0847: return Boolean.FALSE;
0848: } else {
0849: return Boolean.TRUE;
0850: }
0851: } else if ((modifierPattern & Modifier.STATIC) != 0) {
0852: if ((modifiersToMatch & Modifier.STATIC) == 0) {
0853: return Boolean.FALSE;
0854: } else {
0855: return Boolean.TRUE;
0856: }
0857: } else if ((modifierPattern & Modifier.SYNCHRONIZED) != 0) {
0858: if ((modifiersToMatch & Modifier.SYNCHRONIZED) == 0) {
0859: return Boolean.FALSE;
0860: } else {
0861: return Boolean.TRUE;
0862: }
0863: } else if ((modifierPattern & Modifier.FINAL) != 0) {
0864: if ((modifiersToMatch & Modifier.FINAL) == 0) {
0865: return Boolean.FALSE;
0866: } else {
0867: return Boolean.TRUE;
0868: }
0869: } else if ((modifierPattern & Modifier.TRANSIENT) != 0) {
0870: if ((modifiersToMatch & Modifier.TRANSIENT) == 0) {
0871: return Boolean.FALSE;
0872: } else {
0873: return Boolean.TRUE;
0874: }
0875: } else if ((modifierPattern & Modifier.VOLATILE) != 0) {
0876: if ((modifiersToMatch & Modifier.VOLATILE) == 0) {
0877: return Boolean.FALSE;
0878: } else {
0879: return Boolean.TRUE;
0880: }
0881: } else if ((modifierPattern & Modifier.STRICT) != 0) {
0882: if ((modifiersToMatch & Modifier.STRICT) == 0) {
0883: return Boolean.FALSE;
0884: } else {
0885: return Boolean.TRUE;
0886: }
0887: } else {
0888: return Boolean.TRUE;
0889: }
0890: }
0891: }
0892:
0893: protected boolean visitAttributes(SimpleNode node,
0894: ReflectionInfo refInfo) {
0895: int nrChildren = node.jjtGetNumChildren();
0896: if (nrChildren != 0) {
0897: for (int i = 0; i < nrChildren; i++) {
0898: Node child = node.jjtGetChild(i);
0899: if (child instanceof ASTAttribute) {
0900: List annotations = refInfo.getAnnotations();
0901: if (Boolean.TRUE.equals(child.jjtAccept(this ,
0902: annotations))) {
0903: continue;
0904: } else {
0905: return false;
0906: }
0907: }
0908: }
0909: }
0910: return true;
0911: }
0912:
0913: protected boolean visitModifiers(SimpleNode node,
0914: ReflectionInfo refInfo) {
0915: int nrChildren = node.jjtGetNumChildren();
0916: if (nrChildren != 0) {
0917: for (int i = 0; i < nrChildren; i++) {
0918: Node child = node.jjtGetChild(i);
0919: if (child instanceof ASTModifier) {
0920: if (Boolean.TRUE.equals(child.jjtAccept(this ,
0921: refInfo))) {
0922: continue;
0923: } else {
0924: return false;
0925: }
0926: }
0927: }
0928: }
0929: return true;
0930: }
0931:
0932: protected boolean visitParameters(SimpleNode node,
0933: ClassInfo[] parameterTypes) {
0934: int nrChildren = node.jjtGetNumChildren();
0935: if (nrChildren <= 0) {
0936: return (parameterTypes.length == 0);
0937: }
0938:
0939: // collect the parameter nodes
0940: List parameterNodes = new ArrayList();
0941: for (int i = 0; i < nrChildren; i++) {
0942: Node child = node.jjtGetChild(i);
0943: if (child instanceof ASTParameter) {
0944: parameterNodes.add(child);
0945: }
0946: }
0947:
0948: if (parameterNodes.size() <= 0) {
0949: return (parameterTypes.length == 0);
0950: }
0951:
0952: //TODO duplicate code with args() match
0953: //TODO refactor parameterNodes in an array for faster match
0954:
0955: // look for eager pattern at the beginning and end
0956: int expressionParameterCount = parameterNodes.size();
0957: boolean isFirstArgEager = ((ASTParameter) parameterNodes.get(0))
0958: .getDeclaringClassPattern().isEagerWildCard();
0959: boolean isLastArgEager = ((ASTParameter) parameterNodes
0960: .get(expressionParameterCount - 1))
0961: .getDeclaringClassPattern().isEagerWildCard();
0962: // foo(..)
0963: if (isFirstArgEager && expressionParameterCount == 1) {
0964: return true;
0965: }
0966: int contextParametersCount = parameterTypes.length;
0967: if (isFirstArgEager && isLastArgEager) {
0968: expressionParameterCount -= 2;
0969: if (expressionParameterCount == 0) {
0970: // foo(.., ..)
0971: return true;
0972: }
0973: // we need to find a starting position - foo(..,int, bar, ..)
0974: // foo(int) //int is ok
0975: // foo(bar,int,bar) //int is ok
0976: // foo(bar,int,foo,int,bar) // int is ok, but then we fail, so move on to next..
0977: int matchCount = 0;
0978: int ictx = 0;
0979: for (int iexp = 0; iexp < expressionParameterCount; iexp++) {
0980: if (ictx >= contextParametersCount) {
0981: // too many args in foo()
0982: matchCount = -1;
0983: break;
0984: }
0985: // do we have an eager wildcard in the middle ?
0986: ASTParameter parameterNode = (ASTParameter) parameterNodes
0987: .get(iexp + 1);
0988: boolean isEager = parameterNode
0989: .getDeclaringClassPattern().isEagerWildCard();
0990: if (isEager) {
0991: // TODO - ignore for now, but not really supported - eager in the middle will match one
0992: }
0993: if (Boolean.TRUE.equals((Boolean) parameterNode
0994: .jjtAccept(this , parameterTypes[ictx]))) {
0995: matchCount += 1;
0996: ictx++;
0997: } else {
0998: // assume matched by starting ".." and rewind expression index
0999: matchCount = 0;
1000: ictx++;
1001: iexp = -1;
1002: }
1003: }
1004: if (matchCount == expressionParameterCount) {
1005: return true;
1006: } else {
1007: return false;
1008: }
1009: } else if (isFirstArgEager) {
1010: expressionParameterCount--;
1011: if (contextParametersCount >= expressionParameterCount) {
1012: // do a match from last to first, break when foo() nodes are exhausted
1013: for (int i = 0; (i < contextParametersCount)
1014: && (expressionParameterCount - i >= 0); i++) {
1015: ASTParameter parameterNode = (ASTParameter) parameterNodes
1016: .get(expressionParameterCount - i);
1017: if (Boolean.TRUE
1018: .equals((Boolean) parameterNode
1019: .jjtAccept(
1020: this ,
1021: parameterTypes[contextParametersCount
1022: - 1 - i]))) {
1023: ;//go on with "next" param
1024: } else {
1025: return false;
1026: }
1027: }
1028: return true;
1029: } else {
1030: //foo() as more param than context we try to match
1031: return false;
1032: }
1033: } else if (isLastArgEager) {
1034: expressionParameterCount--;
1035: if (contextParametersCount >= expressionParameterCount) {
1036: // do a match from first to last, break when foo() nodes are exhausted
1037: for (int i = 0; (i < contextParametersCount)
1038: && (i < expressionParameterCount); i++) {
1039: ASTParameter parameterNode = (ASTParameter) parameterNodes
1040: .get(i);
1041: if (Boolean.TRUE.equals((Boolean) parameterNode
1042: .jjtAccept(this , parameterTypes[i]))) {
1043: ;//go on with next param
1044: } else {
1045: return false;
1046: }
1047: }
1048: return true;
1049: } else {
1050: return false;
1051: }
1052: } else {
1053: // no eager wildcard in foo()
1054: // check that param length are equals
1055: if (expressionParameterCount == contextParametersCount) {
1056: for (int i = 0; i < parameterNodes.size(); i++) {
1057: ASTParameter parameterNode = (ASTParameter) parameterNodes
1058: .get(i);
1059: if (Boolean.TRUE.equals((Boolean) parameterNode
1060: .jjtAccept(this , parameterTypes[i]))) {
1061: ;//go on with next param
1062: } else {
1063: return false;
1064: }
1065: }
1066: return true;
1067: } else {
1068: return false;
1069: }
1070: }
1071: }
1072:
1073: /**
1074: * Returns the string representation of the expression.
1075: *
1076: * @return
1077: */
1078: public String toString() {
1079: return m_expression;
1080: }
1081:
1082: /**
1083: * Returns the number of parameters to the target method/constructor else -1.
1084: *
1085: * @param ctx
1086: * @return
1087: */
1088: private int getParametersCount(final ExpressionContext ctx) {
1089: ReflectionInfo reflectionInfo = ctx.getReflectionInfo();
1090: if (reflectionInfo instanceof MethodInfo) {
1091: return ((MethodInfo) reflectionInfo).getParameterTypes().length;
1092: } else if (reflectionInfo instanceof ConstructorInfo) {
1093: return ((ConstructorInfo) reflectionInfo)
1094: .getParameterTypes().length;
1095: } else if (reflectionInfo instanceof FieldInfo) {
1096: return 1;//field set support for args()
1097: } else if (ctx.getPointcutType().equals(PointcutType.HANDLER)
1098: && reflectionInfo instanceof ClassInfo) {
1099: // handler args(e) binding
1100: return 1;
1101: } else {
1102: return -1;
1103: }
1104: }
1105:
1106: /**
1107: * Test the context upon the expression tree, under a node that can
1108: * contain annotations.
1109: *
1110: * @param node root node of the annotation expression
1111: * @param reflectInfo context reflection info
1112: *
1113: * @return <CODE>Boolean.TRUE</CODE> in case the <tt>reflectInfo</tt> match
1114: * the expression subtree, <CODE>Boolean.FALSE</CODE> otherwise.
1115: */
1116: protected Object visitAnnotatedNode(SimpleNode node,
1117: ReflectionInfo reflectInfo) {
1118: // In an annotated subtree, only the last child node may represent the pattern
1119: Node patternNode = node
1120: .jjtGetChild(node.jjtGetNumChildren() - 1);
1121: if (!(patternNode instanceof ASTAttribute)) {
1122: if (Boolean.FALSE.equals((Boolean) patternNode.jjtAccept(
1123: this , reflectInfo))) {
1124: return Boolean.FALSE;
1125: }
1126: }
1127:
1128: boolean matchedAnnotations = visitAttributes(node, reflectInfo);
1129: if (!matchedAnnotations) {
1130: return Boolean.FALSE;
1131: } else {
1132: return Boolean.TRUE;
1133: }
1134: }
1135:
1136: /**
1137: * Access the ASTRoot we visit
1138: *
1139: * @return
1140: */
1141: public Node getASTRoot() {
1142: return m_root;
1143: }
1144:
1145: /**
1146: * Access the ExpressionInfo we are build on
1147: *
1148: * @return
1149: */
1150: public ExpressionInfo getExpressionInfo() {
1151: return m_expressionInfo;
1152: }
1153: }
|