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