import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import javax.xml.xpath.XPathExpressionException;
-import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.RecognitionException;
-import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
-import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
import org.opendaylight.yangtools.yang.common.YangConstants;
import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathLexer;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbbreviatedStepContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AbsoluteLocationPathNorootContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AdditiveExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AndExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.AxisSpecifierContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.EqualityExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.ExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FilterExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionCallContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.FunctionNameContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.LocationPathContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.MultiplicativeExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NCNameContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NameTestContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.NodeTestContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.OrExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PathExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PredicateContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.PrimaryExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.QNameContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelationalExprContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.RelativeLocationPathContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.StepContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnaryExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.UnionExprNoRootContext;
+import org.opendaylight.yangtools.yang.xpath.antlr.xpathParser.VariableReferenceContext;
import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbbreviatedStepContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbsoluteLocationPathNorootContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AdditiveExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AndExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AxisSpecifierContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.EqualityExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.ExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FilterExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionCallContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.FunctionNameContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.LocationPathContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.MultiplicativeExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NCNameContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NameTestContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.NodeTestContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.OrExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PathExprNoRootContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PredicateContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.PrimaryExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.QNameContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelationalExprContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.RelativeLocationPathContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.StepContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnaryExprNoRootContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnionExprNoRootContext;
-import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.VariableReferenceContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
*
* @author Robert Varga
*/
-final class AntlrXPathParser implements YangXPathParser {
- private static final Logger LOG = LoggerFactory.getLogger(AntlrXPathParser.class);
+abstract class AntlrXPathParser implements YangXPathParser {
+ static class Base extends AntlrXPathParser {
+ Base(final YangXPathMathMode mathMode) {
+ super(mathMode);
+ }
+
+ @Override
+ public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
+ final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+ return new AntlrYangXPathExpression.Base(mathMode, result.getKey(), result.getValue(), xpath);
+ }
+
+ @Override
+ QNameStep createStep(final YangXPathAxis axis, final String localName,
+ final List<YangExpr> predicates) {
+ return axis.asStep(UnqualifiedQName.of(localName).intern(), predicates);
+ }
+
+ @Override
+ QNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+ final List<YangExpr> predicates) {
+ return axis.asStep(QualifiedQName.of(prefix, localName).intern(), predicates);
+ }
+
+ @Override
+ QName createQName(final String localName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ QName createQName(final String prefix, final String localName) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class Qualified extends Base implements QualifiedBound {
+ final YangNamespaceContext namespaceContext;
+
+ Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+ super(mathMode);
+ this.namespaceContext = requireNonNull(namespaceContext);
+ }
+
+ @Override
+ public YangXPathExpression.QualifiedBound parseExpression(final String xpath) throws XPathExpressionException {
+ final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+ return new AntlrYangXPathExpression.Qualified(mathMode, result.getKey(), result.getValue(), xpath,
+ namespaceContext);
+ }
+
+ @Override
+ final QName createQName(final String prefix, final String localName) {
+ return namespaceContext.createQName(prefix, localName);
+ }
+
+ @Override
+ ResolvedQNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+ final List<YangExpr> predicates) {
+ return axis.asStep(createQName(prefix, localName), predicates);
+ }
+ }
+
+ static final class Unqualified extends Qualified implements UnqualifiedBound {
+ private final QNameModule defaultNamespace;
+
+ Unqualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
+ final QNameModule defaultNamespace) {
+ super(mathMode, namespaceContext);
+ this.defaultNamespace = requireNonNull(defaultNamespace);
+ }
+
+ @Override
+ public YangXPathExpression.UnqualifiedBound parseExpression(final String xpath)
+ throws XPathExpressionException {
+ final Entry<YangVersion, YangExpr> result = parseExpr(xpath);
+
+ return new AntlrYangXPathExpression.Unqualified(mathMode, result.getKey(), result.getValue(), xpath,
+ namespaceContext, defaultNamespace);
+ }
+
+ @Override
+ QName createQName(final String localName) {
+ return QName.create(defaultNamespace, localName);
+ }
+
+ @Override
+ ResolvedQNameStep createStep(final YangXPathAxis axis, final String localName,
+ final List<YangExpr> predicates) {
+ return axis.asStep(QName.create(defaultNamespace, localName), predicates);
+ }
+ }
+
private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
// Cached for checks in hot path
private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
- private final YangXPathMathMode mathMode;
- private final YangXPathMathSupport<?> mathSupport;
- private final YangNamespaceContext namespaceContext;
+ final YangXPathMathMode mathMode;
+ private final YangXPathMathSupport mathSupport;
private final FunctionSupport functionSupport;
- AntlrXPathParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+ private YangVersion minimumYangVersion = YangVersion.VERSION_1;
+
+ AntlrXPathParser(final YangXPathMathMode mathMode) {
this.mathMode = requireNonNull(mathMode);
this.mathSupport = mathMode.getSupport();
- this.namespaceContext = requireNonNull(namespaceContext);
- this.functionSupport = new FunctionSupport(namespaceContext, mathSupport);
+ this.functionSupport = new FunctionSupport(mathSupport);
}
- @Override
- public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
+ abstract QName createQName(String localName);
+
+ abstract QName createQName(String prefix, String localName);
+
+ abstract QNameStep createStep(YangXPathAxis axis, String localName, List<YangExpr> predicates);
+
+ abstract QNameStep createStep(YangXPathAxis axis, String prefix, String localName, List<YangExpr> predicates);
+
+ private QNameStep createStep(final YangXPathAxis axis, final QNameContext expr, final List<YangExpr> predicates) {
+ switch (expr.getChildCount()) {
+ case 1:
+ return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(), predicates);
+ case 3:
+ return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(),
+ getChild(expr, NCNameContext.class, 2).getText(), predicates);
+ default:
+ throw illegalShape(expr);
+ }
+ }
+
+ @SuppressWarnings("checkstyle:illegalCatch")
+ final Entry<YangVersion, YangExpr> parseExpr(final String xpath) throws XPathExpressionException {
// Create a parser and disconnect it from console error output
final xpathLexer lexer = new xpathLexer(CharStreams.fromString(xpath));
final xpathParser parser = new xpathParser(new CommonTokenStream(lexer));
- final List<XPathExpressionException> errors = new ArrayList<>();
- final BaseErrorListener listener = new BaseErrorListener() {
- @Override
- public void syntaxError(final @Nullable Recognizer<?, ?> recognizer, final @Nullable Object offendingSymbol,
- final int line, final int charPositionInLine, final @Nullable String msg,
- final @Nullable RecognitionException cause) {
- final XPathExpressionException ex = new XPathExpressionException(msg);
- ex.initCause(cause);
- if (errors.isEmpty()) {
- errors.add(ex);
- } else {
- errors.get(0).addSuppressed(ex);
- }
- }
- };
-
+ final CapturingErrorListener listener = new CapturingErrorListener();
lexer.removeErrorListeners();
lexer.addErrorListener(listener);
parser.removeErrorListeners();
parser.addErrorListener(listener);
+ final ExprContext antlr = parser.main().expr();
+ listener.reportError();
- final YangExpr expr = parseExpr(parser.main().expr());
- if (!errors.isEmpty()) {
- throw errors.get(0);
- }
+ // Reset our internal context
+ minimumYangVersion = YangVersion.VERSION_1;
- return new AntlrYangXPathExpression(namespaceContext, mathMode, expr, xpath);
+ final YangExpr expr;
+ try {
+ expr = parseExpr(antlr);
+ } catch (RuntimeException e) {
+ throw new XPathExpressionException(e);
+ }
+ return new SimpleImmutableEntry<>(minimumYangVersion, expr);
}
/**
* Parse and simplify an XPath expression in {@link ExprContext} representation.
*
+ * @param ctx Current parsing context
* @param expr ANTLR ExprContext
* @return A {@link YangExpr}
* @throws NullPointerException if {@code expr} is null
parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
break;
case 3:
- parsed = namespaceContext.createQName(name.getChild(0).getText(), name.getChild(2).getText());
+ parsed = createQName(name.getChild(0).getText(), name.getChild(2).getText());
break;
default:
throw illegalShape(name);
}
- final List<YangExpr> args = ImmutableList.copyOf(Lists.transform(expr.expr(), this::parseExpr));
+ final List<YangExpr> args = expr.expr().stream().map(this::parseExpr).collect(ImmutableList.toImmutableList());
final YangFunction func = YANG_FUNCTIONS.get(parsed);
if (func != null) {
+ if (minimumYangVersion.compareTo(func.getYangVersion()) < 0) {
+ minimumYangVersion = func.getYangVersion();
+ }
return functionSupport.functionToExpr(func, args);
}
verifyChildCount(expr, 1);
final ParseTree first = expr.getChild(0);
if (first instanceof RelativeLocationPathContext) {
- return parseRelativeLocationPath((RelativeLocationPathContext) first);
+ return YangLocationPath.relative(parseLocationPathSteps((RelativeLocationPathContext) first));
}
final AbsoluteLocationPathNorootContext abs = verifyTree(AbsoluteLocationPathNorootContext.class, first);
verifyChildCount(abs, 2);
final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
- switch (getTerminalType(abs, 0)) {
- case xpathParser.PATHSEP:
- break;
- case xpathParser.ABRPATH:
- steps.addFirst(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
- break;
- default:
- throw illegalShape(abs);
- }
+ parseStepShorthand(abs.getChild(0)).ifPresent(steps::addFirst);
- return YangLocationPath.of(true, steps);
+ return YangLocationPath.absolute(steps);
}
private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
}
verifyChildCount(expr, 3);
- return parseOperator(expr.getChild(1)).exprWith(filter,
- parseRelativeLocationPath(getChild(expr, RelativeLocationPathContext.class, 2)));
+ final Deque<Step> steps = parseLocationPathSteps(getChild(expr, RelativeLocationPathContext.class, 2));
+ parseStepShorthand(expr.getChild(1)).ifPresent(steps::addFirst);
+ return YangPathExpr.of(filter, YangLocationPath.relative(steps));
}
private YangExpr parsePredicate(final PredicateContext expr) {
private Deque<Step> parseLocationPathSteps(final RelativeLocationPathContext expr) {
final Deque<Step> steps = new ArrayDeque<>(expr.getChildCount());
final Iterator<ParseTree> it = expr.children.iterator();
- steps.add(parseStep(nextContext(it, StepContext.class)));
+ addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
while (it.hasNext()) {
- final ParseTree tree = it.next();
- switch (verifyTerminal(tree).getSymbol().getType()) {
- case xpathParser.PATHSEP:
- break;
- case xpathParser.ABRPATH:
- steps.add(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
- break;
- default:
- throw illegalShape(tree);
- }
+ parseStepShorthand(it.next()).ifPresent(steps::add);
// Parse step and add it if it's not SELF_STEP
- final Step step = parseStep(nextContext(it, StepContext.class));
- if (!SELF_STEP.equals(step)) {
- steps.add(step);
- }
+ addNotSelfStep(steps, parseStep(nextContext(it, StepContext.class)));
}
return steps;
}
- private YangLocationPath parseRelativeLocationPath(final RelativeLocationPathContext expr) {
- return YangLocationPath.of(false, parseLocationPathSteps(expr));
+ private static void addNotSelfStep(final Deque<Step> steps, final Step step) {
+ if (!SELF_STEP.equals(step)) {
+ steps.add(step);
+ }
}
private YangExpr parseTerminal(final TerminalNode term) {
// Even number of '-' tokens cancel out
return ret;
}
-
- return ret instanceof YangNumberExpr ? mathSupport.negateNumber((YangNumberExpr<?, ?>) ret)
- : YangNegateExpr.of(ret);
+ return ret instanceof YangNumberExpr ? mathSupport.negateNumber((YangNumberExpr) ret) : YangNegateExpr.of(ret);
}
private YangExpr parseUnion(final UnionExprNoRootContext expr) {
final YangExpr right) {
if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) {
// Constant folding on numbers -- precision plays a role here
- return mathSupport.tryEvaluate(operator, (YangNumberExpr<?, ?>)left, (YangNumberExpr<?, ?>)right);
+ return mathSupport.tryEvaluate(operator, (YangNumberExpr)left, (YangNumberExpr)right);
}
return Optional.empty();
}
final YangBinaryOperator operator = nextOperator(it);
final YangExpr right = parseAdditive(nextContext(it, AdditiveExprContext.class));
final Optional<YangExpr> simple = simplifyNumbers(operator, ret, right);
- ret = simple.isPresent() ? simple.get() : nextOperator(it).exprWith(ret, right);
+ ret = simple.isPresent() ? simple.get() : operator.exprWith(ret, right);
} while (it.hasNext());
return ret;
}
- private QName parseQName(final QNameContext expr) {
- switch (expr.getChildCount()) {
- case 1:
- return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText());
- case 3:
- return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText(),
- getChild(expr, NCNameContext.class, 2).getText());
- default:
- throw illegalShape(expr);
- }
- }
-
private Step parseStep(final StepContext expr) {
if (expr.getChildCount() == 1) {
final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
return axis.asStep(predicates);
}
- return axis.asStep(parseQName(verifyTree(QNameContext.class, first)), predicates);
+ return createStep(axis, verifyTree(QNameContext.class, first), predicates);
case 3:
return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
case 4:
}
}
+ private QName parseQName(final QNameContext expr) {
+ switch (expr.getChildCount()) {
+ case 1:
+ return createQName(getChild(expr, NCNameContext.class, 0).getText());
+ case 3:
+ return createQName(getChild(expr, NCNameContext.class, 0).getText(),
+ getChild(expr, NCNameContext.class, 2).getText());
+ default:
+ throw illegalShape(expr);
+ }
+ }
+
private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
return verifyTree(type, it.next());
}
final String str = verifyTerminal(tree).getText();
return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str);
}
+
+ private static Optional<Step> parseStepShorthand(final ParseTree tree) {
+ switch (verifyTerminal(tree).getSymbol().getType()) {
+ case xpathParser.PATHSEP:
+ return Optional.empty();
+ case xpathParser.ABRPATH:
+ return Optional.of(YangXPathAxis.DESCENDANT_OR_SELF.asStep());
+ default:
+ throw illegalShape(tree);
+ }
+ }
}