org.aspectj.weaver.tools.PointcutParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjweaver Show documentation
Show all versions of aspectjweaver Show documentation
The AspectJ weaver introduces advices to java classes
/*******************************************************************************
* Copyright (c) 2004, 2017 Contributors
* 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.io.IOException;
import java.io.InputStream;
import java.net.URL;
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.WeakClassLoaderReference;
import org.aspectj.weaver.World;
import org.aspectj.weaver.internal.tools.PointcutExpressionImpl;
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
*
* @author Adrian Colyer
* @author Andy Clement
*/
public class PointcutParser {
private ReflectionWorld world;
private WeakClassLoaderReference classLoaderReference;
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 PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution() {
PointcutParser p = new PointcutParser();
p.setClassLoader(Thread.currentThread().getContextClassLoader());
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 context 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 PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
Set supportedPointcutKinds) {
PointcutParser p = new PointcutParser(supportedPointcutKinds);
p.setClassLoader(Thread.currentThread().getContextClassLoader());
return p;
}
/**
* 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 given classloader is used to find types.
*
*/
public static PointcutParser getPointcutParserSupportingAllPrimitivesAndUsingSpecifiedClassloaderForResolution(
ClassLoader classLoader) {
PointcutParser p = new PointcutParser();
p.setClassLoader(classLoader);
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 PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
Set supportedPointcutKinds, ClassLoader classLoader) {
PointcutParser p = new PointcutParser(supportedPointcutKinds);
p.setClassLoader(classLoader);
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 PointcutParser() {
supportedPrimitives = getAllSupportedPointcutPrimitives();
setClassLoader(PointcutParser.class.getClassLoader());
}
/**
* 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 PointcutParser(Set supportedPointcutKinds) {
supportedPrimitives = supportedPointcutKinds;
for (PointcutPrimitive pointcutPrimitive : supportedPointcutKinds) {
if ((pointcutPrimitive == PointcutPrimitive.IF) || (pointcutPrimitive == PointcutPrimitive.CFLOW)
|| (pointcutPrimitive == PointcutPrimitive.CFLOW_BELOW)) {
throw new UnsupportedOperationException("Cannot handle if, cflow, and cflowbelow primitives");
}
}
setClassLoader(PointcutParser.class.getClassLoader());
}
protected void setWorld(ReflectionWorld aWorld) {
this.world = aWorld;
}
/**
* Set the classloader that this parser should use for type resolution.
*
* @param aLoader
*/
protected void setClassLoader(ClassLoader aLoader) {
this.classLoaderReference = new WeakClassLoaderReference(aLoader);
world = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference);
}
/**
* Set the classloader that this parser should use for type resolution.
*
* @param aLoader
* @param shareWorlds if true then two PointcutParsers operating using the same classloader will share a ReflectionWorld
*/
protected void setClassLoader(ClassLoader aLoader, boolean shareWorlds) {
this.classLoaderReference = new WeakClassLoaderReference(aLoader);
if (shareWorlds) {
world = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference);
} else {
world = new ReflectionWorld(classLoaderReference);
}
}
/**
* 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 PointcutExpression 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 PointcutExpression parsePointcutExpression(String expression, Class> inScope, PointcutParameter[] formalParameters)
throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
PointcutExpressionImpl 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 PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
} catch (ParserException pEx) {
throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
} catch (ReflectionWorld.ReflectionWorldException rwEx) {
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(); // more correctly: parsePointcut(true)
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) {
return new SimpleScope(getWorld(), formalBindings);
} 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() {
}
};
return new BindingScope(inType, sourceContext, formalBindings);
}
}
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();
}
}