Squash self-steps when parsing expression
[yangtools.git] / yang / yang-xpath-impl / src / main / java / org / opendaylight / yangtools / yang / xpath / impl / AntlrXPathParser.java
index 1366bbe334012b186647af689f5d018a12ece3fe..0c8a55413591fa14a36f69b46f0efc24dec6fbe3 100644 (file)
@@ -21,8 +21,8 @@ import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTr
 
 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;
@@ -31,6 +31,7 @@ import java.util.Iterator;
 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;
@@ -40,8 +41,12 @@ import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
 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.api.YangBinaryOperator;
 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
@@ -51,11 +56,14 @@ import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
 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;
@@ -89,16 +97,104 @@ 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(
@@ -111,20 +207,40 @@ final class AntlrXPathParser implements YangXPathParser {
     // Cached for checks in hot path
     private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
 
-    private final YangXPathMathMode mathMode;
+    final YangXPathMathMode mathMode;
     private final YangXPathMathSupport mathSupport;
-    private final YangNamespaceContext namespaceContext;
     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));
@@ -134,15 +250,25 @@ final class AntlrXPathParser implements YangXPathParser {
         lexer.addErrorListener(listener);
         parser.removeErrorListeners();
         parser.addErrorListener(listener);
-
-        final YangExpr expr = parseExpr(parser.main().expr());
+        final ExprContext antlr = parser.main().expr();
         listener.reportError();
-        return new AntlrYangXPathExpression(namespaceContext, mathMode, expr, xpath);
+
+        // Reset our internal context
+        minimumYangVersion = YangVersion.VERSION_1;
+
+        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
@@ -203,15 +329,18 @@ final class AntlrXPathParser implements YangXPathParser {
                 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);
         }
 
@@ -224,7 +353,7 @@ final class AntlrXPathParser implements YangXPathParser {
         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);
@@ -233,7 +362,7 @@ final class AntlrXPathParser implements YangXPathParser {
         final Deque<Step> steps = parseLocationPathSteps(getChild(abs, RelativeLocationPathContext.class, 1));
         parseStepShorthand(abs.getChild(0)).ifPresent(steps::addFirst);
 
-        return YangLocationPath.of(true, steps);
+        return YangLocationPath.absolute(steps);
     }
 
     private YangExpr parseMultiplicative(final MultiplicativeExprContext expr) {
@@ -267,10 +396,9 @@ final class AntlrXPathParser implements YangXPathParser {
         }
 
         verifyChildCount(expr, 3);
-
-        // FIXME: this actually is a concatenation
-        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) {
@@ -306,23 +434,22 @@ final class AntlrXPathParser implements YangXPathParser {
     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()) {
             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) {
@@ -440,18 +567,6 @@ final class AntlrXPathParser implements YangXPathParser {
         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);
@@ -482,7 +597,7 @@ final class AntlrXPathParser implements YangXPathParser {
                     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:
@@ -508,6 +623,18 @@ final class AntlrXPathParser implements YangXPathParser {
         }
     }
 
+    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());
     }