Refactor yang-xpath-impl error handling
[yangtools.git] / yang / yang-xpath-impl / src / main / java / org / opendaylight / yangtools / yang / xpath / impl / InstanceIdentifierParser.java
1 /*
2  * Copyright (c) 2019 Pantheon Technologies, s.r.o.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.xpath.impl;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild;
12 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape;
13 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken;
14
15 import com.google.common.collect.ImmutableSet;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.List;
19 import javax.xml.xpath.XPathExpressionException;
20 import org.antlr.v4.runtime.CharStreams;
21 import org.antlr.v4.runtime.CommonTokenStream;
22 import org.antlr.v4.runtime.tree.ParseTree;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
25 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
26 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
27 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
28 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
29 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
30 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
31 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
32 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
33 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
34 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.EqQuotedStringContext;
35 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.InstanceIdentifierContext;
36 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateContext;
37 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateExprContext;
38 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateContext;
39 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateExprContext;
40 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.NodeIdentifierContext;
41 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PathArgumentContext;
42 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosContext;
43 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PredicateContext;
44 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.QuotedStringContext;
45
46 final class InstanceIdentifierParser {
47     private final YangNamespaceContext namespaceContext;
48     private final YangXPathMathSupport mathSupport;
49
50     InstanceIdentifierParser(final YangNamespaceContext namespaceContext, final YangXPathMathMode mathMode) {
51         this.namespaceContext = requireNonNull(namespaceContext);
52         this.mathSupport = mathMode.getSupport();
53     }
54
55     YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
56         final xpathLexer lexer = new xpathLexer(CharStreams.fromString(expr.getLiteral()));
57         final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer));
58         final CapturingErrorListener listener = new CapturingErrorListener();
59         lexer.removeErrorListeners();
60         lexer.addErrorListener(listener);
61         parser.removeErrorListeners();
62         parser.addErrorListener(listener);
63
64         final InstanceIdentifierContext id = parser.instanceIdentifier();
65         listener.reportError();
66
67         final int length = id.getChildCount();
68         final List<Step> steps = new ArrayList<>(length / 2);
69         for (int i = 1; i < length; i += 2) {
70             steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i)));
71         }
72
73         return YangLocationPath.of(true, steps);
74     }
75
76     private Step parsePathArgument(final PathArgumentContext expr) {
77         final QName qname = parseQName(getChild(expr, NodeIdentifierContext.class, 0));
78         switch (expr.getChildCount()) {
79             case 1:
80                 return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of());
81             case 2:
82                 return YangXPathAxis.CHILD.asStep(qname, parsePredicate(getChild(expr, PredicateContext.class, 1)));
83             default:
84                 throw illegalShape(expr);
85         }
86     }
87
88     private Collection<YangExpr> parsePredicate(final PredicateContext expr) {
89         final ParseTree first = expr.getChild(0);
90         if (first instanceof LeafListPredicateContext) {
91             return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(),
92                 parseEqStringValue(getChild(((LeafListPredicateContext) first)
93                     .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1))));
94         } else if (first instanceof PosContext) {
95             return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(FunctionSupport.POSITION,
96                 mathSupport.createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0)
97                     .getText())));
98         }
99
100         final int length = expr.getChildCount();
101         final List<YangExpr> ret = new ArrayList<>(length);
102         for (int i = 0; i < length; ++i) {
103             final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
104                     .getChild(KeyPredicateExprContext.class, 0);
105             ret.add(YangBinaryOperator.EQUALS.exprWith(
106                 YangQNameExpr.of(parseQName(getChild(pred, NodeIdentifierContext.class, 0))),
107                 parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
108
109         }
110
111         return ret;
112     }
113
114     private QName parseQName(final NodeIdentifierContext expr) {
115         return namespaceContext.createQName(
116             verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(),
117             verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText());
118     }
119
120     private static YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
121         return YangLiteralExpr.of(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1,
122             instanceIdentifierParser.STRING).getText());
123     }
124 }