/* ******************************************************************* * 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 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; 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 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; 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 parseDottedNamePattern() { List names = new ArrayList<>(); 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 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. * * @param includeParameterizedTypes * @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) { 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(";", 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)); } }