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 v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * 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 v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.aspectj.weaver.tools; import java.io.File; import java.util.HashSet; import java.util.Iterator; 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
/** * 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<PointcutPrimitive> supportedPrimitives; private final Set<PointcutDesignatorHandler> pointcutDesignators = new HashSet<PointcutDesignatorHandler>();
Returns:a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser constructor).
/** * @return a Set containing every PointcutPrimitive except if, cflow, and cflowbelow (useful for passing to PointcutParser * constructor). */
public static Set<PointcutPrimitive> getAllSupportedPointcutPrimitives() { Set<PointcutPrimitive> primitives = new HashSet<PointcutPrimitive>(); 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.

/** * Returns a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: * <ul> * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts * <li>The pointcut expression must be anonymous with no formals allowed. * </ul> * <p> * When resolving types in pointcut expressions, the context classloader is used to find types. * </p> */
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.

Params:
  • supportedPointcutKinds – a set of PointcutPrimitives this parser should support
Throws:
/** * 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: * <ul> * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts * <li>The pointcut expression must be anonymous with no formals allowed. * </ul> * <p> * When resolving types in pointcut expressions, the given classloader is used to find types. * </p> * * @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.
/** * Create a pointcut parser that can parse the full AspectJ pointcut language with the following exceptions: * <ul> * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts * <li>The pointcut expression must be anonymous with no formals allowed. * </ul> */
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.
Params:
  • supportedPointcutKinds – a set of PointcutPrimitives this parser should support
Throws:
/** * 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: * <ul> * <li>The <code>if, cflow, and cflowbelow</code> pointcut designators are not supported * <li>Pointcut expressions must be self-contained :- they cannot contain references to other named pointcuts * <li>The pointcut expression must be anonymous with no formals allowed. * </ul> * * @param supportedPointcutKinds a set of PointcutPrimitives this parser should support * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below */
private StandardPointcutParser(Set/* <PointcutPrimitives> */supportedPointcutKinds, World world) { supportedPrimitives = supportedPointcutKinds; for (Iterator iter = supportedPointcutKinds.iterator(); iter.hasNext();) { PointcutPrimitive element = (PointcutPrimitive) iter.next(); 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.
Params:
  • properties –
/** * 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.
Params:
  • designatorHandler –
/** * 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.
Params:
  • name –
  • type –
Returns:
/** * 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.
  • IllegalArgumentException – if the expression is not a well-formed pointcut expression
/** * 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.
  • IllegalArgumentException – if the expression is not a well-formed pointcut expression
/** * 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.
Params:
  • typePattern – an aspectj type pattern
Throws:
Returns:a type pattern matcher that matches using the given pattern
/** * 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) { StringBuffer msg = new StringBuffer(); 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(); } }