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