2 * Copyright (c) 2019 Pantheon Technologies, s.r.o. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.xpath.impl;
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.verifyTerminal;
14 import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken;
16 import com.google.common.base.VerifyException;
17 import com.google.common.collect.ImmutableSet;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 import javax.xml.xpath.XPathExpressionException;
22 import org.antlr.v4.runtime.CharStreams;
23 import org.antlr.v4.runtime.CommonTokenStream;
24 import org.antlr.v4.runtime.tree.ParseTree;
25 import org.opendaylight.yangtools.yang.common.UnresolvedQName;
26 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
27 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierLexer;
28 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser;
29 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.EqQuotedStringContext;
30 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.KeyPredicateContext;
31 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.KeyPredicateExprContext;
32 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.LeafListPredicateContext;
33 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.LeafListPredicateExprContext;
34 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.NodeIdentifierContext;
35 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.PathArgumentContext;
36 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.PosContext;
37 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.PredicateContext;
38 import org.opendaylight.yangtools.yang.xpath.antlr.instanceIdentifierParser.QuotedStringContext;
39 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
40 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
41 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
42 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
43 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
44 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
45 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
46 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
47 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
48 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
49 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport;
51 abstract class InstanceIdentifierParser {
52 static final class Base extends InstanceIdentifierParser {
53 Base(final YangXPathMathMode mathMode) {
58 YangQNameExpr createExpr(final String prefix, final String localName) {
59 return YangQNameExpr.of(UnresolvedQName.qualified(prefix, localName));
63 QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
64 return YangXPathAxis.CHILD.asStep(UnresolvedQName.qualified(prefix, localName), predicates);
68 static final class Qualified extends InstanceIdentifierParser {
69 final YangNamespaceContext namespaceContext;
71 Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
73 this.namespaceContext = requireNonNull(namespaceContext);
77 QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
78 return YangXPathAxis.CHILD.asStep(namespaceContext.createQName(prefix, localName), predicates);
83 YangQNameExpr createExpr(final String prefix, final String localName) {
84 return YangQNameExpr.of(namespaceContext.createQName(prefix, localName));
89 private final YangXPathMathSupport mathSupport;
91 InstanceIdentifierParser(final YangXPathMathMode mathMode) {
92 mathSupport = mathMode.getSupport();
95 final Absolute interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
96 final var lexer = new instanceIdentifierLexer(CharStreams.fromString(expr.getLiteral()));
97 final var parser = new instanceIdentifierParser(new CommonTokenStream(lexer));
98 final var listener = new CapturingErrorListener();
99 lexer.removeErrorListeners();
100 lexer.addErrorListener(listener);
101 parser.removeErrorListeners();
102 parser.addErrorListener(listener);
104 final var id = parser.instanceIdentifier();
105 listener.reportError();
107 final int length = id.getChildCount();
108 final List<Step> steps = new ArrayList<>(length / 2);
109 for (int i = 1; i < length; i += 2) {
110 steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i)));
113 return YangLocationPath.absolute(steps);
116 abstract YangQNameExpr createExpr(String prefix, String localName);
118 abstract QNameStep createChildStep(String prefix, String localName, Collection<YangExpr> predicates);
120 private QNameStep parsePathArgument(final PathArgumentContext expr) {
121 final NodeIdentifierContext childExpr = getChild(expr, NodeIdentifierContext.class, 0);
122 final String prefix = verifyIdentifier(childExpr, 0);
123 final String localName = verifyIdentifier(childExpr, 2);
125 switch (expr.getChildCount()) {
127 return createChildStep(prefix, localName, ImmutableSet.of());
129 return createChildStep(prefix, localName, parsePredicate(getChild(expr, PredicateContext.class, 1)));
131 throw illegalShape(expr);
135 private Collection<YangExpr> parsePredicate(final PredicateContext expr) {
136 final ParseTree first = expr.getChild(0);
137 if (first instanceof LeafListPredicateContext) {
138 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(),
139 parseEqStringValue(getChild(((LeafListPredicateContext) first)
140 .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1))));
141 } else if (first instanceof PosContext) {
142 return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(FunctionSupport.POSITION,
143 mathSupport.createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0)
147 final int length = expr.getChildCount();
148 final List<YangExpr> ret = new ArrayList<>(length);
149 for (int i = 0; i < length; ++i) {
150 final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
151 .getChild(KeyPredicateExprContext.class, 0);
152 ret.add(YangBinaryOperator.EQUALS.exprWith(
153 createChildExpr(getChild(pred, NodeIdentifierContext.class, 0)),
154 parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
161 private YangQNameExpr createChildExpr(final NodeIdentifierContext expr) {
162 return createExpr(verifyIdentifier(expr, 0), verifyIdentifier(expr, 2));
165 private static String verifyIdentifier(final NodeIdentifierContext expr, final int child) {
166 return verifyToken(expr, child, instanceIdentifierParser.Identifier).getText();
169 private static YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
170 final var quotedString = getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1);
171 switch (quotedString.getChildCount()) {
173 return YangLiteralExpr.empty();
175 final var terminal = verifyTerminal(quotedString.getChild(0));
176 final var token = terminal.getSymbol();
177 final String literal;
178 switch (token.getType()) {
179 case instanceIdentifierParser.DQUOT_STRING:
180 literal = unescape(token.getText());
182 case instanceIdentifierParser.SQUOT_STRING:
183 literal = token.getText();
186 throw illegalShape(terminal);
188 return YangLiteralExpr.of(literal);
190 throw illegalShape(quotedString);
194 private static String unescape(final String escaped) {
195 final int firstEscape = escaped.indexOf('\\');
196 return firstEscape == -1 ? escaped : unescape(escaped, firstEscape);
199 private static String unescape(final String escaped, final int firstEscape) {
200 // Sizing: optimize allocation for only a single escape
201 final int length = escaped.length();
202 final var sb = new StringBuilder(length - 1).append(escaped, 0, firstEscape);
204 int esc = firstEscape;
206 sb.append(replace(escaped.charAt(esc + 1)));
208 final int start = esc + 2;
209 esc = escaped.indexOf('\\', start);
211 return sb.append(escaped, start, length).toString();
214 sb.append(escaped, start, esc);
218 private static char replace(final char ch) {
229 throw new VerifyException("Unexpected escaped char '" + ch + "'");