All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.weaver.patterns.PatternParser Maven / Gradle / Ivy

Go to download

The AspectJ matcher can be used for matching pointcuts independently of any AspectJ compilation or weaving steps. Most notably, this can be used by frameworks such as Spring AOP which utilise the @AspectJ pointcut syntax but implement aspect weaving in a way independent of AspectJ, e.g. using dynamic proxies.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * Copyright (c) 2002,2010
 * 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:
 *     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 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 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);
	}

	public boolean moreToParse() {
		return tokenSource.hasMoreTokens();
	}

	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 l = new ArrayList<>();
		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 l = new ArrayList<>();
		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(boolean shouldConsumeAllInput) {
		Pointcut p = parsePointcut();
		if (shouldConsumeAllInput && tokenSource.hasMoreTokens()) {
			throw new ParserException(
					"Found unexpected data after parsing pointcut",
					tokenSource.next());
		}
		return p;
	}

	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;
		StringBuilder pointcutBody = new StringBuilder();
		while (parenCount > 0) {
			if (maybeEat("(")) {
				parenCount++;
				pointcutBody.append("(");
			} else if (maybeEat(")")) {
				parenCount--;
				if (parenCount > 0) {
					pointcutBody.append(")");
				}
			} else {
				pointcutBody.append(nextToken().getString());
			}
		}
		ContextBasedMatcher pcExpr = pcdHandler.parse(pointcutBody.toString());
		return new PointcutDesignatorHandlerBasedPointcut(pcExpr, world);
	}

	public List parseDottedIdentifier() {
		List ret = new ArrayList<>();
		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 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 parseAnnotationValues() {
		Map values = new HashMap<>();
		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 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);
			} else if (category.equals("FinalType")) {
				typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.FINAL);
			} else if (category.equals("AbstractType")) {
				typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ABSTRACT);
			}
		}
		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 names = new ArrayList<>();
		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;
		StringBuilder annotationName = new StringBuilder();
		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 parseDottedNamePattern() {
		List names = new ArrayList<>();
		StringBuilder buf = new StringBuilder();
		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() {
		StringBuilder buf = new StringBuilder();
		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() {
		StringBuilder buf = new StringBuilder();
		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 patterns = new ArrayList<>();
		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 patterns = new ArrayList<>();
		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 required = new ArrayList<>();
			List forbidden = new ArrayList<>();
			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.
	 *
	 * @return
	 */
	public TypeVariablePatternList maybeParseTypeVariableList() {
		if (!maybeEat("<")) {
			return null;
		}
		List typeVars = new ArrayList<>();
		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 - allows identifiers only
	public String[] maybeParseSimpleTypeVariableList() {
		if (!maybeEat("<")) {
			return null;
		}
		List typeVarNames = new ArrayList<>();
		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 typePats = new ArrayList<>();
		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 boundsList = new ArrayList<>();
		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) {
		StringBuilder result = new StringBuilder();

		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(";", 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));
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy