org.aspectj.weaver.tools.StandardPointcutParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjmatcher Show documentation
Show all versions of aspectjmatcher Show documentation
The AspectJ matcher can be used for matching pointcuts independently of any AspectJ compilation or weaving steps.
Most notably, this can be used by frameworks such as Spring AOP which utilise the @AspectJ pointcut syntax but
implement aspect weaving in a way independent of AspectJ, e.g. using dynamic proxies.
/*******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.aspectj.weaver.tools;
import java.io.File;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.weaver.BindingScope;
import org.aspectj.weaver.IHasPosition;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.internal.tools.StandardPointcutExpressionImpl;
import org.aspectj.weaver.internal.tools.TypePatternMatcherImpl;
import org.aspectj.weaver.patterns.AndPointcut;
import org.aspectj.weaver.patterns.CflowPointcut;
import org.aspectj.weaver.patterns.FormalBinding;
import org.aspectj.weaver.patterns.IScope;
import org.aspectj.weaver.patterns.KindedPointcut;
import org.aspectj.weaver.patterns.NotPointcut;
import org.aspectj.weaver.patterns.OrPointcut;
import org.aspectj.weaver.patterns.ParserException;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.SimpleScope;
import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut;
import org.aspectj.weaver.patterns.ThisOrTargetPointcut;
import org.aspectj.weaver.patterns.TypePattern;
import org.aspectj.weaver.reflect.PointcutParameterImpl;
import org.aspectj.weaver.reflect.ReflectionWorld;
/**
* A PointcutParser can be used to build PointcutExpressions for a user-defined subset of AspectJ's pointcut language
*/
public class StandardPointcutParser {
private World world;
private final Set supportedPrimitives;
private final Set pointcutDesignators = new HashSet<>();
/**
* @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser
* constructor).
*/
public static Set getAllSupportedPointcutPrimitives() {
Set primitives = new HashSet<>();
primitives.add(PointcutPrimitive.ADVICE_EXECUTION);
primitives.add(PointcutPrimitive.ARGS);
primitives.add(PointcutPrimitive.CALL);
primitives.add(PointcutPrimitive.EXECUTION);
primitives.add(PointcutPrimitive.GET);
primitives.add(PointcutPrimitive.HANDLER);
primitives.add(PointcutPrimitive.INITIALIZATION);
primitives.add(PointcutPrimitive.PRE_INITIALIZATION);
primitives.add(PointcutPrimitive.SET);
primitives.add(PointcutPrimitive.STATIC_INITIALIZATION);
primitives.add(PointcutPrimitive.TARGET);
primitives.add(PointcutPrimitive.THIS);
primitives.add(PointcutPrimitive.WITHIN);
primitives.add(PointcutPrimitive.WITHIN_CODE);
primitives.add(PointcutPrimitive.AT_ANNOTATION);
primitives.add(PointcutPrimitive.AT_THIS);
primitives.add(PointcutPrimitive.AT_TARGET);
primitives.add(PointcutPrimitive.AT_ARGS);
primitives.add(PointcutPrimitive.AT_WITHIN);
primitives.add(PointcutPrimitive.AT_WITHINCODE);
primitives.add(PointcutPrimitive.REFERENCE);
return primitives;
}
/**
* Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions:
*
* - The
if, cflow, and cflowbelow
pointcut designators are not supported
* - Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts
*
- The pointcut expression must be anonymous with no formals allowed.
*
*
* When resolving types in pointcut expressions, the context classloader is used to find types.
*
*/
public static StandardPointcutParser getPointcutParserSupportingAllPrimitives(World world) {
StandardPointcutParser p = new StandardPointcutParser(world);
return p;
}
/**
* Returns a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported
* pointcut primitives. The following restrictions apply:
*
* - The
if, cflow, and cflowbelow
pointcut designators are not supported
* - Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts
*
- The pointcut expression must be anonymous with no formals allowed.
*
*
* When resolving types in pointcut expressions, the given classloader is used to find types.
*
*
* @param supportedPointcutKinds a set of PointcutPrimitives this parser should support
* @throws UnsupportedOperationException if the set contains if, cflow, or cflow below
*/
public static StandardPointcutParser getPointcutParserSupportingSpecifiedPrimitives(Set supportedPointcutKinds, World world) {
StandardPointcutParser p = new StandardPointcutParser(supportedPointcutKinds, world);
return p;
}
/**
* Create a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions:
*
* - The
if, cflow, and cflowbelow
pointcut designators are not supported
* - Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts
*
- The pointcut expression must be anonymous with no formals allowed.
*
*/
protected StandardPointcutParser(World world) {
supportedPrimitives = getAllSupportedPointcutPrimitives();
this.world = world;
}
/**
* Create a pointcut parser that can parse pointcut expressions built from a user-defined subset of AspectJ's supported pointcut
* primitives. The following restrictions apply:
*
* - The
if, cflow, and cflowbelow
pointcut designators are not supported
* - Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts
*
- The pointcut expression must be anonymous with no formals allowed.
*
*
* @param supportedPointcutKinds a set of PointcutPrimitives this parser should support
* @throws UnsupportedOperationException if the set contains if, cflow, or cflow below
*/
private StandardPointcutParser(Set supportedPointcutKinds, World world) {
supportedPrimitives = supportedPointcutKinds;
for (PointcutPrimitive element : supportedPointcutKinds) {
if ((element == PointcutPrimitive.IF) || (element == PointcutPrimitive.CFLOW)
|| (element == PointcutPrimitive.CFLOW_BELOW)) {
throw new UnsupportedOperationException("Cannot handle if, cflow, and cflowbelow primitives");
}
}
this.world = world;
}
// /**
// * Set the lint properties for this parser from the given resource on the classpath.
// *
// * @param resourcePath path to a file containing aspectj lint properties
// */
// public void setLintProperties(String resourcePath) throws IOException {
// URL url = this.classLoaderReference.getClassLoader().getResource(resourcePath);
// InputStream is = url.openStream();
// Properties p = new Properties();
// p.load(is);
// setLintProperties(p);
// }
/**
* Set the lint properties for this parser from the given properties set.
*
* @param properties
*/
public void setLintProperties(Properties properties) {
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);
if (world != null) {
world.registerPointcutHandler(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);
}
/**
* Parse the given pointcut expression. A global scope is assumed for resolving any type references, and the pointcut must
* contain no formals (variables to be bound).
*
* @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not
* supported by this PointcutParser.
* @throws IllegalArgumentException if the expression is not a well-formed pointcut expression
*/
public StandardPointcutExpression parsePointcutExpression(String expression) throws UnsupportedPointcutPrimitiveException,
IllegalArgumentException {
return parsePointcutExpression(expression, null, new PointcutParameter[0]);
}
/**
* Parse the given pointcut expression. The pointcut is resolved as if it had been declared inside the inScope class (this
* allows the pointcut to contain unqualified references to other pointcuts declared in the same type for example). The pointcut
* may contain zero or more formal parameters to be bound at matched join points.
*
* @throws UnsupportedPointcutPrimitiveException if the parser encounters a primitive pointcut expression of a kind not
* supported by this PointcutParser.
* @throws IllegalArgumentException if the expression is not a well-formed pointcut expression
*/
public StandardPointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters)
throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
StandardPointcutExpressionImpl pcExpr = null;
try {
Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
pc = concretizePointcutExpression(pc, inScope, formalParameters);
validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
pcExpr = new StandardPointcutExpressionImpl(pc, expression, formalParameters, getWorld());
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
} catch (ReflectionWorld.ReflectionWorldException rwEx) {
rwEx.printStackTrace();
throw new IllegalArgumentException(rwEx.getMessage());
}
return pcExpr;
}
protected Pointcut resolvePointcutExpression(String expression, Class> inScope, PointcutParameter[] formalParameters) {
try {
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);
return pc;
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
}
}
protected Pointcut concretizePointcutExpression(Pointcut pc, Class> inScope, PointcutParameter[] formalParameters) {
ResolvedType declaringTypeForResolution = null;
if (inScope != null) {
declaringTypeForResolution = getWorld().resolve(inScope.getName());
} else {
declaringTypeForResolution = ResolvedType.OBJECT.resolve(getWorld());
}
IntMap arity = new IntMap(formalParameters.length);
for (int i = 0; i < formalParameters.length; i++) {
arity.put(i, i);
}
return pc.concretize(declaringTypeForResolution, declaringTypeForResolution, arity);
}
/**
* Parse the given aspectj type pattern, and return a matcher that can be used to match types using it.
*
* @param typePattern an aspectj type pattern
* @return a type pattern matcher that matches using the given pattern
* @throws IllegalArgumentException if the type pattern cannot be successfully parsed.
*/
public TypePatternMatcher parseTypePattern(String typePattern) throws IllegalArgumentException {
try {
TypePattern tp = new PatternParser(typePattern).parseTypePattern();
tp.resolve(world);
return new TypePatternMatcherImpl(tp, world);
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(typePattern, pEx));
} catch (ReflectionWorld.ReflectionWorldException rwEx) {
throw new IllegalArgumentException(rwEx.getMessage());
}
}
private World getWorld() {
return world;
}
/* for testing */
Set getSupportedPrimitives() {
return supportedPrimitives;
}
/* for testing */
IMessageHandler setCustomMessageHandler(IMessageHandler aHandler) {
IMessageHandler current = getWorld().getMessageHandler();
getWorld().setMessageHandler(aHandler);
return current;
}
private IScope buildResolutionScope(Class> inScope, PointcutParameter[] formalParameters) {
if (formalParameters == null) {
formalParameters = new PointcutParameter[0];
}
FormalBinding[] formalBindings = new FormalBinding[formalParameters.length];
for (int i = 0; i < formalBindings.length; i++) {
formalBindings[i] = new FormalBinding(toUnresolvedType(formalParameters[i].getType()), formalParameters[i].getName(), i);
}
if (inScope == null) {
SimpleScope ss = new SimpleScope(getWorld(), formalBindings);
ss.setImportedPrefixes(new String[] { "java.lang.", "java.util." });
return ss;
} else {
ResolvedType inType = getWorld().resolve(inScope.getName());
ISourceContext sourceContext = new ISourceContext() {
public ISourceLocation makeSourceLocation(IHasPosition position) {
return new SourceLocation(new File(""), 0);
}
public ISourceLocation makeSourceLocation(int line, int offset) {
return new SourceLocation(new File(""), line);
}
public int getOffset() {
return 0;
}
public void tidy() {
}
};
BindingScope bScope = new BindingScope(inType, sourceContext, formalBindings);
bScope.setImportedPrefixes(new String[] { "java.lang.", "java.util." });
return bScope;
}
}
private UnresolvedType toUnresolvedType(Class> clazz) {
if (clazz.isArray()) {
return UnresolvedType.forSignature(clazz.getName().replace('.', '/'));
} else {
return UnresolvedType.forName(clazz.getName());
}
}
private void validateAgainstSupportedPrimitives(Pointcut pc, String expression) {
switch (pc.getPointcutKind()) {
case Pointcut.AND:
validateAgainstSupportedPrimitives(((AndPointcut) pc).getLeft(), expression);
validateAgainstSupportedPrimitives(((AndPointcut) pc).getRight(), expression);
break;
case Pointcut.ARGS:
if (!supportedPrimitives.contains(PointcutPrimitive.ARGS)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ARGS);
}
break;
case Pointcut.CFLOW:
CflowPointcut cfp = (CflowPointcut) pc;
if (cfp.isCflowBelow()) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW_BELOW);
} else {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CFLOW);
}
case Pointcut.HANDLER:
if (!supportedPrimitives.contains(PointcutPrimitive.HANDLER)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.HANDLER);
}
break;
case Pointcut.IF:
case Pointcut.IF_FALSE:
case Pointcut.IF_TRUE:
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.IF);
case Pointcut.KINDED:
validateKindedPointcut(((KindedPointcut) pc), expression);
break;
case Pointcut.NOT:
validateAgainstSupportedPrimitives(((NotPointcut) pc).getNegatedPointcut(), expression);
break;
case Pointcut.OR:
validateAgainstSupportedPrimitives(((OrPointcut) pc).getLeft(), expression);
validateAgainstSupportedPrimitives(((OrPointcut) pc).getRight(), expression);
break;
case Pointcut.THIS_OR_TARGET:
boolean isThis = ((ThisOrTargetPointcut) pc).isThis();
if (isThis && !supportedPrimitives.contains(PointcutPrimitive.THIS)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.THIS);
} else if (!supportedPrimitives.contains(PointcutPrimitive.TARGET)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.TARGET);
}
break;
case Pointcut.WITHIN:
if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN);
}
break;
case Pointcut.WITHINCODE:
if (!supportedPrimitives.contains(PointcutPrimitive.WITHIN_CODE)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.WITHIN_CODE);
}
break;
case Pointcut.ATTHIS_OR_TARGET:
isThis = ((ThisOrTargetAnnotationPointcut) pc).isThis();
if (isThis && !supportedPrimitives.contains(PointcutPrimitive.AT_THIS)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_THIS);
} else if (!supportedPrimitives.contains(PointcutPrimitive.AT_TARGET)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_TARGET);
}
break;
case Pointcut.ATARGS:
if (!supportedPrimitives.contains(PointcutPrimitive.AT_ARGS)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ARGS);
}
break;
case Pointcut.ANNOTATION:
if (!supportedPrimitives.contains(PointcutPrimitive.AT_ANNOTATION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_ANNOTATION);
}
break;
case Pointcut.ATWITHIN:
if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHIN)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHIN);
}
break;
case Pointcut.ATWITHINCODE:
if (!supportedPrimitives.contains(PointcutPrimitive.AT_WITHINCODE)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.AT_WITHINCODE);
}
break;
case Pointcut.REFERENCE:
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());
}
}
private void validateKindedPointcut(KindedPointcut pc, String expression) {
Shadow.Kind kind = pc.getKind();
if ((kind == Shadow.MethodCall) || (kind == Shadow.ConstructorCall)) {
if (!supportedPrimitives.contains(PointcutPrimitive.CALL)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.CALL);
}
} else if ((kind == Shadow.MethodExecution) || (kind == Shadow.ConstructorExecution)) {
if (!supportedPrimitives.contains(PointcutPrimitive.EXECUTION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.EXECUTION);
}
} else if (kind == Shadow.AdviceExecution) {
if (!supportedPrimitives.contains(PointcutPrimitive.ADVICE_EXECUTION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.ADVICE_EXECUTION);
}
} else if (kind == Shadow.FieldGet) {
if (!supportedPrimitives.contains(PointcutPrimitive.GET)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.GET);
}
} else if (kind == Shadow.FieldSet) {
if (!supportedPrimitives.contains(PointcutPrimitive.SET)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.SET);
}
} else if (kind == Shadow.Initialization) {
if (!supportedPrimitives.contains(PointcutPrimitive.INITIALIZATION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.INITIALIZATION);
}
} else if (kind == Shadow.PreInitialization) {
if (!supportedPrimitives.contains(PointcutPrimitive.PRE_INITIALIZATION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.PRE_INITIALIZATION);
}
} else if (kind == Shadow.StaticInitialization) {
if (!supportedPrimitives.contains(PointcutPrimitive.STATIC_INITIALIZATION)) {
throw new UnsupportedPointcutPrimitiveException(expression, PointcutPrimitive.STATIC_INITIALIZATION);
}
}
}
private String buildUserMessageFromParserException(String pc, ParserException ex) {
StringBuilder msg = new StringBuilder();
msg.append("Pointcut is not well-formed: expecting '");
msg.append(ex.getMessage());
msg.append("'");
IHasPosition location = ex.getLocation();
msg.append(" at character position ");
msg.append(location.getStart());
msg.append("\n");
msg.append(pc);
msg.append("\n");
for (int i = 0; i < location.getStart(); i++) {
msg.append(" ");
}
for (int j = location.getStart(); j <= location.getEnd(); j++) {
msg.append("^");
}
msg.append("\n");
return msg.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy