super(mathMode);
}
+ @Override
+ YangQNameExpr createExpr(final String localName) {
+ return YangQNameExpr.of(UnresolvedQName.unqualified(localName));
+ }
+
@Override
YangQNameExpr createExpr(final String prefix, final String localName) {
return YangQNameExpr.of(UnresolvedQName.qualified(prefix, localName));
}
+ @Override
+ QNameStep createChildStep(final String localName, final Collection<YangExpr> predicates) {
+ return YangXPathAxis.CHILD.asStep(UnresolvedQName.unqualified(localName), predicates);
+ }
+
@Override
QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
return YangXPathAxis.CHILD.asStep(UnresolvedQName.qualified(prefix, localName), predicates);
}
static final class Qualified extends InstanceIdentifierParser {
- final YangNamespaceContext namespaceContext;
+ private final YangNamespaceContext namespaceContext;
Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
super(mathMode);
}
@Override
- QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
- return YangXPathAxis.CHILD.asStep(namespaceContext.createQName(prefix, localName), predicates);
+ YangQNameExpr createExpr(final String localName) {
+ return YangQNameExpr.of(UnresolvedQName.unqualified(localName));
}
-
@Override
YangQNameExpr createExpr(final String prefix, final String localName) {
return YangQNameExpr.of(namespaceContext.createQName(prefix, localName));
}
+ @Override
+ QNameStep createChildStep(final String localName, final Collection<YangExpr> predicates) {
+ return YangXPathAxis.CHILD.asStep(UnresolvedQName.unqualified(localName), predicates);
+ }
+
+ @Override
+ QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
+ return YangXPathAxis.CHILD.asStep(namespaceContext.createQName(prefix, localName), predicates);
+ }
}
private final YangXPathMathSupport mathSupport;
return YangLocationPath.absolute(steps);
}
+ abstract YangQNameExpr createExpr(String localName);
+
abstract YangQNameExpr createExpr(String prefix, String localName);
+ abstract QNameStep createChildStep(String localName, Collection<YangExpr> predicates);
+
abstract QNameStep createChildStep(String prefix, String localName, Collection<YangExpr> predicates);
private QNameStep parsePathArgument(final PathArgumentContext expr) {
- final NodeIdentifierContext childExpr = getChild(expr, NodeIdentifierContext.class, 0);
- final String prefix = verifyIdentifier(childExpr, 0);
- final String localName = verifyIdentifier(childExpr, 2);
-
+ final Collection<YangExpr> predicates;
switch (expr.getChildCount()) {
case 1:
- return createChildStep(prefix, localName, ImmutableSet.of());
+ predicates = ImmutableSet.of();
+ break;
case 2:
- return createChildStep(prefix, localName, parsePredicate(getChild(expr, PredicateContext.class, 1)));
+ predicates = parsePredicate(getChild(expr, PredicateContext.class, 1));
+ break;
default:
throw illegalShape(expr);
}
+
+ final NodeIdentifierContext childExpr = getChild(expr, NodeIdentifierContext.class, 0);
+ final String first = verifyIdentifier(childExpr, 0);
+
+ switch (childExpr.getChildCount()) {
+ case 1:
+ return createChildStep(first, predicates);
+ case 3:
+ return createChildStep(first, verifyIdentifier(childExpr, 2), predicates);
+ default:
+ throw illegalShape(childExpr);
+ }
}
private Collection<YangExpr> parsePredicate(final PredicateContext expr) {
}
private YangQNameExpr createChildExpr(final NodeIdentifierContext expr) {
- return createExpr(verifyIdentifier(expr, 0), verifyIdentifier(expr, 2));
+ final var first = verifyIdentifier(expr, 0);
+ switch (expr.getChildCount()) {
+ case 1:
+ return createExpr(first);
+ case 3:
+ return createExpr(first, verifyIdentifier(expr, 2));
+ default:
+ throw illegalShape(expr);
+ }
}
private static String verifyIdentifier(final NodeIdentifierContext expr, final int child) {
*/
package org.opendaylight.yangtools.yang.xpath.impl;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
import com.google.common.collect.ImmutableBiMap;
-import org.junit.Test;
+import java.util.List;
+import java.util.stream.Stream;
+import javax.xml.xpath.XPathExpressionException;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.BiMapYangNamespaceContext;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.UnresolvedQName;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+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.Absolute;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
-class InstanceIdentifierParserTest {
+public class InstanceIdentifierParserTest {
private static final QNameModule DEFNS = QNameModule.create(XMLNamespace.of("defaultns"));
private static final YangNamespaceContext CONTEXT = new BiMapYangNamespaceContext(ImmutableBiMap.of(
"def", DEFNS,
"foo", QNameModule.create(XMLNamespace.of("foo")),
"bar", QNameModule.create(XMLNamespace.of("bar"))));
- private static final InstanceIdentifierParser PARSER =
+ private static final QName FOO_FOO_QUALIFIED = CONTEXT.createQName("foo", "foo");
+ private static final QName FOO_X_QUALIFIED = CONTEXT.createQName("foo", "x");
+ private static final UnresolvedQName FOO_FOO_UNRESOLVED = UnresolvedQName.qualified("foo", "foo");
+ private static final UnresolvedQName FOO_X_UNRESOLVED = UnresolvedQName.qualified("foo", "x");
+ private static final UnresolvedQName FOO_UNRESOLVED = UnresolvedQName.unqualified("foo");
+ private static final UnresolvedQName X_UNRESOLVED = UnresolvedQName.unqualified("x");
+
+ private static final YangExpr POSITION_EXPR = FunctionSupport.POSITION;
+ private static final YangExpr SELF_EXPR = YangLocationPath.self();
+
+ private static final InstanceIdentifierParser BASE = new InstanceIdentifierParser.Base(YangXPathMathMode.IEEE754);
+ private static final InstanceIdentifierParser QUALIFIED =
new InstanceIdentifierParser.Qualified(YangXPathMathMode.IEEE754, CONTEXT);
- @Test
- void smokeTests() throws Exception {
- assertPath("/foo:foo");
- assertPath("/foo:foo[.='abcd']");
- assertPath("/foo:foo[.=\"abcd\"]");
+ @ParameterizedTest(name = "Smoke test: {0}")
+ @MethodSource("smokeTestArgs")
+ public void smokeTest(final String input) throws Exception {
+ parseBase(input);
+ parseQualified(input);
+ }
+
+ public static List<String> smokeTestArgs() {
+ return List.of("/foo:foo/bar:bar", "/foo/bar", "/foo/bar[ 1 ]", "/foo/bar[11]", "/foo:foo[.='abc']",
+ "/foo[ . = \"abc\" ]", "/bar:bar[foo:x = 'a'][foo:y = 'b']", "/a/b/c");
+ }
+
+ @ParameterizedTest(name = "Qualified parsing: {0}")
+ @MethodSource("qualifiedParsingArgs")
+ public void qualifiedParsing(final String input, final AbstractQName expectedQName,
+ final @Nullable YangExpr expectedLeft, final @Nullable YangExpr expectedRight) throws Exception {
+ assertParsed(parseQualified(input), expectedQName, expectedLeft, expectedRight);
+ }
+
+ public static Stream<Arguments> qualifiedParsingArgs() {
+ return Stream.of(
+ // input, expected QName, expected left expr of predicate, expected right expr of predicate
+ Arguments.of("/foo:foo", FOO_FOO_QUALIFIED, null, null),
+ Arguments.of("/foo", FOO_UNRESOLVED, null, null),
+ Arguments.of("/foo:foo[1]", FOO_FOO_QUALIFIED, POSITION_EXPR, toNumberExpr(1)),
+ Arguments.of("/foo[ 101 ]", FOO_UNRESOLVED, POSITION_EXPR, toNumberExpr(101)),
+ Arguments.of("/foo:foo[foo:x='abc']", FOO_FOO_QUALIFIED,
+ YangQNameExpr.of(FOO_X_QUALIFIED), YangLiteralExpr.of("abc")),
+ Arguments.of("/foo[ x = \"xyz\" ]", FOO_UNRESOLVED,
+ YangQNameExpr.of(X_UNRESOLVED), YangLiteralExpr.of("xyz")),
+ Arguments.of("/foo:foo[.=\"\"]", FOO_FOO_QUALIFIED, SELF_EXPR, YangLiteralExpr.of("")),
+ Arguments.of("/foo[ . = \"value\" ]", FOO_UNRESOLVED, SELF_EXPR, YangLiteralExpr.of("value"))
+ );
+ }
+
+ @ParameterizedTest(name = "Base parsing: {0}")
+ @MethodSource("baseParsingArgs")
+ public void baseParsing(final String input, final AbstractQName expectedQName,
+ @Nullable final YangExpr expectedLeft, @Nullable final YangExpr expectedRight) throws Exception {
+ assertParsed(parseBase(input), expectedQName, expectedLeft, expectedRight);
+ }
+
+ public static Stream<Arguments> baseParsingArgs() {
+ return Stream.of(
+ // input, expected QName, expected left expr of predicate, expected right expr of predicate
+ Arguments.of("/foo:foo", FOO_FOO_UNRESOLVED, null, null),
+ Arguments.of("/foo", FOO_UNRESOLVED, null, null),
+ Arguments.of("/foo:foo[1]", FOO_FOO_UNRESOLVED, POSITION_EXPR, toNumberExpr(1)),
+ Arguments.of("/foo[ 101 ]", FOO_UNRESOLVED, POSITION_EXPR, toNumberExpr(101)),
+ Arguments.of("/foo:foo[foo:x='abc']", FOO_FOO_UNRESOLVED,
+ YangQNameExpr.of(FOO_X_UNRESOLVED), YangLiteralExpr.of("abc")),
+ Arguments.of("/foo[ x = \"xyz\" ]", FOO_UNRESOLVED,
+ YangQNameExpr.of(X_UNRESOLVED), YangLiteralExpr.of("xyz")),
+ Arguments.of("/foo:foo[.=\"\"]", FOO_FOO_UNRESOLVED, SELF_EXPR, YangLiteralExpr.of("")),
+ Arguments.of("/foo[ . = \"value\" ]", FOO_UNRESOLVED, SELF_EXPR, YangLiteralExpr.of("value"))
+ );
+ }
+
+ private static void assertParsed(final Absolute parsed, final AbstractQName expectedQName,
+ final @Nullable YangExpr expectedLeft, final @Nullable YangExpr expectedRight) throws Exception {
+ final var step = assertInstanceOf(QNameStep.class, extractFirstStep(parsed));
+ assertEquals(expectedQName, step.getQName());
+
+ if (expectedLeft == null) {
+ return;
+ }
+
+ final var predicate = assertInstanceOf(YangBinaryExpr.class, extractFirstPredicate(step));
+ assertEquals(YangBinaryOperator.EQUALS, predicate.getOperator());
+ assertEquals(expectedLeft, predicate.getLeftExpr());
+ assertEquals(expectedRight, predicate.getRightExpr());
+ }
+
+ @ParameterizedTest(name = "Unrecognized prefix: {0}")
+ @ValueSource(strings = {"/x:foo", "/foo:foo[x:bar = 'a']", "/foo[x:bar = 'b']"})
+ public void unrecognizedPrefix(final String input) {
+ assertThrows(IllegalArgumentException.class, () -> parseQualified(input));
+ }
+
+ @ParameterizedTest(name = "Literals with escaped quotes: {0}")
+ @MethodSource("escapedQuotesLiteralArgs")
+ public void escapedQuotesLiteral(final String input, final String expectedText) throws Exception {
+
+ // expected 1 step with predicate having literal
+ final var step = extractFirstStep(parseQualified(input));
+ final var predicate = assertInstanceOf(YangBinaryExpr.class, extractFirstPredicate(step));
+ final var right = assertInstanceOf(YangLiteralExpr.class, predicate.getRightExpr());
+
+ // ensure the string literal value unquoted properly
+ assertEquals(expectedText, right.getLiteral());
+ }
+
+ public static Stream<Arguments> escapedQuotesLiteralArgs() {
+ return Stream.of(
+ Arguments.of("/foo[.= \"quo\\\"tes\\\\\"]", "quo\"tes\\"),
+ Arguments.of("/foo[.= \"quo\\ntes\\t\"]", "quo\ntes\t"),
+ Arguments.of("/foo[.= 'quo\"tes']", "quo\"tes"),
+ Arguments.of("/foo[.= 'quo\\tes']", "quo\\tes"),
+ Arguments.of("/foo[.= \"\"]", ""),
+ Arguments.of("/foo[.= '']", ""),
+ Arguments.of("/foo[.= \"\"]", "")
+ );
+ }
+
+ private static Absolute parseQualified(final String literal) throws XPathExpressionException {
+ return QUALIFIED.interpretAsInstanceIdentifier(YangLiteralExpr.of(literal));
+ }
+
+ private static Absolute parseBase(final String literal) throws XPathExpressionException {
+ return BASE.interpretAsInstanceIdentifier(YangLiteralExpr.of(literal));
+ }
+
+ private static YangExpr toNumberExpr(final int number) {
+ return YangXPathMathMode.IEEE754.getSupport().createNumber(number);
+ }
+
+ private static Step extractFirstStep(final Absolute parsed) {
+ assertNotNull(parsed);
+ assertNotNull(parsed.getSteps());
+ assertFalse(parsed.getSteps().isEmpty());
+ final var step = parsed.getSteps().get(0);
+ assertNotNull(step);
+ return step;
}
- private static Absolute assertPath(final String literal) throws Exception {
- return PARSER.interpretAsInstanceIdentifier(YangLiteralExpr.of(literal));
+ private static YangExpr extractFirstPredicate(final Step step) {
+ assertNotNull(step.getPredicates());
+ assertFalse(step.getPredicates().isEmpty());
+ final var predicate = step.getPredicates().iterator().next();
+ assertNotNull(predicate);
+ return predicate;
}
}