/* *******************************************************************
* Copyright (c) 2002,2010
* 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:
* PARC initial implementation
* Adrian Colyer, IBM
* Andy Clement, IBM, SpringSource
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberKind;
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.tools.ContextBasedMatcher;
import org.aspectj.weaver.tools.PointcutDesignatorHandler;
/**
* @author PARC
* @author Adrian Colyer
* @author Andy Clement
*/
// XXX doesn't handle errors for extra tokens very well (sometimes ignores)
public class PatternParser {
private ITokenSource tokenSource;
private ISourceContext sourceContext;
/** 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<PointcutDesignatorHandler> pointcutDesignatorHandlers = Collections.emptySet();
private World world;
/**
* Constructor for PatternParser.
*/
public PatternParser(ITokenSource tokenSource) {
super();
this.tokenSource = tokenSource;
this.sourceContext = tokenSource.getSourceContext();
}
/** only used by weaver tools API */
public void setPointcutDesignatorHandlers(Set<PointcutDesignatorHandler> handlers, World world) {
this.pointcutDesignatorHandlers = handlers;
this.world = world;
}
public PerClause maybeParsePerClause() {
IToken tok = tokenSource.peek();
if (tok == IToken.EOF) {
return null;
}
if (tok.isIdentifier()) {
String name = tok.getString();
if (name.equals("issingleton")) {
return parsePerSingleton();
} else if (name.equals("perthis")) {
return parsePerObject(true);
} else if (name.equals("pertarget")) {
return parsePerObject(false);
} else if (name.equals("percflow")) {
return parsePerCflow(false);
} else if (name.equals("percflowbelow")) {
return parsePerCflow(true);
} else if (name.equals("pertypewithin")) { // PTWIMPL Parse the pertypewithin clause
return parsePerTypeWithin();
} else {
return null;
}
}
return null;
}
private PerClause parsePerCflow(boolean isBelow) {
parseIdentifier();
eat("(");
Pointcut entry = parsePointcut();
eat(")");
return new PerCflow(entry, isBelow);
}
private PerClause parsePerObject(boolean isThis) {
parseIdentifier();
eat("(");
Pointcut entry = parsePointcut();
eat(")");
return new PerObject(entry, isThis);
}
private PerClause parsePerTypeWithin() {
parseIdentifier();
eat("(");
TypePattern withinTypePattern = parseTypePattern();
eat(")");
return new PerTypeWithin(withinTypePattern);
}
private PerClause parsePerSingleton() {
parseIdentifier();
eat("(");
eat(")");
return new PerSingleton();
}
public Declare parseDeclare() {
int startPos = tokenSource.peek().getStart();
eatIdentifier("declare");
String kind = parseIdentifier();
Declare ret;
if (kind.equals("error")) {
eat(":");
ret = parseErrorOrWarning(true);
} else if (kind.equals("warning")) {
eat(":");
ret = parseErrorOrWarning(false);
} else if (kind.equals("precedence")) {
eat(":");
ret = parseDominates();
} else if (kind.equals("dominates")) {
throw new ParserException("name changed to declare precedence", tokenSource.peek(-2));
} else if (kind.equals("parents")) {
ret = parseParents();
} else if (kind.equals("soft")) {
eat(":");
ret = parseSoft();
} else {
throw new ParserException(
"expected one of error, warning, parents, soft, precedence, @type, @method, @constructor, @field",
tokenSource.peek(-1));
}
int endPos = tokenSource.peek(-1).getEnd();
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
public Declare parseDeclareAnnotation() {
int startPos = tokenSource.peek().getStart();
eatIdentifier("declare");
eat("@");
String kind = parseIdentifier();
eat(":");
Declare ret;
if (kind.equals("type")) {
ret = parseDeclareAtType();
} else if (kind.equals("method")) {
ret = parseDeclareAtMethod(true);
} else if (kind.equals("field")) {
ret = parseDeclareAtField();
} else if (kind.equals("constructor")) {
ret = parseDeclareAtMethod(false);
} else {
throw new ParserException("one of type, method, field, constructor", tokenSource.peek(-1));
}
eat(";");
int endPos = tokenSource.peek(-1).getEnd();
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
public DeclareAnnotation parseDeclareAtType() {
allowHasTypePatterns = true;
TypePattern p = parseTypePattern();
allowHasTypePatterns = false;
return new DeclareAnnotation(DeclareAnnotation.AT_TYPE, p);
}
public DeclareAnnotation parseDeclareAtMethod(boolean isMethod) {
ISignaturePattern sp = parseCompoundMethodOrConstructorSignaturePattern(isMethod);// parseMethodOrConstructorSignaturePattern();
if (!isMethod) {
return new DeclareAnnotation(DeclareAnnotation.AT_CONSTRUCTOR, sp);
} else {
return new DeclareAnnotation(DeclareAnnotation.AT_METHOD, sp);
}
}
public DeclareAnnotation parseDeclareAtField() {
ISignaturePattern compoundFieldSignaturePattern = parseCompoundFieldSignaturePattern();
DeclareAnnotation da = new DeclareAnnotation(DeclareAnnotation.AT_FIELD, compoundFieldSignaturePattern);
return da;
}
public ISignaturePattern parseCompoundFieldSignaturePattern() {
int index = tokenSource.getIndex();
try {
ISignaturePattern atomicFieldSignaturePattern = parseMaybeParenthesizedFieldSignaturePattern();
while (isEitherAndOrOr()) {
if (maybeEat("&&")) {
atomicFieldSignaturePattern = new AndSignaturePattern(atomicFieldSignaturePattern,
parseMaybeParenthesizedFieldSignaturePattern());
}
if (maybeEat("||")) {
atomicFieldSignaturePattern = new OrSignaturePattern(atomicFieldSignaturePattern,
parseMaybeParenthesizedFieldSignaturePattern());
}
}
return atomicFieldSignaturePattern;
} catch (ParserException e) {
// fallback in the case of a regular single field signature pattern that just happened to start with '('
int nowAt = tokenSource.getIndex();
tokenSource.setIndex(index);
try {
ISignaturePattern fsp = parseFieldSignaturePattern();
return fsp;
} catch (Exception e2) {
tokenSource.setIndex(nowAt);
// throw the original
throw e;
}
}
}
private boolean isEitherAndOrOr() {
String tokenstring = tokenSource.peek().getString();
return tokenstring.equals("&&") || tokenstring.equals("||");
}
public ISignaturePattern parseCompoundMethodOrConstructorSignaturePattern(boolean isMethod) {
ISignaturePattern atomicMethodCtorSignaturePattern = parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod);
while (isEitherAndOrOr()) {
if (maybeEat("&&")) {
atomicMethodCtorSignaturePattern = new AndSignaturePattern(atomicMethodCtorSignaturePattern,
parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod));
}
if (maybeEat("||")) {
atomicMethodCtorSignaturePattern = new OrSignaturePattern(atomicMethodCtorSignaturePattern,
parseMaybeParenthesizedMethodOrConstructorSignaturePattern(isMethod));
}
}
return atomicMethodCtorSignaturePattern;
}
public DeclarePrecedence parseDominates() {
List<TypePattern> l = new ArrayList<TypePattern>();
do {
l.add(parseTypePattern());
} while (maybeEat(","));
return new DeclarePrecedence(l);
}
private Declare parseParents() {
/*
* simplified design requires use of raw types for declare parents, no generic spec. allowed String[] typeParameters =
* maybeParseSimpleTypeVariableList();
*/
eat(":");
allowHasTypePatterns = true;
TypePattern p = parseTypePattern(false, false);
allowHasTypePatterns = false;
IToken t = tokenSource.next();
if (!(t.getString().equals("extends") || t.getString().equals("implements"))) {
throw new ParserException("extends or implements", t);
}
boolean isExtends = t.getString().equals("extends");
List<TypePattern> l = new ArrayList<TypePattern>();
do {
l.add(parseTypePattern());
} while (maybeEat(","));
// XXX somewhere in the chain we need to enforce that we have only ExactTypePatterns
DeclareParents decp = new DeclareParents(p, l, isExtends);
return decp;
}
private Declare parseSoft() {
TypePattern p = parseTypePattern();
eat(":");
Pointcut pointcut = parsePointcut();
return new DeclareSoft(p, pointcut);
}
/**
* Attempt to parse a pointcut, if that fails then try again for a type pattern.
*
* @param isError true if it is declare error rather than declare warning
* @return the new declare
*/
private Declare parseErrorOrWarning(boolean isError) {
Pointcut pointcut = null;
int index = tokenSource.getIndex();
try {
pointcut = parsePointcut();
} catch (ParserException pe) {
try {
tokenSource.setIndex(index);
boolean oldValue = allowHasTypePatterns;
TypePattern typePattern = null;
try {
allowHasTypePatterns = true;
typePattern = parseTypePattern();
} finally {
allowHasTypePatterns = oldValue;
}
eat(":");
String message = parsePossibleStringSequence(true);
return new DeclareTypeErrorOrWarning(isError, typePattern, message);
} catch (ParserException pe2) {
// deliberately throw the original problem
throw pe;
}
}
eat(":");
String message = parsePossibleStringSequence(true);
return new DeclareErrorOrWarning(isError, pointcut, message);
}
public Pointcut parsePointcut() {
Pointcut p = parseAtomicPointcut();
if (maybeEat("&&")) {
p = new AndPointcut(p, parseNotOrPointcut());
}
if (maybeEat("||")) {
p = new OrPointcut(p, parsePointcut());
}
return p;
}
private Pointcut parseNotOrPointcut() {
Pointcut p = parseAtomicPointcut();
if (maybeEat("&&")) {
p = new AndPointcut(p, parseNotOrPointcut());
}
return p;
}
private Pointcut parseAtomicPointcut() {
if (maybeEat("!")) {
int startPos = tokenSource.peek(-1).getStart();
Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
return p;
}
if (maybeEat("(")) {
Pointcut p = parsePointcut();
eat(")");
return p;
}
if (maybeEat("@")) {
int startPos = tokenSource.peek().getStart();
Pointcut p = parseAnnotationPointcut();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
int startPos = tokenSource.peek().getStart();
Pointcut p = parseSinglePointcut();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
return p;
}
public Pointcut parseSinglePointcut() {
int start = tokenSource.getIndex();
IToken t = tokenSource.peek();
Pointcut p = t.maybeGetParsedPointcut();
if (p != null) {
tokenSource.next();
return p;
}
String kind = parseIdentifier();
// IToken possibleTypeVariableToken = tokenSource.peek();
// String[] typeVariables = maybeParseSimpleTypeVariableList();
if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) {
p = parseKindedPointcut(kind);
} else if (kind.equals("args")) {
p = parseArgsPointcut();
} else if (kind.equals("this")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("target")) {
p = parseThisOrTargetPointcut(kind);
} else if (kind.equals("within")) {
p = parseWithinPointcut();
} else if (kind.equals("withincode")) {
p = parseWithinCodePointcut();
} else if (kind.equals("cflow")) {
p = parseCflowPointcut(false);
} else if (kind.equals("cflowbelow")) {
p = parseCflowPointcut(true);
} else if (kind.equals("adviceexecution")) {
eat("(");
eat(")");
p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("handler")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new HandlerPointcut(typePat);
} else if (kind.equals("lock") || kind.equals("unlock")) {
p = parseMonitorPointcut(kind);
} else if (kind.equals("initialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.Initialization, sig);
} else if (kind.equals("staticinitialization")) {
eat("(");
TypePattern typePat = parseTypePattern(false, false);
eat(")");
p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION,
ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY,
AnnotationTypePattern.ANY));
} else if (kind.equals("preinitialization")) {
eat("(");
SignaturePattern sig = parseConstructorSignaturePattern();
eat(")");
p = new KindedPointcut(Shadow.PreInitialization, sig);
} else if (kind.equals("if")) {
// - annotation style only allows if(), if(true) or if(false)
// - if() means the body of the annotated method represents the if expression
// - anything else is an error because code cannot be put into the if()
// - code style will already have been processed and the call to maybeGetParsedPointcut()
// at the top of this method will have succeeded.
eat("(");
if (maybeEatIdentifier("true")) {
eat(")");
p = new IfPointcut.IfTruePointcut();
} else if (maybeEatIdentifier("false")) {
eat(")");
p = new IfPointcut.IfFalsePointcut();
} else {
if (!maybeEat(")")) {
throw new ParserException(
"in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method",
t);
}
// TODO - Alex has some token stuff going on here to get a readable name in place of ""...
p = new IfPointcut("");
}
} else {
boolean matchedByExtensionDesignator = false;
// see if a registered handler wants to parse it, otherwise
// treat as a reference pointcut
for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
if (pcd.getDesignatorName().equals(kind)) {
p = parseDesignatorPointcut(pcd);
matchedByExtensionDesignator = true;
}
}
if (!matchedByExtensionDesignator) {
tokenSource.setIndex(start);
p = parseReferencePointcut();
}
}
return p;
}
private void assertNoTypeVariables(String[] tvs, String errorMessage, IToken token) {
if (tvs != null) {
throw new ParserException(errorMessage, token);
}
}
public Pointcut parseAnnotationPointcut() {
int start = tokenSource.getIndex();
IToken t = tokenSource.peek();
String kind = parseIdentifier();
IToken possibleTypeVariableToken = tokenSource.peek();
String[] typeVariables = maybeParseSimpleTypeVariableList();
if (typeVariables != null) {
String message = "(";
assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken);
}
tokenSource.setIndex(start);
if (kind.equals("annotation")) {
return parseAtAnnotationPointcut();
} else if (kind.equals("args")) {
return parseArgsAnnotationPointcut();
} else if (kind.equals("this") || kind.equals("target")) {
return parseThisOrTargetAnnotationPointcut();
} else if (kind.equals("within")) {
return parseWithinAnnotationPointcut();
} else if (kind.equals("withincode")) {
return parseWithinCodeAnnotationPointcut();
}
throw new ParserException("pointcut name", t);
}
private Pointcut parseAtAnnotationPointcut() {
parseIdentifier();
eat("(");
if (maybeEat(")")) {
throw new ParserException("@AnnotationName or parameter", tokenSource.peek());
}
ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern();
eat(")");
return new AnnotationPointcut(type);
}
private SignaturePattern parseConstructorSignaturePattern() {
SignaturePattern ret = parseMethodOrConstructorSignaturePattern();
if (ret.getKind() == Member.CONSTRUCTOR) {
return ret;
}
throw new ParserException("constructor pattern required, found method pattern", ret);
}
private Pointcut parseWithinCodePointcut() {
// parseIdentifier();
eat("(");
SignaturePattern sig = parseMethodOrConstructorSignaturePattern();
eat(")");
return new WithincodePointcut(sig);
}
private Pointcut parseCflowPointcut(boolean isBelow) {
// parseIdentifier();
eat("(");
Pointcut entry = parsePointcut();
eat(")");
return new CflowPointcut(entry, isBelow, null);
}
/**
* Method parseWithinPointcut.
*
* @return Pointcut
*/
private Pointcut parseWithinPointcut() {
// parseIdentifier();
eat("(");
TypePattern type = parseTypePattern();
eat(")");
return new WithinPointcut(type);
}
/**
* Method parseThisOrTargetPointcut.
*
* @return Pointcut
*/
private Pointcut parseThisOrTargetPointcut(String kind) {
eat("(");
TypePattern type = parseTypePattern();
eat(")");
return new ThisOrTargetPointcut(kind.equals("this"), type);
}
private Pointcut parseThisOrTargetAnnotationPointcut() {
String kind = parseIdentifier();
eat("(");
if (maybeEat(")")) {
throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek());
}
ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern();
eat(")");
return new ThisOrTargetAnnotationPointcut(kind.equals("this"), type);
}
private Pointcut parseWithinAnnotationPointcut() {
/* String kind = */parseIdentifier();
eat("(");
if (maybeEat(")")) {
throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek());
}
AnnotationTypePattern type = parseAnnotationNameOrVarTypePattern();
eat(")");
return new WithinAnnotationPointcut(type);
}
private Pointcut parseWithinCodeAnnotationPointcut() {
/* String kind = */parseIdentifier();
eat("(");
if (maybeEat(")")) {
throw new ParserException("expecting @AnnotationName or parameter, but found ')'", tokenSource.peek());
}
ExactAnnotationTypePattern type = parseAnnotationNameOrVarTypePattern();
eat(")");
return new WithinCodeAnnotationPointcut(type);
}
/**
* Method parseArgsPointcut.
*
* @return Pointcut
*/
private Pointcut parseArgsPointcut() {
// parseIdentifier();
TypePatternList arguments = parseArgumentsPattern(false);
return new ArgsPointcut(arguments);
}
private Pointcut parseArgsAnnotationPointcut() {
parseIdentifier();
AnnotationPatternList arguments = parseArgumentsAnnotationPattern();
return new ArgsAnnotationPointcut(arguments);
}
private Pointcut parseReferencePointcut() {
TypePattern onType = parseTypePattern();
NamePattern name = null;
if (onType.typeParameters.size() > 0) {
eat(".");
name = parseNamePattern();
} else {
name = tryToExtractName(onType);
}
if (name == null) {
throw new ParserException("name pattern", tokenSource.peek());
}
if (onType.toString().equals("")) {
onType = null;
}
String simpleName = name.maybeGetSimpleName();
if (simpleName == null) {
throw new ParserException("(", tokenSource.peek(-1));
}
TypePatternList arguments = parseArgumentsPattern(false);
return new ReferencePointcut(onType, simpleName, 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<String> parseDottedIdentifier() {
List<String> ret = new ArrayList<String>();
ret.add(parseIdentifier());
while (maybeEat(".")) {
ret.add(parseIdentifier());
}
return ret;
}
private KindedPointcut parseKindedPointcut(String kind) {
eat("(");
SignaturePattern sig;
Shadow.Kind shadowKind = null;
if (kind.equals("execution")) {
sig = parseMethodOrConstructorSignaturePattern();
if (sig.getKind() == Member.METHOD) {
shadowKind = Shadow.MethodExecution;
} else if (sig.getKind() == Member.CONSTRUCTOR) {
shadowKind = Shadow.ConstructorExecution;
}
} else if (kind.equals("call")) {
sig = parseMethodOrConstructorSignaturePattern();
if (sig.getKind() == Member.METHOD) {
shadowKind = Shadow.MethodCall;
} else if (sig.getKind() == Member.CONSTRUCTOR) {
shadowKind = Shadow.ConstructorCall;
}
} else if (kind.equals("get")) {
sig = parseFieldSignaturePattern();
shadowKind = Shadow.FieldGet;
} else if (kind.equals("set")) {
sig = parseFieldSignaturePattern();
shadowKind = Shadow.FieldSet;
} else {
throw new ParserException("bad kind: " + kind, tokenSource.peek());
}
eat(")");
return new KindedPointcut(shadowKind, sig);
}
/** Covers the 'lock()' and 'unlock()' pointcuts */
private KindedPointcut parseMonitorPointcut(String kind) {
eat("(");
// TypePattern type = TypePattern.ANY;
eat(")");
if (kind.equals("lock")) {
return new KindedPointcut(Shadow.SynchronizationLock, new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY,
// type,
NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY));
} else {
return new KindedPointcut(Shadow.SynchronizationUnlock, new SignaturePattern(Member.MONITORENTER, ModifiersPattern.ANY,
TypePattern.ANY, TypePattern.ANY,
// type,
NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY, AnnotationTypePattern.ANY));
}
}
public TypePattern parseTypePattern() {
return parseTypePattern(false, false);
}
public TypePattern parseTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) {
TypePattern p = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible);
if (maybeEat("&&")) {
p = new AndTypePattern(p, parseNotOrTypePattern(insideTypeParameters, parameterAnnotationsPossible));
}
if (maybeEat("||")) {
p = new OrTypePattern(p, parseTypePattern(insideTypeParameters, parameterAnnotationsPossible));
}
return p;
}
private TypePattern parseNotOrTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) {
TypePattern p = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible);
if (maybeEat("&&")) {
p = new AndTypePattern(p, parseTypePattern(insideTypeParameters, parameterAnnotationsPossible));
}
return p;
}
// Need to differentiate in here between two kinds of annotation pattern - depending on where the ( is
private TypePattern parseAtomicTypePattern(boolean insideTypeParameters, boolean parameterAnnotationsPossible) {
AnnotationTypePattern ap = maybeParseAnnotationPattern(); // might be parameter annotation pattern or type annotation
// pattern
if (maybeEat("!")) {
// int startPos = tokenSource.peek(-1).getStart();
// ??? we lose source location for true start of !type
// An annotation, if processed, is outside of the Not - so here we have to build
// an And pattern containing the annotation and the not as left and right children
// *unless* the annotation pattern was just 'Any' then we can skip building the
// And and just return the Not directly (pr228980)
TypePattern p = null;
TypePattern tp = parseAtomicTypePattern(insideTypeParameters, parameterAnnotationsPossible);
if (!(ap instanceof AnyAnnotationTypePattern)) {
p = new NotTypePattern(tp);
p = new AndTypePattern(setAnnotationPatternForTypePattern(TypePattern.ANY, ap, false), p);
} else {
p = new NotTypePattern(tp);
}
return p;
}
if (maybeEat("(")) {
int openParenPos = tokenSource.peek(-1).getStart();
TypePattern p = parseTypePattern(insideTypeParameters, false);
if ((p instanceof NotTypePattern) && !(ap instanceof AnyAnnotationTypePattern)) {
// dont set the annotation on it, we don't want the annotation to be
// considered as part of the not, it is outside the not (pr228980)
TypePattern tp = setAnnotationPatternForTypePattern(TypePattern.ANY, ap, parameterAnnotationsPossible);
p = new AndTypePattern(tp, p);
} else {
p = setAnnotationPatternForTypePattern(p, ap, parameterAnnotationsPossible);
}
eat(")");
int closeParenPos = tokenSource.peek(-1).getStart();
boolean isVarArgs = maybeEat("...");
if (isVarArgs) {
p.setIsVarArgs(isVarArgs);
}
boolean isIncludeSubtypes = maybeEat("+");
if (isIncludeSubtypes) {
p.includeSubtypes = true; // need the test because (A+) should not set subtypes to false!
}
p.start = openParenPos;
p.end = closeParenPos;
return p;
}
int startPos = tokenSource.peek().getStart();
if (ap.start != -1) {
startPos = ap.start;
}
TypePattern p = parseSingleTypePattern(insideTypeParameters);
int endPos = tokenSource.peek(-1).getEnd();
p = setAnnotationPatternForTypePattern(p, ap, false);
p.setLocation(sourceContext, startPos, endPos);
return p;
}
private TypePattern setAnnotationPatternForTypePattern(TypePattern t, AnnotationTypePattern ap,
boolean parameterAnnotationsPattern) {
TypePattern ret = t;
if (parameterAnnotationsPattern) {
ap.setForParameterAnnotationMatch();
}
if (ap != AnnotationTypePattern.ANY) {
if (t == TypePattern.ANY) {
if (t.annotationPattern == AnnotationTypePattern.ANY) {
return new AnyWithAnnotationTypePattern(ap);
} else {
return new AnyWithAnnotationTypePattern(new AndAnnotationTypePattern(ap, t.annotationPattern));
}
// ret = new WildTypePattern(new NamePattern[] { NamePattern.ANY }, false, 0, false, null);
}
if (t.annotationPattern == AnnotationTypePattern.ANY) {
ret.setAnnotationTypePattern(ap);
} else {
ret.setAnnotationTypePattern(new AndAnnotationTypePattern(ap, t.annotationPattern)); // ???
}
}
return ret;
}
public AnnotationTypePattern maybeParseAnnotationPattern() {
AnnotationTypePattern ret = AnnotationTypePattern.ANY;
AnnotationTypePattern nextPattern = null;
while ((nextPattern = maybeParseSingleAnnotationPattern()) != null) {
if (ret == AnnotationTypePattern.ANY) {
ret = nextPattern;
} else {
ret = new AndAnnotationTypePattern(ret, nextPattern);
}
}
return ret;
}
// PVAL cope with annotation values at other places in this code
public AnnotationTypePattern maybeParseSingleAnnotationPattern() {
AnnotationTypePattern ret = null;
Map<String, String> values = null;
// LALR(2) - fix by making "!@" a single token
int startIndex = tokenSource.getIndex();
if (maybeEat("!")) {
if (maybeEat("@")) {
if (maybeEat("(")) {
TypePattern p = parseTypePattern();
ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p));
eat(")");
return ret;
} else {
TypePattern p = parseSingleTypePattern();
if (maybeEatAdjacent("(")) {
values = parseAnnotationValues();
eat(")");
ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p, values));
} else {
ret = new NotAnnotationTypePattern(new WildAnnotationTypePattern(p));
}
return ret;
}
} else {
tokenSource.setIndex(startIndex); // not for us!
return ret;
}
}
if (maybeEat("@")) {
if (maybeEat("(")) {
TypePattern p = parseTypePattern();
ret = new WildAnnotationTypePattern(p);
eat(")");
return ret;
} else {
int atPos = tokenSource.peek(-1).getStart();
TypePattern p = parseSingleTypePattern();
if (maybeEatAdjacent("(")) {
values = parseAnnotationValues();
eat(")");
ret = new WildAnnotationTypePattern(p, values);
} else {
ret = new WildAnnotationTypePattern(p);
}
ret.start = atPos;
return ret;
}
} else {
tokenSource.setIndex(startIndex); // not for us!
return ret;
}
}
// Parse annotation values. In an expression in @A(a=b,c=d) this method will be
// parsing the a=b,c=d.)
public Map<String, String> parseAnnotationValues() {
Map<String, String> values = new HashMap<String, String>();
boolean seenDefaultValue = false;
do {
String possibleKeyString = parseAnnotationNameValuePattern();
if (possibleKeyString == null) {
throw new ParserException("expecting simple literal ", tokenSource.peek(-1));
}
// did they specify just a single entry 'v' or a keyvalue pair 'k=v'
if (maybeEat("=")) {
// it was a key!
String valueString = parseAnnotationNameValuePattern();
if (valueString == null) {
throw new ParserException("expecting simple literal ", tokenSource.peek(-1));
}
values.put(possibleKeyString, valueString);
} else if (maybeEat("!=")) {
// it was a key, with a !=
String valueString = parseAnnotationNameValuePattern();
if (valueString == null) {
throw new ParserException("expecting simple literal ", tokenSource.peek(-1));
}
// negation is captured by adding a trailing ! to the key name
values.put(possibleKeyString + "!", valueString);
} else {
if (seenDefaultValue) {
throw new ParserException("cannot specify two default values", tokenSource.peek(-1));
}
seenDefaultValue = true;
values.put("value", possibleKeyString);
}
} while (maybeEat(",")); // keep going whilst there are ','
return values;
}
public TypePattern parseSingleTypePattern() {
return parseSingleTypePattern(false);
}
public TypePattern parseSingleTypePattern(boolean insideTypeParameters) {
if (insideTypeParameters && maybeEat("?")) {
return parseGenericsWildcardTypePattern();
}
if (allowHasTypePatterns) {
if (maybeEatIdentifier("hasmethod")) {
return parseHasMethodTypePattern();
}
if (maybeEatIdentifier("hasfield")) {
return parseHasFieldTypePattern();
}
}
// // Check for a type category
// IToken token = tokenSource.peek();
// if (token.isIdentifier()) {
// String category = token.getString();
// TypeCategoryTypePattern typeIsPattern = null;
// if (category.equals("isClass")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS);
// } else if (category.equals("isAspect")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT);
// } else if (category.equals("isInterface")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE);
// } else if (category.equals("isInner")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER);
// } else if (category.equals("isAnonymous")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS);
// } else if (category.equals("isEnum")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM);
// } else if (category.equals("isAnnotation")) {
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION);
// }
// if (typeIsPattern != null) {
// tokenSource.next();
// typeIsPattern.setLocation(tokenSource.getSourceContext(), token.getStart(), token.getEnd());
// return typeIsPattern;
// }
// }
if (maybeEatIdentifier("is")) {
int pos = tokenSource.getIndex() - 1;
TypePattern typeIsPattern = parseIsTypePattern();
if (typeIsPattern != null) {
return typeIsPattern;
}
// rewind as if we never tried to parse it as a typeIs
tokenSource.setIndex(pos);
}
List<NamePattern> names = parseDottedNamePattern();
int dim = 0;
while (maybeEat("[")) {
eat("]");
dim++;
}
TypePatternList typeParameters = maybeParseTypeParameterList();
int endPos = tokenSource.peek(-1).getEnd();
boolean includeSubtypes = maybeEat("+");
// TODO do we need to associate the + with either the type or the array?
while (maybeEat("[")) {
eat("]");
dim++;
}
boolean isVarArgs = maybeEat("...");
// ??? what about the source location of any's????
if (names.size() == 1 && names.get(0).isAny() && dim == 0 && !isVarArgs && typeParameters == null) {
return TypePattern.ANY;
}
// Notice we increase the dimensions if varargs is set. this is to allow type matching to
// succeed later: The actual signature at runtime of a method declared varargs is an array type of
// the original declared type (so Integer... becomes Integer[] in the bytecode). So, here for the
// pattern 'Integer...' we create a WildTypePattern 'Integer[]' with varargs set. If this matches
// during shadow matching, we confirm that the varargs flags match up before calling it a successful
// match.
return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs, typeParameters);
}
public TypePattern parseHasMethodTypePattern() {
int startPos = tokenSource.peek(-1).getStart();
eat("(");
SignaturePattern sp = parseMethodOrConstructorSignaturePattern();
eat(")");
int endPos = tokenSource.peek(-1).getEnd();
HasMemberTypePattern ret = new HasMemberTypePattern(sp);
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
/**
* Attempt to parse a typeIs(<category>) construct. If it cannot be parsed we just return null and that should cause the caller
* to reset their position and attempt to consume it in another way. This means we won't have problems here: execution(*
* typeIs(..)) because someone has decided to call a method the same as our construct.
*
* @return a TypeIsTypePattern or null if could not be parsed
*/
public TypePattern parseIsTypePattern() {
int startPos = tokenSource.peek(-1).getStart(); // that will be the start of the 'typeIs'
if (!maybeEatAdjacent("(")) {
return null;
}
IToken token = tokenSource.next();
TypeCategoryTypePattern typeIsPattern = null;
if (token.isIdentifier()) {
String category = token.getString();
if (category.equals("ClassType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS);
} else if (category.equals("AspectType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT);
} else if (category.equals("InterfaceType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE);
} else if (category.equals("InnerType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER);
} else if (category.equals("AnonymousType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS);
} else if (category.equals("EnumType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM);
} else if (category.equals("AnnotationType")) {
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION);
}
}
if (typeIsPattern == null) {
return null;
}
if (!maybeEat(")")) {
throw new ParserException(")", tokenSource.peek());
}
int endPos = tokenSource.peek(-1).getEnd();
typeIsPattern.setLocation(tokenSource.getSourceContext(), startPos, endPos);
return typeIsPattern;
}
// if (names.size() == 1 && !names.get(0).isAny()) {
// if (maybeEatAdjacent("(")) {
// if (maybeEat(")")) {
// // likely to be one of isClass()/isInterface()/isInner()/isAnonymous()/isAspect()
// if (names.size() == 1) {
// NamePattern np = names.get(0);
// String simpleName = np.maybeGetSimpleName();
// if (simpleName != null) {
// return new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION, np);
// } else {
// throw new ParserException(
// "not a supported type category, supported are isClass/isInterface/isEnum/isAnnotation/isInner/isAnonymous",
// tokenSource.peek(-3));
// }
// }
// int stop = 1;
// // return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs,
// // typeParameters);
// }
// } else {
// throw new ParserException("category type pattern is missing closing parentheses", tokenSource.peek(-2));
// }
// }
// }
public TypePattern parseHasFieldTypePattern() {
int startPos = tokenSource.peek(-1).getStart();
eat("(");
SignaturePattern sp = parseFieldSignaturePattern();
eat(")");
int endPos = tokenSource.peek(-1).getEnd();
HasMemberTypePattern ret = new HasMemberTypePattern(sp);
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
public TypePattern parseGenericsWildcardTypePattern() {
List<NamePattern> names = new ArrayList<NamePattern>();
names.add(new NamePattern("?"));
TypePattern upperBound = null;
TypePattern[] additionalInterfaceBounds = new TypePattern[0];
TypePattern lowerBound = null;
if (maybeEatIdentifier("extends")) {
upperBound = parseTypePattern(false, false);
additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds();
}
if (maybeEatIdentifier("super")) {
lowerBound = parseTypePattern(false, false);
}
int endPos = tokenSource.peek(-1).getEnd();
return new WildTypePattern(names, false, 0, endPos, false, null, upperBound, additionalInterfaceBounds, lowerBound);
}
// private AnnotationTypePattern completeAnnotationPattern(AnnotationTypePattern p) {
// if (maybeEat("&&")) {
// return new AndAnnotationTypePattern(p,parseNotOrAnnotationPattern());
// }
// if (maybeEat("||")) {
// return new OrAnnotationTypePattern(p,parseAnnotationTypePattern());
// }
// return p;
// }
//
// protected AnnotationTypePattern parseAnnotationTypePattern() {
// AnnotationTypePattern ap = parseAtomicAnnotationPattern();
// if (maybeEat("&&")) {
// ap = new AndAnnotationTypePattern(ap, parseNotOrAnnotationPattern());
// }
//
// if (maybeEat("||")) {
// ap = new OrAnnotationTypePattern(ap, parseAnnotationTypePattern());
// }
// return ap;
// }
//
// private AnnotationTypePattern parseNotOrAnnotationPattern() {
// AnnotationTypePattern p = parseAtomicAnnotationPattern();
// if (maybeEat("&&")) {
// p = new AndAnnotationTypePattern(p,parseAnnotationTypePattern());
// }
// return p;
// }
protected ExactAnnotationTypePattern parseAnnotationNameOrVarTypePattern() {
ExactAnnotationTypePattern p = null;
int startPos = tokenSource.peek().getStart();
if (maybeEat("@")) {
throw new ParserException("@Foo form was deprecated in AspectJ 5 M2: annotation name or var ", tokenSource.peek(-1));
}
p = parseSimpleAnnotationName();
int endPos = tokenSource.peek(-1).getEnd();
p.setLocation(sourceContext, startPos, endPos);
// For optimized syntax that allows binding directly to annotation values (pr234943)
if (maybeEat("(")) {
String formalName = parseIdentifier();
p = new ExactAnnotationFieldTypePattern(p, formalName);
eat(")");
}
return p;
}
/**
* @return
*/
private ExactAnnotationTypePattern parseSimpleAnnotationName() {
// the @ has already been eaten...
ExactAnnotationTypePattern p;
StringBuffer annotationName = new StringBuffer();
annotationName.append(parseIdentifier());
while (maybeEat(".")) {
annotationName.append('.');
annotationName.append(parseIdentifier());
}
UnresolvedType type = UnresolvedType.forName(annotationName.toString());
p = new ExactAnnotationTypePattern(type, null);
return p;
}
// private AnnotationTypePattern parseAtomicAnnotationPattern() {
// if (maybeEat("!")) {
// //int startPos = tokenSource.peek(-1).getStart();
// //??? we lose source location for true start of !type
// AnnotationTypePattern p = new NotAnnotationTypePattern(parseAtomicAnnotationPattern());
// return p;
// }
// if (maybeEat("(")) {
// AnnotationTypePattern p = parseAnnotationTypePattern();
// eat(")");
// return p;
// }
// int startPos = tokenSource.peek().getStart();
// eat("@");
// StringBuffer annotationName = new StringBuffer();
// annotationName.append(parseIdentifier());
// while (maybeEat(".")) {
// annotationName.append('.');
// annotationName.append(parseIdentifier());
// }
// UnresolvedType type = UnresolvedType.forName(annotationName.toString());
// AnnotationTypePattern p = new ExactAnnotationTypePattern(type);
// int endPos = tokenSource.peek(-1).getEnd();
// p.setLocation(sourceContext, startPos, endPos);
// return p;
// }
public List<NamePattern> parseDottedNamePattern() {
List<NamePattern> names = new ArrayList<NamePattern>();
StringBuffer buf = new StringBuffer();
IToken previous = null;
boolean justProcessedEllipsis = false; // Remember if we just dealt with an ellipsis (PR61536)
boolean justProcessedDot = false;
boolean onADot = false;
while (true) {
IToken tok = null;
int startPos = tokenSource.peek().getStart();
String afterDot = null;
while (true) {
if (previous != null && previous.getString().equals(".")) {
justProcessedDot = true;
}
tok = tokenSource.peek();
onADot = (tok.getString().equals("."));
if (previous != null) {
if (!isAdjacent(previous, tok)) {
break;
}
}
if (tok.getString() == "*" || (tok.isIdentifier() && tok.getString() != "...")) {
buf.append(tok.getString());
} else if (tok.getString() == "...") {
break;
} else if (tok.getLiteralKind() != null) {
// System.err.println("literal kind: " + tok.getString());
String s = tok.getString();
int dot = s.indexOf('.');
if (dot != -1) {
buf.append(s.substring(0, dot));
afterDot = s.substring(dot + 1);
previous = tokenSource.next();
break;
}
buf.append(s); // ??? so-so
} else {
break;
}
previous = tokenSource.next();
// XXX need to handle floats and other fun stuff
}
int endPos = tokenSource.peek(-1).getEnd();
if (buf.length() == 0 && names.isEmpty()) {
throw new ParserException("name pattern", tok);
}
if (buf.length() == 0 && justProcessedEllipsis) {
throw new ParserException("name pattern cannot finish with ..", tok);
}
if (buf.length() == 0 && justProcessedDot && !onADot) {
throw new ParserException("name pattern cannot finish with .", tok);
}
if (buf.length() == 0) {
names.add(NamePattern.ELLIPSIS);
justProcessedEllipsis = true;
} else {
checkLegalName(buf.toString(), previous);
NamePattern ret = new NamePattern(buf.toString());
ret.setLocation(sourceContext, startPos, endPos);
names.add(ret);
justProcessedEllipsis = false;
}
if (afterDot == null) {
buf.setLength(0);
// no elipsis or dotted name part
if (!maybeEat(".")) {
break;
// go on
} else {
previous = tokenSource.peek(-1);
}
} else {
buf.setLength(0);
buf.append(afterDot);
afterDot = null;
}
}
// System.err.println("parsed: " + names);
return names;
}
// supported form 'a.b.c.d' or just 'a'
public String parseAnnotationNameValuePattern() {
StringBuffer buf = new StringBuffer();
IToken tok;
// int startPos =
tokenSource.peek().getStart();
boolean dotOK = false;
int depth = 0;
while (true) {
tok = tokenSource.peek();
// keep going until we hit ')' or '=' or ','
if (tok.getString() == ")" && depth == 0) {
break;
}
if (tok.getString() == "!=" && depth == 0) {
break;
}
if (tok.getString() == "=" && depth == 0) {
break;
}
if (tok.getString() == "," && depth == 0) {
break;
}
if (tok == IToken.EOF) {
throw new ParserException("eof", tokenSource.peek());
}
// keep track of nested brackets
if (tok.getString() == "(") {
depth++;
}
if (tok.getString() == ")") {
depth--;
}
if (tok.getString() == "{") {
depth++;
}
if (tok.getString() == "}") {
depth--;
}
if (tok.getString() == "." && !dotOK) {
throw new ParserException("dot not expected", tok);
}
buf.append(tok.getString());
tokenSource.next();
dotOK = true;
}
return buf.toString();
}
public NamePattern parseNamePattern() {
StringBuffer buf = new StringBuffer();
IToken previous = null;
IToken tok;
int startPos = tokenSource.peek().getStart();
while (true) {
tok = tokenSource.peek();
if (previous != null) {
if (!isAdjacent(previous, tok)) {
break;
}
}
if (tok.getString() == "*" || tok.isIdentifier()) {
buf.append(tok.getString());
} else if (tok.getLiteralKind() != null) {
// System.err.println("literal kind: " + tok.getString());
String s = tok.getString();
if (s.indexOf('.') != -1) {
break;
}
buf.append(s); // ??? so-so
} else {
break;
}
previous = tokenSource.next();
// XXX need to handle floats and other fun stuff
}
int endPos = tokenSource.peek(-1).getEnd();
if (buf.length() == 0) {
throw new ParserException("name pattern", tok);
}
checkLegalName(buf.toString(), previous);
NamePattern ret = new NamePattern(buf.toString());
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
private void checkLegalName(String s, IToken tok) {
char ch = s.charAt(0);
if (!(ch == '*' || Character.isJavaIdentifierStart(ch))) {
throw new ParserException("illegal identifier start (" + ch + ")", tok);
}
for (int i = 1, len = s.length(); i < len; i++) {
ch = s.charAt(i);
if (!(ch == '*' || Character.isJavaIdentifierPart(ch))) {
throw new ParserException("illegal identifier character (" + ch + ")", tok);
}
}
}
private boolean isAdjacent(IToken first, IToken second) {
return first.getEnd() == second.getStart() - 1;
}
public ModifiersPattern parseModifiersPattern() {
int requiredFlags = 0;
int forbiddenFlags = 0;
int start;
while (true) {
start = tokenSource.getIndex();
boolean isForbidden = false;
isForbidden = maybeEat("!");
IToken t = tokenSource.next();
int flag = ModifiersPattern.getModifierFlag(t.getString());
if (flag == -1) {
break;
}
if (isForbidden) {
forbiddenFlags |= flag;
} else {
requiredFlags |= flag;
}
}
tokenSource.setIndex(start);
if (requiredFlags == 0 && forbiddenFlags == 0) {
return ModifiersPattern.ANY;
} else {
return new ModifiersPattern(requiredFlags, forbiddenFlags);
}
}
public TypePatternList parseArgumentsPattern(boolean parameterAnnotationsPossible) {
List<TypePattern> patterns = new ArrayList<TypePattern>();
eat("(");
// ()
if (maybeEat(")")) {
return new TypePatternList();
}
do {
if (maybeEat(".")) { // ..
eat(".");
patterns.add(TypePattern.ELLIPSIS);
} else {
patterns.add(parseTypePattern(false, parameterAnnotationsPossible));
}
} while (maybeEat(","));
eat(")");
return new TypePatternList(patterns);
}
public AnnotationPatternList parseArgumentsAnnotationPattern() {
List<AnnotationTypePattern> patterns = new ArrayList<AnnotationTypePattern>();
eat("(");
if (maybeEat(")")) {
return new AnnotationPatternList();
}
do {
if (maybeEat(".")) {
eat(".");
patterns.add(AnnotationTypePattern.ELLIPSIS);
} else if (maybeEat("*")) {
patterns.add(AnnotationTypePattern.ANY);
} else {
patterns.add(parseAnnotationNameOrVarTypePattern());
}
} while (maybeEat(","));
eat(")");
return new AnnotationPatternList(patterns);
}
public ThrowsPattern parseOptionalThrowsPattern() {
IToken t = tokenSource.peek();
if (t.isIdentifier() && t.getString().equals("throws")) {
tokenSource.next();
List<TypePattern> required = new ArrayList<TypePattern>();
List<TypePattern> forbidden = new ArrayList<TypePattern>();
do {
boolean isForbidden = maybeEat("!");
// ???might want an error for a second ! without a paren
TypePattern p = parseTypePattern();
if (isForbidden) {
forbidden.add(p);
} else {
required.add(p);
}
} while (maybeEat(","));
return new ThrowsPattern(new TypePatternList(required), new TypePatternList(forbidden));
}
return ThrowsPattern.ANY;
}
public SignaturePattern parseMethodOrConstructorSignaturePattern() {
int startPos = tokenSource.peek().getStart();
AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern();
ModifiersPattern modifiers = parseModifiersPattern();
TypePattern returnType = parseTypePattern(false, false);
TypePattern declaringType;
NamePattern name = null;
MemberKind kind;
// here we can check for 'new'
if (maybeEatNew(returnType)) {
kind = Member.CONSTRUCTOR;
if (returnType.toString().length() == 0) {
declaringType = TypePattern.ANY;
} else {
declaringType = returnType;
}
returnType = TypePattern.ANY;
name = NamePattern.ANY;
} else {
kind = Member.METHOD;
IToken nameToken = tokenSource.peek();
declaringType = parseTypePattern(false, false);
if (maybeEat(".")) {
nameToken = tokenSource.peek();
name = parseNamePattern();
} else {
name = tryToExtractName(declaringType);
if (declaringType.toString().equals("")) {
declaringType = TypePattern.ANY;
}
}
if (name == null) {
throw new ParserException("name pattern", tokenSource.peek());
}
String simpleName = name.maybeGetSimpleName();
// XXX should add check for any Java keywords
if (simpleName != null && simpleName.equals("new")) {
throw new ParserException("method name (not constructor)", nameToken);
}
}
TypePatternList parameterTypes = parseArgumentsPattern(true);
ThrowsPattern throwsPattern = parseOptionalThrowsPattern();
SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name, parameterTypes,
throwsPattern, annotationPattern);
int endPos = tokenSource.peek(-1).getEnd();
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
private boolean maybeEatNew(TypePattern returnType) {
if (returnType instanceof WildTypePattern) {
WildTypePattern p = (WildTypePattern) returnType;
if (p.maybeExtractName("new")) {
return true;
}
}
int start = tokenSource.getIndex();
if (maybeEat(".")) {
String id = maybeEatIdentifier();
if (id != null && id.equals("new")) {
return true;
}
tokenSource.setIndex(start);
}
return false;
}
public ISignaturePattern parseMaybeParenthesizedFieldSignaturePattern() {
boolean negated = tokenSource.peek().getString().equals("!") && tokenSource.peek(1).getString().equals("(");
if (negated) {
eat("!");
}
ISignaturePattern result = null;
if (maybeEat("(")) {
result = parseCompoundFieldSignaturePattern();
eat(")", "missing ')' - unbalanced parentheses around field signature pattern in declare @field");
if (negated) {
result = new NotSignaturePattern(result);
}
} else {
result = parseFieldSignaturePattern();
}
return result;
}
public ISignaturePattern parseMaybeParenthesizedMethodOrConstructorSignaturePattern(boolean isMethod) {
boolean negated = tokenSource.peek().getString().equals("!") && tokenSource.peek(1).getString().equals("(");
if (negated) {
eat("!");
}
ISignaturePattern result = null;
if (maybeEat("(")) {
result = parseCompoundMethodOrConstructorSignaturePattern(isMethod);
eat(")", "missing ')' - unbalanced parentheses around method/ctor signature pattern in declare annotation");
if (negated) {
result = new NotSignaturePattern(result);
}
} else {
SignaturePattern sp = parseMethodOrConstructorSignaturePattern();
boolean isConstructorPattern = (sp.getKind() == Member.CONSTRUCTOR);
if (isMethod && isConstructorPattern) {
throw new ParserException("method signature pattern", tokenSource.peek(-1));
}
if (!isMethod && !isConstructorPattern) {
throw new ParserException("constructor signature pattern", tokenSource.peek(-1));
}
result = sp;
}
return result;
}
public SignaturePattern parseFieldSignaturePattern() {
int startPos = tokenSource.peek().getStart();
// TypePatternList followMe = TypePatternList.ANY;
AnnotationTypePattern annotationPattern = maybeParseAnnotationPattern();
ModifiersPattern modifiers = parseModifiersPattern();
TypePattern returnType = parseTypePattern();
TypePattern declaringType = parseTypePattern();
NamePattern name;
// System.err.println("parsed field: " + declaringType.toString());
if (maybeEat(".")) {
name = parseNamePattern();
} else {
name = tryToExtractName(declaringType);
if (name == null) {
throw new ParserException("name pattern", tokenSource.peek());
}
if (declaringType.toString().equals("")) {
declaringType = TypePattern.ANY;
}
}
SignaturePattern ret = new SignaturePattern(Member.FIELD, modifiers, returnType, declaringType, name, TypePatternList.ANY,
ThrowsPattern.ANY, annotationPattern);
int endPos = tokenSource.peek(-1).getEnd();
ret.setLocation(sourceContext, startPos, endPos);
return ret;
}
private NamePattern tryToExtractName(TypePattern nextType) {
if (nextType == TypePattern.ANY) {
return NamePattern.ANY;
} else if (nextType instanceof WildTypePattern) {
WildTypePattern p = (WildTypePattern) nextType;
return p.extractName();
} else {
return null;
}
}
/**
* Parse type variable declarations for a generic method or at the start of a signature pointcut to identify type variable names
* in a generic type.
*
* @param includeParameterizedTypes
* @return
*/
public TypeVariablePatternList maybeParseTypeVariableList() {
if (!maybeEat("<")) {
return null;
}
List<TypeVariablePattern> typeVars = new ArrayList<TypeVariablePattern>();
TypeVariablePattern t = parseTypeVariable();
typeVars.add(t);
while (maybeEat(",")) {
TypeVariablePattern nextT = parseTypeVariable();
typeVars.add(nextT);
}
eat(">");
TypeVariablePattern[] tvs = new TypeVariablePattern[typeVars.size()];
typeVars.toArray(tvs);
return new TypeVariablePatternList(tvs);
}
// of the form execution<T,S,V> - allows identifiers only
public String[] maybeParseSimpleTypeVariableList() {
if (!maybeEat("<")) {
return null;
}
List<String> typeVarNames = new ArrayList<String>();
do {
typeVarNames.add(parseIdentifier());
} while (maybeEat(","));
eat(">", "',' or '>'");
String[] tvs = new String[typeVarNames.size()];
typeVarNames.toArray(tvs);
return tvs;
}
public TypePatternList maybeParseTypeParameterList() {
if (!maybeEat("<")) {
return null;
}
List<TypePattern> typePats = new ArrayList<TypePattern>();
do {
TypePattern tp = parseTypePattern(true, false);
typePats.add(tp);
} while (maybeEat(","));
eat(">");
TypePattern[] tps = new TypePattern[typePats.size()];
typePats.toArray(tps);
return new TypePatternList(tps);
}
public TypeVariablePattern parseTypeVariable() {
TypePattern upperBound = null;
TypePattern[] additionalInterfaceBounds = null;
TypePattern lowerBound = null;
String typeVariableName = parseIdentifier();
if (maybeEatIdentifier("extends")) {
upperBound = parseTypePattern();
additionalInterfaceBounds = maybeParseAdditionalInterfaceBounds();
} else if (maybeEatIdentifier("super")) {
lowerBound = parseTypePattern();
}
return new TypeVariablePattern(typeVariableName, upperBound, additionalInterfaceBounds, lowerBound);
}
private TypePattern[] maybeParseAdditionalInterfaceBounds() {
List<TypePattern> boundsList = new ArrayList<TypePattern>();
while (maybeEat("&")) {
TypePattern tp = parseTypePattern();
boundsList.add(tp);
}
if (boundsList.size() == 0) {
return null;
}
TypePattern[] ret = new TypePattern[boundsList.size()];
boundsList.toArray(ret);
return ret;
}
public String parsePossibleStringSequence(boolean shouldEnd) {
StringBuffer result = new StringBuffer();
IToken token = tokenSource.next();
if (token.getLiteralKind() == null) {
throw new ParserException("string", token);
}
while (token.getLiteralKind().equals("string")) {
result.append(token.getString());
boolean plus = maybeEat("+");
if (!plus) {
break;
}
token = tokenSource.next();
if (token.getLiteralKind() == null) {
throw new ParserException("string", token);
}
}
eatIdentifier(";");
IToken t = tokenSource.next();
if (shouldEnd && t != IToken.EOF) {
throw new ParserException("<string>;", token);
}
// bug 125027: since we've eaten the ";" we need to set the index
// to be one less otherwise the end position isn't set correctly.
int currentIndex = tokenSource.getIndex();
tokenSource.setIndex(currentIndex - 1);
return result.toString();
}
public String parseStringLiteral() {
IToken token = tokenSource.next();
String literalKind = token.getLiteralKind();
if (literalKind == "string") {
return token.getString();
}
throw new ParserException("string", token);
}
public String parseIdentifier() {
IToken token = tokenSource.next();
if (token.isIdentifier()) {
return token.getString();
}
throw new ParserException("identifier", token);
}
public void eatIdentifier(String expectedValue) {
IToken next = tokenSource.next();
if (!next.getString().equals(expectedValue)) {
throw new ParserException(expectedValue, next);
}
}
public boolean maybeEatIdentifier(String expectedValue) {
IToken next = tokenSource.peek();
if (next.getString().equals(expectedValue)) {
tokenSource.next();
return true;
} else {
return false;
}
}
public void eat(String expectedValue) {
eat(expectedValue, expectedValue);
}
private void eat(String expectedValue, String expectedMessage) {
IToken next = nextToken();
if (next.getString() != expectedValue) {
if (expectedValue.equals(">") && next.getString().startsWith(">")) {
// handle problem of >> and >>> being lexed as single tokens
pendingRightArrows = BasicToken.makeLiteral(next.getString().substring(1).intern(), "string", next.getStart() + 1,
next.getEnd());
return;
}
throw new ParserException(expectedMessage, next);
}
}
private IToken pendingRightArrows;
private IToken nextToken() {
if (pendingRightArrows != null) {
IToken ret = pendingRightArrows;
pendingRightArrows = null;
return ret;
} else {
return tokenSource.next();
}
}
public boolean maybeEatAdjacent(String token) {
IToken next = tokenSource.peek();
if (next.getString() == token) {
if (isAdjacent(tokenSource.peek(-1), next)) {
tokenSource.next();
return true;
}
}
return false;
}
public boolean maybeEat(String token) {
IToken next = tokenSource.peek();
if (next.getString() == token) {
tokenSource.next();
return true;
} else {
return false;
}
}
public String maybeEatIdentifier() {
IToken next = tokenSource.peek();
if (next.isIdentifier()) {
tokenSource.next();
return next.getString();
} else {
return null;
}
}
public boolean peek(String token) {
IToken next = tokenSource.peek();
return next.getString() == token;
}
public void checkEof() {
IToken last = tokenSource.next();
if (last != IToken.EOF) {
throw new ParserException("unexpected pointcut element: " + last.toString(), last);
}
}
public PatternParser(String data) {
this(BasicTokenSource.makeTokenSource(data, null));
}
public PatternParser(String data, ISourceContext context) {
this(BasicTokenSource.makeTokenSource(data, context));
}
}