<module>yang-model-api</module>
<module>yang-model-export</module>
<module>yang-model-util</module>
+ <module>yang-model-util-ut</module>
<!-- YANG XPath API and implementation -->
<module>yang-xpath-api</module>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>4.0.4-SNAPSHOT</version>
+ <relativePath>../../bundle-parent</relativePath>
+ </parent>
+
+ <!-- FIXME: YANGTOOLS-1052: merge this into yang-model-util -->
+ <artifactId>yang-model-util-ut</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>${project.artifactId}</description>
+
+ <properties>
+ <maven.deploy.skip>true</maven.deploy.skip>
+ <maven.install.skip>true</maven.install.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-test-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.util.ut;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class LeafrefStaticAnalysisTest {
+ private static final QName FOO = QName.create("leafrefs", "foo");
+
+ private static EffectiveModelContext context;
+ private static GroupingDefinition grp;
+ private static ListSchemaNode foo;
+ private static ContainerSchemaNode bar;
+ private static Module module;
+
+ @BeforeClass
+ public static void beforeClass() {
+ context = YangParserTestUtils.parseYangResource("/leafrefs.yang");
+ module = context.getModules().iterator().next();
+
+ foo = (ListSchemaNode) module.findDataChildByName(FOO).get();
+ bar = (ContainerSchemaNode) foo.findDataChildByName(QName.create(FOO, "bar")).get();
+ grp = module.getGroupings().iterator().next();
+ }
+
+ @Test
+ public void testGrpOuterId() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(QName.create(FOO, "outer-id")).get();
+ // Cannot be found as the reference goes outside of the grouping
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+
+ @Test
+ public void testFooOuterId() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(QName.create(FOO, "outer-id")).get();
+ final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+
+ assertThat(found, isA(LeafSchemaNode.class));
+ assertEquals(SchemaPath.create(true, FOO, QName.create(FOO, "id")), found.getPath());
+ }
+
+ @Test
+ public void testGrpOuterIndirectProp() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
+ QName.create(FOO, "outer-indirect-prop")).get();
+ // Cannot resolve deref outer-id
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+
+ @Test
+ public void testFooOuterIndirectProp() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
+ QName.create(FOO, "outer-indirect-prop")).get();
+ final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+
+ assertThat(found, isA(LeafSchemaNode.class));
+ assertEquals(QName.create(FOO, "prop"), found.getQName());
+ }
+
+ @Test
+ public void testGrpIndirect() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(QName.create(FOO, "indirect")).get();
+ final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+
+ assertThat(found, isA(LeafSchemaNode.class));
+ assertEquals(QName.create(FOO, "prop"), found.getQName());
+ }
+
+ @Test
+ public void testFooIndirect() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(QName.create(FOO, "indirect")).get();
+ final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement());
+
+ assertThat(found, isA(LeafSchemaNode.class));
+ assertEquals(QName.create(FOO, "prop"), found.getQName());
+ }
+
+ @Test
+ public void testGrpDerefNonExistent() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
+ QName.create(FOO, "deref-non-existent")).get();
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+
+ @Test
+ public void testFooDerefNonExistent() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
+ QName.create(FOO, "deref-non-existent")).get();
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+
+ @Test
+ public void testGrpNonExistentDeref() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName(
+ QName.create(FOO, "non-existent-deref")).get();
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+
+ @Test
+ public void testFooNonExistentDeref() {
+ final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName(
+ QName.create(FOO, "non-existent-deref")).get();
+ assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf,
+ ((LeafrefTypeDefinition) leaf.getType()).getPathStatement()));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.util.ut;
+
+import static org.hamcrest.Matchers.isA;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YT1050Test {
+ private static final QName SECONDARY = QName.create("yt1050", "secondary");
+ private static final QName TYPE = QName.create(SECONDARY, "type");
+ private static final QName GRP_USES = QName.create(SECONDARY, "grp-uses");
+
+ private EffectiveModelContext context;
+ private LeafSchemaNode secondaryType;
+ private LeafSchemaNode primaryType;
+ private Module module;
+
+ @Before
+ public void before() {
+ context = YangParserTestUtils.parseYangResource("/yt1050.yang");
+ module = context.getModules().iterator().next();
+
+ final ListSchemaNode grpUses = (ListSchemaNode) module.findDataChildByName(GRP_USES).get();
+ primaryType = (LeafSchemaNode) grpUses.findDataChildByName(TYPE).get();
+
+ final GroupingDefinition grp = module.getGroupings().iterator().next();
+ secondaryType = (LeafSchemaNode) ((ListSchemaNode) grp.findDataChildByName(SECONDARY).get())
+ .findDataChildByName(TYPE).get();
+ }
+
+ @Test
+ public void testFindDataSchemaNodeForRelativeXPathWithDeref() {
+ final TypeDefinition<?> typeNodeType = secondaryType.getType();
+ assertThat(typeNodeType, isA(LeafrefTypeDefinition.class));
+
+ final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, secondaryType,
+ ((LeafrefTypeDefinition) typeNodeType).getPathStatement());
+ assertSame(primaryType, found);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Additional unit test suite for {@code yang-model-util}, which are using YANG parser to construct the testing data
+ * and thus cannot live directly in that artifact.
+ */
+// FIXME: YANTTOOLS-1052: this should be eliminated
+package org.opendaylight.yangtools.yang.model.util.ut;
\ No newline at end of file
--- /dev/null
+module leafrefs {
+ yang-version 1.1;
+ namespace "leafrefs";
+ prefix "lrs";
+
+ typedef str-type {
+ type string {
+ length 1..max;
+ }
+ }
+
+ typedef int-type {
+ type int8 {
+ range min..0;
+ }
+ }
+
+ grouping grp {
+ leaf outer-id {
+ type leafref {
+ // points outside of the grouping
+ path ../../id;
+ }
+ }
+
+ leaf outer-indirect-prop {
+ type leafref {
+ path deref(../outer-id)/../prop;
+ }
+ }
+
+ leaf absolute {
+ type leafref {
+ // direct path to an instantiation
+ path /foo/id;
+ }
+ }
+
+ leaf indirect {
+ type leafref {
+ // deref through absolute to prop
+ path deref(../absolute)/../prop;
+ }
+ }
+
+ leaf non-existent-abs {
+ type leafref {
+ path /xyzzy;
+ }
+ }
+
+ leaf non-existent-rel {
+ type leafref {
+ path ../xyzzy;
+ }
+ }
+
+ leaf deref-non-existent {
+ type leafref {
+ path deref(../non-existent-rel)/../absolute;
+ }
+ }
+
+ leaf non-existent-deref {
+ type leafref {
+ path deref(../absolute)/../xyzzy;
+ }
+ }
+ }
+
+ list foo {
+ key id;
+
+ leaf id {
+ type str-type;
+ }
+
+ leaf id-copy {
+ type leafref {
+ path ../id;
+ }
+ }
+
+ leaf prop {
+ type int-type;
+ }
+
+ container bar {
+ uses grp;
+ }
+ }
+}
+
--- /dev/null
+module yt1050 {
+ yang-version 1.1;
+ namespace "yt1050";
+ prefix "yt1050";
+
+ identity target-base;
+
+ typedef target-type {
+ type identityref {
+ base target-base;
+ }
+ }
+
+ grouping grp {
+ leaf id {
+ type string;
+ }
+ leaf type {
+ type target-type;
+ }
+
+ list secondary {
+ key "id type";
+ leaf id {
+ type leafref {
+ path "/grp-uses/id";
+ }
+ }
+ leaf type {
+ type leafref {
+ path "deref(../id)/../type";
+ }
+ }
+ }
+ }
+
+ list grp-uses {
+ uses grp;
+ key "id type";
+ }
+}
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+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.YangLocationPath.UnresolvedQNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//
// which would then be passed in to a method similar to this one. In static contexts, like MD-SAL codegen,
// that feels like an overkill.
+ // FIXME: YANGTOOLS-1052: this is a static analysis util, move it to yang-model-sa
public static SchemaNode findDataSchemaNodeForRelativeXPath(final SchemaContext context, final Module module,
final SchemaNode actualSchemaNode, final PathExpression relativeXPath) {
checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
+ "for non relative Revision Aware XPath use findDataSchemaNode method");
-
- final Iterable<QName> qnamePath = resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
-
- // We do not have enough information about resolution context, hence cannot account for actions, RPCs
- // and notifications. We therefore attempt to make a best estimate, but this can still fail.
- final Optional<DataSchemaNode> pureData = context.findDataTreeChild(qnamePath);
- return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, qnamePath);
+ return resolveRelativeXPath(context, module, relativeXPath, actualSchemaNode);
}
/**
* Non conditional Revision Aware Relative XPath
* @param actualSchemaNode
* actual schema node
- * @return list of QName
+ * @return target schema node
* @throws IllegalArgumentException if any arguments are null
*/
- private static Iterable<QName> resolveRelativeXPath(final SchemaContext context, final Module module,
+ private static @Nullable SchemaNode resolveRelativeXPath(final SchemaContext context, final Module module,
final PathExpression relativeXPath, final SchemaNode actualSchemaNode) {
checkState(!relativeXPath.isAbsolute(), "Revision Aware XPath MUST be relative i.e. MUST contains ../, "
+ "for non relative Revision Aware XPath use findDataSchemaNode method");
checkState(actualSchemaNode.getPath() != null, "Schema Path reference for Leafref cannot be NULL");
- List<String> xpaths = new ArrayList<>();
- splitXPath(relativeXPath.getOriginalString(), xpaths);
+ final String orig = relativeXPath.getOriginalString();
+ return orig.startsWith("deref(") ? resolveDerefPath(context, module, actualSchemaNode, orig)
+ : findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode, doSplitXPath(orig)));
+ }
+ private static Iterable<QName> resolveRelativePath(final SchemaContext context, final Module module,
+ final SchemaNode actualSchemaNode, final List<String> steps) {
// Find out how many "parent" components there are and trim them
- final int colCount = normalizeXPath(xpaths);
- if (colCount != 0) {
- xpaths = xpaths.subList(colCount, xpaths.size());
- }
+ final int colCount = normalizeXPath(steps);
+ final List<String> xpaths = colCount == 0 ? steps : steps.subList(colCount, steps.size());
final Iterable<QName> schemaNodePath = actualSchemaNode.getPath().getPathFromRoot();
-
if (Iterables.size(schemaNodePath) - colCount >= 0) {
return Iterables.concat(Iterables.limit(schemaNodePath, Iterables.size(schemaNodePath) - colCount),
Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input)));
}
+ private static SchemaNode resolveDerefPath(final SchemaContext context, final Module module,
+ final SchemaNode actualSchemaNode, final String xpath) {
+ final int paren = xpath.indexOf(')', 6);
+ checkArgument(paren != -1, "Cannot find matching parentheses in %s", xpath);
+
+ final String derefArg = xpath.substring(6, paren);
+ // Look up the node which we need to reference
+ final SchemaNode derefTarget = findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode,
+ doSplitXPath(derefArg)));
+ checkArgument(derefTarget != null, "Cannot find deref(%s) target node %s in context of %s", derefArg,
+ actualSchemaNode);
+ checkArgument(derefTarget instanceof TypedDataSchemaNode, "deref(%s) resolved to non-typed %s", derefArg,
+ derefTarget);
+
+ // We have a deref() target, decide what to do about it
+ final TypeDefinition<?> targetType = ((TypedDataSchemaNode) derefTarget).getType();
+ if (targetType instanceof InstanceIdentifierTypeDefinition) {
+ // Static inference breaks down, we cannot determine where this points to
+ // FIXME: dedicated exception, users can recover from it, derive from IAE
+ throw new UnsupportedOperationException("Cannot infer instance-identifier reference " + targetType);
+ }
+
+ // deref() is define only for instance-identifier and leafref types, handle the latter
+ checkArgument(targetType instanceof LeafrefTypeDefinition, "Illegal target type %s", targetType);
+
+ final PathExpression targetPath = ((LeafrefTypeDefinition) targetType).getPathStatement();
+ LOG.debug("Derefencing path {}", targetPath);
+
+ final SchemaNode deref = targetPath.isAbsolute()
+ ? findTargetNode(context, actualSchemaNode,
+ ((LocationPathSteps) targetPath.getSteps()).getLocationPath())
+ : findDataSchemaNodeForRelativeXPath(context, module, actualSchemaNode, targetPath);
+ if (deref == null) {
+ LOG.debug("Path {} could not be derefenced", targetPath);
+ return null;
+ }
+
+ checkArgument(deref instanceof LeafSchemaNode, "Unexpected %s reference in %s", deref, targetPath);
+
+ final List<String> qnames = doSplitXPath(xpath.substring(paren + 1).stripLeading());
+ return findTargetNode(context, resolveRelativePath(context, module, deref, qnames));
+ }
+
+ private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final SchemaNode actualSchemaNode,
+ final YangLocationPath path) {
+ final QNameModule defaultModule = actualSchemaNode.getQName().getModule();
+ final Deque<QName> ret = new ArrayDeque<>();
+ for (Step step : path.getSteps()) {
+ if (step instanceof AxisStep) {
+ // We only support parent axis steps
+ final YangXPathAxis axis = ((AxisStep) step).getAxis();
+ checkState(axis == YangXPathAxis.PARENT, "Unexpected axis %s", axis);
+ ret.removeLast();
+ continue;
+ }
+
+ // This has to be a QNameStep
+ checkState(step instanceof QNameStep, "Unhandled step %s in %s", step, path);
+ final QName qname;
+ if (step instanceof ResolvedQNameStep) {
+ qname = ((ResolvedQNameStep) step).getQName();
+ } else if (step instanceof UnresolvedQNameStep) {
+ final AbstractQName toResolve = ((UnresolvedQNameStep) step).getQName();
+ // TODO: should handle qualified QNames, too? parser should have resolved them when we get here...
+ checkState(toResolve instanceof UnqualifiedQName, "Unhandled qname %s in %s", toResolve, path);
+ qname = QName.create(defaultModule, toResolve.getLocalName());
+ } else {
+ throw new IllegalStateException("Unhandled step " + step);
+ }
+
+ ret.addLast(qname);
+ }
+
+ return findTargetNode(context, ret);
+ }
+
+ private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final Iterable<QName> qnamePath) {
+ // We do not have enough information about resolution context, hence cannot account for actions, RPCs
+ // and notifications. We therefore attempt to make a best estimate, but this can still fail.
+ final Optional<DataSchemaNode> pureData = context.findDataTreeChild(qnamePath);
+ return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, qnamePath);
+ }
+
@VisibleForTesting
static int normalizeXPath(final List<String> xpath) {
LOG.trace("Normalize {}", xpath);
return -1;
}
- private static void splitXPath(final String xpath, final List<String> output) {
- // This is a major hack, but should do the trick for now.
- final int deref = xpath.indexOf("deref(");
- if (deref == -1) {
- doSplitXPath(xpath, output);
- return;
- }
-
- // Interpret leading part
- doSplitXPath(xpath.substring(0, deref), output);
-
- // Find matching parentheses
- final int start = deref + 6;
- final int paren = xpath.indexOf(')', start);
- checkArgument(paren != -1, "Cannot find matching parentheses in %s", xpath);
-
- // Interpret the argument
- doSplitXPath(xpath.substring(start, paren), output);
-
- // And now the last bit
- splitXPath(xpath.substring(paren + 1), output);
- }
-
- private static void doSplitXPath(final String xpath, final List<String> output) {
- SLASH_SPLITTER.split(xpath).forEach(output::add);
+ private static List<String> doSplitXPath(final String xpath) {
+ return SLASH_SPLITTER.splitToList(xpath);
}
/**
private static final Splitter SPACE_SPLITTER = Splitter.on(' ');
private static final URI NAMESPACE = URI.create("abc");
- // The idea is:
- // container baz {
- // leaf xyzzy {
- // type leafref;
- // }
- // leaf foo {
- // type string;
- // }
- // leaf bar {
- // type string;
- // }
- // }
- private static final QName FOO = QName.create(NAMESPACE, "foo");
- private static final QName BAR = QName.create(NAMESPACE, "bar");
- private static final QName BAZ = QName.create(NAMESPACE, "baz");
- private static final QName XYZZY = QName.create(NAMESPACE, "xyzzy");
-
@Mock
private SchemaContext mockSchemaContext;
@Mock
doReturn(NAMESPACE).when(mockModule).getNamespace();
doReturn(QNameModule.create(NAMESPACE)).when(mockModule).getQNameModule();
doReturn(Optional.empty()).when(mockModule).getRevision();
-
- doReturn(SchemaPath.create(true, BAZ, XYZZY)).when(schemaNode).getPath();
}
@Test
assertNull("Should be null.", SchemaContextUtil.findParentModule(mockSchemaContext, int32node));
}
- @Test
- public void testDeref() {
- PathExpression xpath = new PathExpressionImpl("deref(../foo)/../bar", false);
- assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(mockSchemaContext, mockModule, schemaNode,
- xpath));
- }
-
@Test
public void testNormalizeXPath() {
assertNormalizedPath(0, ImmutableList.of(""), "");