From: acolyer Date: Tue, 13 Dec 2005 15:52:47 +0000 (+0000) Subject: permit extensions to pointcut designator set when using weaver tools API X-Git-Tag: V1_5_0RC1~16 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=dba2ee75f6cf5ee6de9462cb38a669bb488f2257;p=aspectj.git permit extensions to pointcut designator set when using weaver tools API --- diff --git a/weaver/src/org/aspectj/weaver/ast/ITestVisitor.java b/weaver/src/org/aspectj/weaver/ast/ITestVisitor.java index 67035e6dc..a80a8509c 100644 --- a/weaver/src/org/aspectj/weaver/ast/ITestVisitor.java +++ b/weaver/src/org/aspectj/weaver/ast/ITestVisitor.java @@ -13,6 +13,8 @@ package org.aspectj.weaver.ast; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; + public interface ITestVisitor { @@ -24,5 +26,6 @@ public interface ITestVisitor { void visit(Call call); void visit(FieldGetCall fieldGetCall); void visit(HasAnnotation hasAnnotation); + void visit(MatchingContextBasedTest matchingContextTest); } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java b/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java index 25586fbdd..f879633fd 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelRenderer.java @@ -42,6 +42,7 @@ import org.aspectj.weaver.ast.Or; import org.aspectj.weaver.ast.StringConstExpr; import org.aspectj.weaver.ast.Test; import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; // we generate right to left, btw. public class BcelRenderer implements ITestVisitor, IExprVisitor { @@ -202,6 +203,13 @@ public class BcelRenderer implements ITestVisitor, IExprVisitor { hasAnnotation.getVar().accept(this); } + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.ITestVisitor#visit(org.aspectj.weaver.internal.tools.MatchingContextBasedTest) + */ + public void visit(MatchingContextBasedTest matchingContextTest) { + throw new UnsupportedOperationException("matching context extension not supported in bytecode weaving"); + } + private InstructionList createJumpBasedOnBooleanOnStack() { InstructionList il = new InstructionList(); if (sk == fk) { diff --git a/weaver/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java b/weaver/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java new file mode 100644 index 000000000..b5a78e0e6 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/internal/tools/MatchingContextBasedTest.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import org.aspectj.weaver.ast.ITestVisitor; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.tools.MatchingContext; +import org.aspectj.weaver.tools.ContextBasedMatcher; + +/** + * Test that uses MatchingContext to match (or not) + * + */ +public class MatchingContextBasedTest extends Test { + + private final ContextBasedMatcher matcher; + + public MatchingContextBasedTest(ContextBasedMatcher pc) { + this.matcher = pc; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.ast.Test#accept(org.aspectj.weaver.ast.ITestVisitor) + */ + public void accept(ITestVisitor v) { + v.visit(this); + } + + public boolean matches(MatchingContext context) { + return this.matcher.matchesDynamically(context); + } + +} diff --git a/weaver/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java b/weaver/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java new file mode 100644 index 000000000..3934f56c9 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/internal/tools/PointcutDesignatorHandlerBasedPointcut.java @@ -0,0 +1,152 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.internal.tools; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import org.aspectj.util.FuzzyBoolean; +import org.aspectj.weaver.IntMap; +import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.Shadow; +import org.aspectj.weaver.ast.Literal; +import org.aspectj.weaver.ast.Test; +import org.aspectj.weaver.patterns.Bindings; +import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.patterns.FastMatchInfo; +import org.aspectj.weaver.patterns.IScope; +import org.aspectj.weaver.patterns.PatternNodeVisitor; +import org.aspectj.weaver.patterns.Pointcut; +import org.aspectj.weaver.reflect.ReflectionShadow; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.MatchingContext; + +/** + * Implementation of Pointcut that is backed by a user-extension + * pointcut designator handler. + * + */ +public class PointcutDesignatorHandlerBasedPointcut extends Pointcut{ + + private final ContextBasedMatcher matcher; + private final ReflectionWorld world; + + public PointcutDesignatorHandlerBasedPointcut( + ContextBasedMatcher expr, + ReflectionWorld world) { + this.matcher = expr; + this.world = world; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#getPointcutKind() + */ + public byte getPointcutKind() { + return Pointcut.USER_EXTENSION; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo) + */ + public FuzzyBoolean fastMatch(FastMatchInfo info) { + try { + return FuzzyBoolean.fromBoolean( + this.matcher.couldMatchJoinPointsInType( + Class.forName(info.getType().getName(),false,world.getClassLoader())) + ); + } catch (ClassNotFoundException cnfEx) { + return FuzzyBoolean.MAYBE; + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#couldMatchKinds() + */ + public Set couldMatchKinds() { + return Shadow.ALL_SHADOW_KINDS; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#matchInternal(org.aspectj.weaver.Shadow) + */ + protected FuzzyBoolean matchInternal(Shadow shadow) { + if (shadow instanceof ReflectionShadow) { + MatchingContext context = ((ReflectionShadow)shadow).getMatchingContext(); + org.aspectj.weaver.tools.FuzzyBoolean match = + this.matcher.matchesStatically(context); + if (match == org.aspectj.weaver.tools.FuzzyBoolean.MAYBE) { + return FuzzyBoolean.MAYBE; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.YES) { + return FuzzyBoolean.YES; + } else if (match == org.aspectj.weaver.tools.FuzzyBoolean.NO) { + return FuzzyBoolean.NO; + } + } + throw new IllegalStateException("Can only match user-extension pcds against Reflection shadows (not BCEL)"); + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope, org.aspectj.weaver.patterns.Bindings) + */ + protected void resolveBindings(IScope scope, Bindings bindings) { + // no-op + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap) + */ + protected Pointcut concretize1(ResolvedType inAspect, + ResolvedType declaringType, IntMap bindings) { + return this; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#findResidueInternal(org.aspectj.weaver.Shadow, org.aspectj.weaver.patterns.ExposedState) + */ + protected Test findResidueInternal(Shadow shadow, ExposedState state) { + if (!this.matcher.mayNeedDynamicTest()) { + return Literal.TRUE; + } else { + // could be more efficient here! + matchInternal(shadow); + return new MatchingContextBasedTest(this.matcher); + } + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.Pointcut#parameterizeWith(java.util.Map) + */ + public Pointcut parameterizeWith(Map typeVariableMap) { + return this; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream) + */ + public void write(DataOutputStream s) throws IOException { + throw new UnsupportedOperationException("can't write custom pointcut designator expressions to stream"); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.patterns.PatternNode#accept(org.aspectj.weaver.patterns.PatternNodeVisitor, java.lang.Object) + */ + public Object accept(PatternNodeVisitor visitor, Object data) { + //visitor.visit(this); + // no-op? + return data; + } + +} diff --git a/weaver/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java b/weaver/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java index fd0558cd8..631cadcad 100644 --- a/weaver/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java +++ b/weaver/src/org/aspectj/weaver/internal/tools/PointcutExpressionImpl.java @@ -32,6 +32,8 @@ import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; import org.aspectj.weaver.patterns.ThisOrTargetPointcut; import org.aspectj.weaver.reflect.ReflectionShadow; import org.aspectj.weaver.reflect.ShadowMatchImpl; +import org.aspectj.weaver.tools.DefaultMatchingContext; +import org.aspectj.weaver.tools.MatchingContext; import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParameter; import org.aspectj.weaver.tools.ShadowMatch; @@ -45,6 +47,7 @@ public class PointcutExpressionImpl implements PointcutExpression { private Pointcut pointcut; private String expression; private PointcutParameter[] parameters; + private MatchingContext matchContext = new DefaultMatchingContext(); public PointcutExpressionImpl(Pointcut pointcut, String expression, PointcutParameter[] params, World inWorld) { this.pointcut = pointcut; @@ -58,6 +61,13 @@ public class PointcutExpressionImpl implements PointcutExpression { return this.pointcut; } + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutExpression#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } + public boolean couldMatchJoinPointsInType(Class aClass) { return pointcut.fastMatch(new FastMatchInfo(world.resolve(aClass.getName()),null)).maybeTrue(); } @@ -81,7 +91,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } private ShadowMatch matchesExecution(Member aMember) { - Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember); + Shadow s = ReflectionShadow.makeExecutionShadow(world, aMember,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aMember); sm.setWithinCode(null); @@ -90,7 +100,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesStaticInitialization(Class aClass) { - Shadow s = ReflectionShadow.makeStaticInitializationShadow(world, aClass); + Shadow s = ReflectionShadow.makeStaticInitializationShadow(world, aClass,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(null); sm.setWithinCode(null); @@ -99,7 +109,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesAdviceExecution(Method aMethod) { - Shadow s = ReflectionShadow.makeAdviceExecutionShadow(world, aMethod); + Shadow s = ReflectionShadow.makeAdviceExecutionShadow(world, aMethod,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aMethod); sm.setWithinCode(null); @@ -108,7 +118,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesInitialization(Constructor aConstructor) { - Shadow s = ReflectionShadow.makeInitializationShadow(world, aConstructor); + Shadow s = ReflectionShadow.makeInitializationShadow(world, aConstructor,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aConstructor); sm.setWithinCode(null); @@ -117,7 +127,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesPreInitialization(Constructor aConstructor) { - Shadow s = ReflectionShadow.makePreInitializationShadow(world, aConstructor); + Shadow s = ReflectionShadow.makePreInitializationShadow(world, aConstructor,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aConstructor); sm.setWithinCode(null); @@ -126,7 +136,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesMethodCall(Method aMethod, Member withinCode) { - Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, withinCode); + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, withinCode,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aMethod); sm.setWithinCode(withinCode); @@ -135,7 +145,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesMethodCall(Method aMethod, Class callerType) { - Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, callerType); + Shadow s = ReflectionShadow.makeCallShadow(world, aMethod, callerType,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aMethod); sm.setWithinCode(null); @@ -144,7 +154,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesConstructorCall(Constructor aConstructor, Class callerType) { - Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, callerType); + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor, callerType,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aConstructor); sm.setWithinCode(null); @@ -153,7 +163,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesConstructorCall(Constructor aConstructor, Member withinCode) { - Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor,withinCode); + Shadow s = ReflectionShadow.makeCallShadow(world, aConstructor,withinCode,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aConstructor); sm.setWithinCode(withinCode); @@ -162,7 +172,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesHandler(Class exceptionType, Class handlingType) { - Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,handlingType); + Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,handlingType,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(null); sm.setWithinCode(null); @@ -171,7 +181,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesHandler(Class exceptionType, Member withinCode) { - Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,withinCode); + Shadow s = ReflectionShadow.makeHandlerShadow(world,exceptionType,withinCode,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(null); sm.setWithinCode(withinCode); @@ -180,7 +190,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesFieldGet(Field aField, Class withinType) { - Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinType); + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinType,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aField); sm.setWithinCode(null); @@ -189,7 +199,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesFieldGet(Field aField, Member withinCode) { - Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinCode); + Shadow s = ReflectionShadow.makeFieldGetShadow(world, aField, withinCode,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aField); sm.setWithinCode(withinCode); @@ -198,7 +208,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesFieldSet(Field aField, Class withinType) { - Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinType); + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinType,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aField); sm.setWithinCode(null); @@ -207,7 +217,7 @@ public class PointcutExpressionImpl implements PointcutExpression { } public ShadowMatch matchesFieldSet(Field aField, Member withinCode) { - Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinCode); + Shadow s = ReflectionShadow.makeFieldSetShadow(world, aField, withinCode,this.matchContext); ShadowMatchImpl sm = getShadowMatch(s); sm.setSubject(aField); sm.setWithinCode(withinCode); @@ -222,7 +232,9 @@ public class PointcutExpressionImpl implements PointcutExpression { if (match.maybeTrue()) { residueTest = pointcut.findResidue(forShadow, state); } - return new ShadowMatchImpl(match,residueTest,state,parameters); + ShadowMatchImpl sm = new ShadowMatchImpl(match,residueTest,state,parameters); + sm.setMatchingContext(this.matchContext); + return sm; } /* (non-Javadoc) diff --git a/weaver/src/org/aspectj/weaver/patterns/PatternParser.java b/weaver/src/org/aspectj/weaver/patterns/PatternParser.java index ede05652f..bb1eca20e 100644 --- a/weaver/src/org/aspectj/weaver/patterns/PatternParser.java +++ b/weaver/src/org/aspectj/weaver/patterns/PatternParser.java @@ -16,12 +16,20 @@ package org.aspectj.weaver.patterns; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Set; import org.aspectj.weaver.ISourceContext; import org.aspectj.weaver.Member; import org.aspectj.weaver.Shadow; import org.aspectj.weaver.UnresolvedType; +import org.aspectj.weaver.World; +import org.aspectj.weaver.internal.tools.PointcutDesignatorHandlerBasedPointcut; +import org.aspectj.weaver.reflect.ReflectionWorld; +import org.aspectj.weaver.tools.ContextBasedMatcher; +import org.aspectj.weaver.tools.PointcutDesignatorHandler; //XXX doesn't handle errors for extra tokens very well (sometimes ignores) public class PatternParser { @@ -31,6 +39,10 @@ public class PatternParser { /** not thread-safe, but this class is not intended to be... */ private boolean allowHasTypePatterns = false; + + /** extension handlers used in weaver tools API only */ + private Set pointcutDesignatorHandlers = Collections.EMPTY_SET; + private ReflectionWorld world; /** * Constructor for PatternParser. @@ -40,6 +52,12 @@ public class PatternParser { this.tokenSource = tokenSource; this.sourceContext = tokenSource.getSourceContext(); } + + /** only used by weaver tools API */ + public void setPointcutDesignatorHandlers(Set handlers, ReflectionWorld world) { + this.pointcutDesignatorHandlers = handlers; + this.world = world; + } public PerClause maybeParsePerClause() { IToken tok = tokenSource.peek(); @@ -354,10 +372,23 @@ public class PatternParser { // TODO - Alex has some token stuff going on here to get a readable name in place of ""... p = new IfPointcut(""); } - } + } else { - tokenSource.setIndex(start); - p = parseReferencePointcut(); + boolean matchedByExtensionDesignator = false; + // see if a registered handler wants to parse it, otherwise + // treat as a reference pointcut + for (Iterator iter = this.pointcutDesignatorHandlers.iterator(); iter.hasNext();) { + PointcutDesignatorHandler pcd = (PointcutDesignatorHandler) iter.next(); + if (pcd.getDesignatorName().equals(kind)) { + p = parseDesignatorPointcut(pcd); + matchedByExtensionDesignator = true; + } + + } + if (!matchedByExtensionDesignator) { + tokenSource.setIndex(start); + p = parseReferencePointcut(); + } } return p; } @@ -519,6 +550,27 @@ public class PatternParser { TypePatternList arguments = parseArgumentsPattern(); return new ReferencePointcut(onType, name.maybeGetSimpleName(), arguments); } + + private Pointcut parseDesignatorPointcut(PointcutDesignatorHandler pcdHandler) { + eat("("); + int parenCount = 1; + StringBuffer pointcutBody = new StringBuffer(); + while (parenCount > 0) { + if (maybeEat("(")) { + parenCount++; + pointcutBody.append("("); + } else if (maybeEat(")")) { + parenCount--; + if (parenCount > 0) { + pointcutBody.append(")"); + } + } else { + pointcutBody.append(nextToken().getString()); + } + } + ContextBasedMatcher pcExpr = pcdHandler.parse(pointcutBody.toString()); + return new PointcutDesignatorHandlerBasedPointcut(pcExpr,world); + } public List parseDottedIdentifier() { diff --git a/weaver/src/org/aspectj/weaver/patterns/Pointcut.java b/weaver/src/org/aspectj/weaver/patterns/Pointcut.java index 1cf99da28..4fdf124ba 100644 --- a/weaver/src/org/aspectj/weaver/patterns/Pointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/Pointcut.java @@ -175,6 +175,7 @@ public abstract class Pointcut extends PatternNode { public static final byte NONE = 20; // DO NOT CHANGE OR REORDER THIS SEQUENCE, THIS VALUE CAN BE PUT OUT BY ASPECTJ1.2.1 public static final byte ATARGS = 21; + public static final byte USER_EXTENSION = 22; public byte getPointcutKind() { return pointcutKind; } diff --git a/weaver/src/org/aspectj/weaver/reflect/ReflectionShadow.java b/weaver/src/org/aspectj/weaver/reflect/ReflectionShadow.java index a8ce5659d..6f6f98926 100644 --- a/weaver/src/org/aspectj/weaver/reflect/ReflectionShadow.java +++ b/weaver/src/org/aspectj/weaver/reflect/ReflectionShadow.java @@ -25,6 +25,7 @@ import org.aspectj.weaver.Shadow; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.World; import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.tools.MatchingContext; /** * @author colyer @@ -32,9 +33,10 @@ import org.aspectj.weaver.ast.Var; */ public class ReflectionShadow extends Shadow { - private World world; - private ResolvedType enclosingType; - private ResolvedMember enclosingMember; + private final World world; + private final ResolvedType enclosingType; + private final ResolvedMember enclosingMember; + private final MatchingContext matchContext; private Var thisVar = null; private Var targetVar = null; private Var[] argsVars = null; @@ -46,118 +48,119 @@ public class ReflectionShadow extends Shadow { private Map annotationVar = new HashMap(); private AnnotationFinder annotationFinder; - public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod) { + public static Shadow makeExecutionShadow(World inWorld, java.lang.reflect.Member forMethod, MatchingContext withContext) { Kind kind = (forMethod instanceof Method) ? Shadow.MethodExecution : Shadow.ConstructorExecution; Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forMethod, inWorld); ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); } - public static Shadow makeAdviceExecutionShadow(World inWorld, java.lang.reflect.Method forMethod) { + public static Shadow makeAdviceExecutionShadow(World inWorld, java.lang.reflect.Method forMethod, MatchingContext withContext) { Kind kind = Shadow.AdviceExecution; Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedAdviceMember(forMethod, inWorld); ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); } - public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, java.lang.reflect.Member withinCode) { - Shadow enclosingShadow = makeExecutionShadow(inWorld,withinCode); + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, java.lang.reflect.Member withinCode, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,withinCode,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, Class thisClass) { - Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, thisClass); + public static Shadow makeCallShadow(World inWorld, java.lang.reflect.Member aMember, Class thisClass, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, thisClass,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(aMember, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(thisClass, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = aMember instanceof Method ? Shadow.MethodCall : Shadow.ConstructorCall; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeStaticInitializationShadow(World inWorld, Class forType) { + public static Shadow makeStaticInitializationShadow(World inWorld, Class forType, MatchingContext withContext) { Member signature = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(forType, inWorld); ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); Kind kind = Shadow.StaticInitialization; - return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); } - public static Shadow makePreInitializationShadow(World inWorld, Constructor forConstructor) { + public static Shadow makePreInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { Kind kind = Shadow.PreInitialization; Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); } - public static Shadow makeInitializationShadow(World inWorld, Constructor forConstructor) { + public static Shadow makeInitializationShadow(World inWorld, Constructor forConstructor, MatchingContext withContext) { Kind kind = Shadow.Initialization; Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(forConstructor, inWorld); ResolvedType enclosingType = signature.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null); + return new ReflectionShadow(inWorld,kind,signature,null,enclosingType,null,withContext); } - public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, Class withinType) { + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, Class withinType, MatchingContext withContext) { Kind kind = Shadow.ExceptionHandler; - Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, withinType); + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, withinType,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinType, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(withinType, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, java.lang.reflect.Member withinCode) { + public static Shadow makeHandlerShadow(World inWorld, Class exceptionType, java.lang.reflect.Member withinCode, MatchingContext withContext) { Kind kind = Shadow.ExceptionHandler; - Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode); + Shadow enclosingShadow = makeExecutionShadow(inWorld, withinCode,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createHandlerMember(exceptionType, withinCode.getDeclaringClass(), inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(withinCode, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeFieldGetShadow(World inWorld, Field forField, Class callerType) { - Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType); + public static Shadow makeFieldGetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = Shadow.FieldGet; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeFieldGetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember) { - Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember); + public static Shadow makeFieldGetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = Shadow.FieldGet; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeFieldSetShadow(World inWorld, Field forField, Class callerType) { - Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType); + public static Shadow makeFieldSetShadow(World inWorld, Field forField, Class callerType, MatchingContext withContext) { + Shadow enclosingShadow = makeStaticInitializationShadow(inWorld, callerType, withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createStaticInitMember(callerType, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = Shadow.FieldSet; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public static Shadow makeFieldSetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember) { - Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember); + public static Shadow makeFieldSetShadow(World inWorld, Field forField, java.lang.reflect.Member inMember, MatchingContext withContext) { + Shadow enclosingShadow = makeExecutionShadow(inWorld,inMember,withContext); Member signature = ReflectionBasedReferenceTypeDelegateFactory.createResolvedField(forField, inWorld); ResolvedMember enclosingMember = ReflectionBasedReferenceTypeDelegateFactory.createResolvedMember(inMember, inWorld); ResolvedType enclosingType = enclosingMember.getDeclaringType().resolve(inWorld); Kind kind = Shadow.FieldSet; - return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember); + return new ReflectionShadow(inWorld,kind,signature,enclosingShadow,enclosingType,enclosingMember,withContext); } - public ReflectionShadow(World world, Kind kind, Member signature, Shadow enclosingShadow, ResolvedType enclosingType, ResolvedMember enclosingMember) { + public ReflectionShadow(World world, Kind kind, Member signature, Shadow enclosingShadow, ResolvedType enclosingType, ResolvedMember enclosingMember, MatchingContext withContext) { super(kind,signature,enclosingShadow); this.world = world; this.enclosingType = enclosingType; this.enclosingMember = enclosingMember; + this.matchContext = withContext; if (world instanceof ReflectionWorld) { this.annotationFinder = ((ReflectionWorld)world).getAnnotationFinder(); } @@ -336,4 +339,7 @@ public class ReflectionShadow extends Shadow { return null; } + public MatchingContext getMatchingContext() { + return this.matchContext; + } } diff --git a/weaver/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java b/weaver/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java index 1cc631e77..88309b8aa 100644 --- a/weaver/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java +++ b/weaver/src/org/aspectj/weaver/reflect/ShadowMatchImpl.java @@ -27,8 +27,11 @@ import org.aspectj.weaver.ast.Not; import org.aspectj.weaver.ast.Or; import org.aspectj.weaver.ast.Test; import org.aspectj.weaver.ast.Var; +import org.aspectj.weaver.internal.tools.MatchingContextBasedTest; import org.aspectj.weaver.patterns.ExposedState; +import org.aspectj.weaver.tools.DefaultMatchingContext; import org.aspectj.weaver.tools.JoinPointMatch; +import org.aspectj.weaver.tools.MatchingContext; import org.aspectj.weaver.tools.PointcutParameter; import org.aspectj.weaver.tools.ShadowMatch; @@ -45,6 +48,7 @@ public class ShadowMatchImpl implements ShadowMatch { private Member withinCode; private Member subject; private Class withinType; + private MatchingContext matchContext = new DefaultMatchingContext(); public ShadowMatchImpl(FuzzyBoolean match, Test test, ExposedState state, PointcutParameter[] params) { this.match = match; @@ -71,12 +75,19 @@ public class ShadowMatchImpl implements ShadowMatch { public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) { if (neverMatches()) return JoinPointMatchImpl.NO_MATCH; - if (new RuntimeTestEvaluator(residualTest,thisObject,targetObject,args).matches()) { + if (new RuntimeTestEvaluator(residualTest,thisObject,targetObject,args,this.matchContext).matches()) { return new JoinPointMatchImpl(getPointcutParameters(thisObject,targetObject,args)); } else { return JoinPointMatchImpl.NO_MATCH; } } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ShadowMatch#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) + */ + public void setMatchingContext(MatchingContext aMatchContext) { + this.matchContext = aMatchContext; + } private PointcutParameter[] getPointcutParameters(Object thisObject, Object targetObject, Object[] args) { Var[] vars = state.vars; @@ -91,16 +102,19 @@ public class ShadowMatchImpl implements ShadowMatch { private static class RuntimeTestEvaluator implements ITestVisitor { private boolean matches = true; - private Test test; - private Object thisObject; - private Object targetObject; - private Object[] args; + private final Test test; + private final Object thisObject; + private final Object targetObject; + private final Object[] args; + private final MatchingContext matchContext; + - public RuntimeTestEvaluator(Test aTest,Object thisObject, Object targetObject, Object[] args) { + public RuntimeTestEvaluator(Test aTest,Object thisObject, Object targetObject, Object[] args, MatchingContext context) { this.test = aTest; this.thisObject = thisObject; this.targetObject = targetObject; this.args = args; + this.matchContext = context; } public boolean matches() { @@ -110,11 +124,11 @@ public class ShadowMatchImpl implements ShadowMatch { public void visit(And e) { boolean leftMatches = - new RuntimeTestEvaluator(e.getLeft(),thisObject,targetObject,args).matches(); + new RuntimeTestEvaluator(e.getLeft(),thisObject,targetObject,args,matchContext).matches(); if (!leftMatches) { matches = false; } else { - matches = new RuntimeTestEvaluator(e.getRight(),thisObject,targetObject,args).matches(); + matches = new RuntimeTestEvaluator(e.getRight(),thisObject,targetObject,args,matchContext).matches(); } } @@ -126,18 +140,22 @@ public class ShadowMatchImpl implements ShadowMatch { ResolvedType actualType = world.resolve(value.getClass().getName()); matches = desiredType.isAssignableFrom(actualType); } + + public void visit(MatchingContextBasedTest matchingContextTest) { + matches = matchingContextTest.matches(this.matchContext); + } public void visit(Not not) { - matches = ! new RuntimeTestEvaluator(not.getBody(),thisObject,targetObject,args).matches(); + matches = ! new RuntimeTestEvaluator(not.getBody(),thisObject,targetObject,args,matchContext).matches(); } public void visit(Or or) { boolean leftMatches = - new RuntimeTestEvaluator(or.getLeft(),thisObject,targetObject,args).matches(); + new RuntimeTestEvaluator(or.getLeft(),thisObject,targetObject,args,matchContext).matches(); if (leftMatches) { matches = true; } else { - matches = new RuntimeTestEvaluator(or.getRight(),thisObject,targetObject,args).matches(); + matches = new RuntimeTestEvaluator(or.getRight(),thisObject,targetObject,args,matchContext).matches(); } } diff --git a/weaver/src/org/aspectj/weaver/tools/ContextBasedMatcher.java b/weaver/src/org/aspectj/weaver/tools/ContextBasedMatcher.java new file mode 100644 index 000000000..92a636866 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/ContextBasedMatcher.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * Pointcut expression interface for pointcut + * expressions returned by a + * PointcutDesignatorHandler. Provides an additional + * matching method for matching based on context + * information over and above that normally used + * by AspectJ. + * + * @see MatchingContext + * + */ +public interface ContextBasedMatcher { + + /** + * return true iff this matcher could ever match + * a join point in the given type + */ + boolean couldMatchJoinPointsInType(Class aClass); + + /** + * return true if matchesStatically can ever return + * FuzzyBoolean.MAYBE (necessitating a per-join point test + * to determine matching at a given join point). + */ + boolean mayNeedDynamicTest(); + + /** + * Return FuzzyBoolean.YES if a join point with the given + * matching context is always matched. + * Return FuzzyBoolean.NO if a join point with the given + * matching context is never matched. + * Return FuzzyBoolean.MAYBE if a match cannot be determined + * statically (whilst generating a ShadowMatch), and must + * be determined on a per-join point basis. + */ + FuzzyBoolean matchesStatically(MatchingContext matchContext); + + /** + * Called during processing of ShadowMatch.matchesJoinPoint + * when matchesStatically returned FuzzyBoolean.MAYBE. + */ + boolean matchesDynamically(MatchingContext matchContext); +} diff --git a/weaver/src/org/aspectj/weaver/tools/DefaultMatchingContext.java b/weaver/src/org/aspectj/weaver/tools/DefaultMatchingContext.java new file mode 100644 index 000000000..fa47a87a3 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/DefaultMatchingContext.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import java.util.HashMap; +import java.util.Map; + +/** + * Default implementation of MatchingContext, backed + * by a Map. + */ +public class DefaultMatchingContext implements MatchingContext { + + private Map contextMap = new HashMap(); + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#hasContextParameter(java.lang.String) + */ + public boolean hasContextBinding(String contextParameterName) { + return this.contextMap.containsKey(contextParameterName); + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.MatchingContext#get(java.lang.String) + */ + public Object getBinding(String contextParameterName) { + return this.contextMap.get(contextParameterName); + } + + /** + * Add a context binding with the given name and value + * @param name + * @param value + */ + public void addContextBinding(String name, Object value) { + this.contextMap.put(name, value); + } + + /** + * Remove the context binding with the given name + * @param name + */ + public void removeContextBinding(String name) { + this.contextMap.remove(name); + } + +} diff --git a/weaver/src/org/aspectj/weaver/tools/MatchingContext.java b/weaver/src/org/aspectj/weaver/tools/MatchingContext.java new file mode 100644 index 000000000..e668a30b7 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/MatchingContext.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +/** + * When extending AspectJ's pointcut parsing and + * matching with custom PointcutDesignatorHandlers, + * it may be necessary to match based on context information + * at a join point not exposed simply by java.lang.reflect + * member information or argument values. The matching context + * interface provides an extension point for the specification + * of additional shadow and join point context that can be + * taken into account during the matching process. + * + * @see DefaultMatchingContext + */ +public interface MatchingContext { + + /** + * Returns true iff this matching context has a defined + * binding for the given context parameter. + * @param contextParameterName + */ + boolean hasContextBinding(String contextParameterName); + + /** + * returns the binding associated with the + * given context parameter name (or null if + * there is no such context). + * @param contextParameterName + * @return + */ + Object getBinding(String contextParameterName); +} diff --git a/weaver/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java b/weaver/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java new file mode 100644 index 000000000..63f4a81e2 --- /dev/null +++ b/weaver/src/org/aspectj/weaver/tools/PointcutDesignatorHandler.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + + +/** + * The PointcutDesignator interface allows extension of the + * AspectJ pointcut language so that third-party tools integrating + * with AspectJ can add easily their own custom + * domain-specific designators and have them interoperate seamlessly + * with the standard AspectJ designators. + * + * A pointcut designator can only be used for matching, not for + * binding. + */ +public interface PointcutDesignatorHandler { + + /** + * The name of this pointcut designator. For example, + * if this designator handles a "bean(<NamePattern>) + * format designator, this method would return "bean". + * @return + */ + String getDesignatorName() ; + + /** + * Parse the given expression string + * and return a ContextBasedMatcher that can be used + * for matching. + * @param expression the body of the pointcut expression. + * For example, given the expression "bean(*DAO)" the parse + * method will be called with the argument "*DAO". + * @return a pointcut expression that can be used for + * matching. + * @throws IllegalArgumentException if the expression + * is ill-formed. + */ + ContextBasedMatcher parse(String expression); + +} diff --git a/weaver/src/org/aspectj/weaver/tools/PointcutExpression.java b/weaver/src/org/aspectj/weaver/tools/PointcutExpression.java index 64d82a96a..fe5de47ee 100644 --- a/weaver/src/org/aspectj/weaver/tools/PointcutExpression.java +++ b/weaver/src/org/aspectj/weaver/tools/PointcutExpression.java @@ -22,6 +22,13 @@ import java.lang.reflect.Method; */ public interface PointcutExpression { + /** + * Set the matching context to be used for + * subsequent calls to match. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); + /** * Determine whether or not this pointcut could ever match a join point in the given class. * @param aClass the candidate class diff --git a/weaver/src/org/aspectj/weaver/tools/PointcutParser.java b/weaver/src/org/aspectj/weaver/tools/PointcutParser.java index d52a92687..f4c68eff3 100644 --- a/weaver/src/org/aspectj/weaver/tools/PointcutParser.java +++ b/weaver/src/org/aspectj/weaver/tools/PointcutParser.java @@ -55,9 +55,10 @@ import org.aspectj.weaver.reflect.ReflectionWorld; */ public class PointcutParser { - private World world; + private ReflectionWorld world; private ClassLoader classLoader; private Set supportedPrimitives; + private Set pointcutDesignators = new HashSet(); /** * @return a Set containing every PointcutPrimitive except @@ -246,6 +247,23 @@ public class PointcutParser { getWorld().getLint().setFromProperties(properties); } + /** + * Register a new pointcut designator handler with this parser. + * This provides an extension mechansim for the integration of + * domain-specific pointcut designators with the AspectJ + * pointcut language. + * @param designatorHandler + */ + public void registerPointcutDesignatorHandler(PointcutDesignatorHandler designatorHandler) { + this.pointcutDesignators.add(designatorHandler); + } + + /** + * Create a pointcut parameter of the given name and type. + * @param name + * @param type + * @return + */ public PointcutParameter createPointcutParameter(String name, Class type) { return new PointcutParameterImpl(name,type); } @@ -283,7 +301,9 @@ public class PointcutParser { throws UnsupportedPointcutPrimitiveException, IllegalArgumentException { PointcutExpressionImpl pcExpr = null; try { - Pointcut pc = new PatternParser(expression).parsePointcut(); + PatternParser parser = new PatternParser(expression); + parser.setPointcutDesignatorHandlers(pointcutDesignators, world); + Pointcut pc = parser.parsePointcut(); validateAgainstSupportedPrimitives(pc,expression); IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope),formalParameters); pc = pc.resolve(resolutionScope); @@ -450,6 +470,9 @@ public class PointcutParser { if (!supportedPrimitives.contains(PointcutPrimitive.REFERENCE)) throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.REFERENCE); break; + case Pointcut.USER_EXTENSION: + // always ok... + break; case Pointcut.NONE: // deliberate fall-through default: throw new IllegalArgumentException("Unknown pointcut kind: " + pc.getPointcutKind()); diff --git a/weaver/src/org/aspectj/weaver/tools/ShadowMatch.java b/weaver/src/org/aspectj/weaver/tools/ShadowMatch.java index 85170f451..55e2581df 100644 --- a/weaver/src/org/aspectj/weaver/tools/ShadowMatch.java +++ b/weaver/src/org/aspectj/weaver/tools/ShadowMatch.java @@ -48,4 +48,11 @@ public interface ShadowMatch { * @return */ JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args); + + /** + * Set a matching context to be used when matching + * join points. + * @see MatchingContext + */ + void setMatchingContext(MatchingContext aMatchContext); } diff --git a/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java b/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java new file mode 100644 index 000000000..e26fe73d3 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/PointcutDesignatorHandlerTests.java @@ -0,0 +1,142 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.weaver.tools; + +import junit.framework.TestCase; + +/** + * @author Adrian + * + */ +public class PointcutDesignatorHandlerTests extends TestCase { + + public void testParseWithoutHandler() { + try { + PointcutParser + .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() + .parsePointcutExpression("bean(service.*"); + fail("should not be able to parse bean(service.*)"); + } catch(IllegalArgumentException ex) { + assertTrue("contains bean",ex.getMessage().contains("bean")); + } + } + + public void testParseWithHandler() { + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + parser.parsePointcutExpression("bean(service.*)"); + assertEquals("service.*",beanHandler.getExpressionLastAskedToParse()); + } + + public void testStaticMatch() throws Exception { + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + parser.registerPointcutDesignatorHandler(beanHandler); + PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); + DefaultMatchingContext context = new DefaultMatchingContext(); + context.addContextBinding("beanName","myBean"); + pc.setMatchingContext(context); + ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + assertTrue(sm.alwaysMatches()); + context.addContextBinding("beanName", "notMyBean"); + sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + assertTrue(sm.neverMatches()); + } + + public void testDynamicMatch() throws Exception { + PointcutParser parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); + BeanDesignatorHandler beanHandler = new BeanDesignatorHandler(); + beanHandler.simulateDynamicTest = true; + parser.registerPointcutDesignatorHandler(beanHandler); + PointcutExpression pc = parser.parsePointcutExpression("bean(myBean)"); + ShadowMatch sm = pc.matchesMethodExecution(Object.class.getMethod("toString",new Class[0])); + DefaultMatchingContext context = new DefaultMatchingContext(); + assertTrue(sm.maybeMatches()); + assertFalse(sm.alwaysMatches()); + assertFalse(sm.neverMatches()); + context.addContextBinding("beanName","myBean"); + sm.setMatchingContext(context); + assertTrue(sm.matchesJoinPoint(null, null, null).matches()); + context.addContextBinding("beanName", "notMyBean"); + assertFalse(sm.matchesJoinPoint(null, null, null).matches()); + } + + + + private class BeanDesignatorHandler implements PointcutDesignatorHandler { + + private String askedToParse; + public boolean simulateDynamicTest = false; + + public String getDesignatorName() { + return "bean"; + } + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.PointcutDesignatorHandler#parse(java.lang.String) + */ + public ContextBasedMatcher parse(String expression) { + this.askedToParse = expression; + return new BeanPointcutExpression(expression,this.simulateDynamicTest); + } + + public String getExpressionLastAskedToParse() { + return this.askedToParse; + } + } + + private class BeanPointcutExpression implements ContextBasedMatcher { + + private final String beanNamePattern; + private final boolean simulateDynamicTest; + + public BeanPointcutExpression(String beanNamePattern, boolean simulateDynamicTest) { + this.beanNamePattern = beanNamePattern; + this.simulateDynamicTest = simulateDynamicTest; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#couldMatchJoinPointsInType(java.lang.Class) + */ + public boolean couldMatchJoinPointsInType(Class aClass) { + return true; + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#mayNeedDynamicTest() + */ + public boolean mayNeedDynamicTest() { + return this.simulateDynamicTest; + } + + + public FuzzyBoolean matchesStatically(MatchingContext matchContext) { + if (this.simulateDynamicTest) return FuzzyBoolean.MAYBE; + if (this.beanNamePattern.equals(matchContext.getBinding("beanName"))) { + return FuzzyBoolean.YES; + } else { + return FuzzyBoolean.NO; + } + } + + + /* (non-Javadoc) + * @see org.aspectj.weaver.tools.ContextBasedMatcher#matchesDynamically(org.aspectj.weaver.tools.MatchingContext) + */ + public boolean matchesDynamically(MatchingContext matchContext) { + return this.beanNamePattern.equals(matchContext.getBinding("beanName")); + } + } +} \ No newline at end of file diff --git a/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java b/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java index aa91c9f65..f98303ad5 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/ToolsTests.java @@ -20,6 +20,7 @@ public class ToolsTests { suite.addTestSuite(PointcutParserTest.class); suite.addTestSuite(PointcutExpressionTest.class); suite.addTestSuite(TypePatternMatcherTest.class); + suite.addTestSuite(PointcutDesignatorHandlerTests.class); //$JUnit-END$ return suite; }