From 5280e8d0efe5177e5bd9fa15bfd2bdc01e222d1b Mon Sep 17 00:00:00 2001 From: "miroslav.kovac" Date: Tue, 5 Jan 2021 15:00:46 +0100 Subject: [PATCH] Reimplement SchemaContextUtil.getBaseTypeForLeafRef() This is the primary entry point to resolution of TypeDefinitions and generally inferring along the data tree. We already have required utilities in SchemaInferenceStack. Introduce SchemaInferenceStack.resolvePathExpression(), which serves the serves the same purpose, but results in a stack manipulation. This completely removes the need to perform any string-based operations, or dedicated SchemaNode lookups or anything of that kind -- removing a lot of legacy crud. JIRA: YANGTOOLS-1127 JIRA: YANGTOOLS-1229 Change-Id: I64e4f4ef2062ab2a0d143a520a53e8972cf54b26 Signed-off-by: miroslav.kovac Signed-off-by: Robert Varga --- .../data/codec/gson/JSONCodecFactory.java | 10 - .../codec/gson/JSONCodecFactorySupplier.java | 33 +- .../gson/JSONInstanceIdentifierCodec.java | 6 +- .../gson/JSONNormalizedNodeStreamWriter.java | 2 +- .../data/codec/gson/JsonParserStream.java | 54 +- .../gson/JsonStreamToNormalizedNodeTest.java | 15 +- .../src/test/resources/bug-4969/yang/bar.yang | 6 +- .../leafref/yang/augment-leafref-module.yang | 2 +- ...reXMLStreamNormalizedNodeStreamWriter.java | 20 +- .../xml/SchemaAwareXMLStreamWriterUtils.java | 11 - .../xml/SchemalessXMLStreamWriterUtils.java | 8 - .../data/codec/xml/XMLStreamWriterUtils.java | 27 +- .../yang/data/codec/xml/XmlCodecFactory.java | 10 - .../yang/data/codec/xml/XmlParserStream.java | 5 +- .../xml/XmlStringInstanceIdentifierCodec.java | 6 +- .../data/codec/xml/XmlStreamUtilsTest.java | 93 +-- .../src/main/java/module-info.java | 3 +- ...AbstractStringInstanceIdentifierCodec.java | 4 +- .../yang/data/util/ContainerSchemaNodes.java | 1 + .../util/NormalizedNodeStreamWriterStack.java | 19 +- ...XpathStringParsingPathArgumentBuilder.java | 25 +- .../data/util/codec/AbstractCodecFactory.java | 17 +- yang/yang-model-util/pom.xml | 9 + .../src/main/java/module-info.java | 1 + .../yang/model/util/LeafrefResolver.java | 30 + .../yang/model/util/PathExpressionImpl.java | 61 -- .../yang/model/util/SchemaContextUtil.java | 618 +----------------- .../yang/model/util/SchemaInferenceStack.java | 136 +++- .../yang/model/util/Bug4969Test.java | 24 - .../model/util/LeafrefStaticAnalysisTest.java | 94 ++- .../SchemaContextUtilIntegrationTest.java | 46 +- .../model/util/SchemaContextUtilTest.java | 91 --- .../yangtools/yang/model/util/YT1050Test.java | 10 +- .../yangtools/yang/model/util/YT1060Test.java | 19 +- .../yangtools/yang/model/util/YT1100Test.java | 18 +- .../yangtools/yang/model/util/YT588Test.java | 21 +- .../yangtools/yang/stmt/Bug5335Test.java | 3 +- .../yangtools/yang/stmt/RpcStmtTest.java | 5 +- 38 files changed, 476 insertions(+), 1087 deletions(-) create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/LeafrefResolver.java delete mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java delete mode 100644 yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java index e3fee0126e..aaa6730195 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java @@ -7,8 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static com.google.common.base.Verify.verifyNotNull; - import com.google.common.annotations.Beta; import java.util.List; import org.eclipse.jdt.annotation.NonNull; @@ -24,7 +22,6 @@ import org.opendaylight.yangtools.yang.data.util.codec.AbstractCodecFactory; import org.opendaylight.yangtools.yang.data.util.codec.CodecCache; import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; @@ -44,7 +41,6 @@ import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; /** * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to @@ -167,10 +163,4 @@ public abstract class JSONCodecFactory extends AbstractCodecFactory abstract JSONCodec wrapDecimalCodec(DecimalStringCodec decimalCodec); abstract JSONCodec wrapIntegerCodec(AbstractIntegerStringCodec integerCodec); - - final JSONCodec codecFor(final TypedDataSchemaNode currentNode) { - return codecFor(currentNode, type -> verifyNotNull( - SchemaContextUtil.getBaseTypeForLeafRef(type, getEffectiveModelContext(), currentNode), - "Unable to find base type for leafref node %s type %s.", currentNode, type)); - } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java index c42e4d5102..2c3dacd2fe 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactorySupplier.java @@ -15,18 +15,22 @@ import com.google.common.base.Stopwatch; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.util.codec.CodecCache; import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache; import org.opendaylight.yangtools.yang.data.util.codec.NoopCodecCache; import org.opendaylight.yangtools.yang.data.util.codec.PrecomputedCodecCache; import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,7 +75,13 @@ public enum JSONCodecFactorySupplier { final Stopwatch sw = Stopwatch.createStarted(); final LazyCodecCache> lazyCache = new LazyCodecCache<>(); final JSONCodecFactory lazy = factorySupplier.apply(key, lazyCache); - final int visitedLeaves = requestCodecsForChildren(lazy, key); + final SchemaInferenceStack stack = SchemaInferenceStack.of(key); + + int visitedLeaves = 0; + for (ModuleEffectiveStatement module : key.getModuleStatements().values()) { + visitedLeaves += codecsForChildren(lazy, stack, module); + stack.clear(); + } sw.stop(); final PrecomputedCodecCache> cache = lazyCache.toPrecomputed(); @@ -80,14 +90,19 @@ public enum JSONCodecFactorySupplier { return factorySupplier.apply(key, cache); } - private static int requestCodecsForChildren(final JSONCodecFactory lazy, final DataNodeContainer parent) { + private static int codecsForChildren(final JSONCodecFactory lazy, final SchemaInferenceStack stack, + final DataTreeAwareEffectiveStatement parent) { int ret = 0; - for (DataSchemaNode child : parent.getChildNodes()) { - if (child instanceof TypedDataSchemaNode) { - lazy.codecFor((TypedDataSchemaNode) child); + final Map> dataTree = + parent.getAll(DataTreeAwareEffectiveStatement.Namespace.class); + for (DataTreeEffectiveStatement child : dataTree.values()) { + if (child instanceof DataTreeAwareEffectiveStatement) { + stack.enterDataTree(child.argument()); + ret += codecsForChildren(lazy, stack, (DataTreeAwareEffectiveStatement) child); + stack.exit(); + } else if (child instanceof TypedDataSchemaNode) { + lazy.codecFor((TypedDataSchemaNode) child, stack); ++ret; - } else if (child instanceof DataNodeContainer) { - ret += requestCodecsForChildren(lazy, (DataNodeContainer) child); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java index 3daa055401..cf7388f7e4 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONInstanceIdentifierCodec.java @@ -21,6 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.util.LeafrefResolver; abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec implements JSONCodec { @@ -52,10 +53,11 @@ abstract class JSONInstanceIdentifierCodec extends AbstractModuleStringInstanceI } @Override - protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) { + protected final Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver, + final String value) { requireNonNull(schemaNode, "schemaNode cannot be null"); checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode"); - final JSONCodec objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode); + final JSONCodec objectJSONCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver); return objectJSONCodec.parseValue(null, value); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java index 67e4c1f1b0..a62b8029f5 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java @@ -406,7 +406,7 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt public void scalarValue(final Object value) throws IOException { final Object current = tracker.getParent(); if (current instanceof TypedDataSchemaNode) { - writeValue(value, codecs.codecFor((TypedDataSchemaNode) current)); + writeValue(value, codecs.codecFor((TypedDataSchemaNode) current, tracker)); } else if (current instanceof AnydataSchemaNode) { writeAnydataValue(value); } else { diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java index a1d2e49bb7..e6f8dfdc43 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java @@ -31,6 +31,7 @@ import java.util.Set; import javax.xml.transform.dom.DOMSource; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.XMLNamespace; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema; @@ -48,10 +49,12 @@ import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.OperationDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -72,15 +75,30 @@ public final class JsonParserStream implements Closeable, Flushable { private final JSONCodecFactory codecs; private final DataSchemaNode parentNode; + private final SchemaInferenceStack stack; + // TODO: consider class specialization to remove this field private final boolean lenient; private JsonParserStream(final NormalizedNodeStreamWriter writer, final JSONCodecFactory codecs, - final DataSchemaNode parentNode, final boolean lenient) { + final SchemaInferenceStack stack, final boolean lenient) { this.writer = requireNonNull(writer); this.codecs = requireNonNull(codecs); - this.parentNode = parentNode; + this.stack = requireNonNull(stack); this.lenient = lenient; + + if (!stack.isEmpty()) { + final EffectiveStatement parent = stack.currentStatement(); + if (parent instanceof DataSchemaNode) { + parentNode = (DataSchemaNode) parent; + } else if (parent instanceof OperationDefinition) { + parentNode = OperationAsContainer.of((OperationDefinition) parent); + } else { + throw new IllegalArgumentException("Illegal parent node " + parent); + } + } else { + parentNode = stack.getEffectiveModelContext(); + } } /** @@ -95,7 +113,8 @@ public final class JsonParserStream implements Closeable, Flushable { */ public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer, final @NonNull JSONCodecFactory codecFactory) { - return new JsonParserStream(writer, codecFactory, codecFactory.getEffectiveModelContext(), false); + return new JsonParserStream(writer, codecFactory, + SchemaInferenceStack.of(codecFactory.getEffectiveModelContext()), false); } /** @@ -109,8 +128,8 @@ public final class JsonParserStream implements Closeable, Flushable { * @throws NullPointerException if any of the arguments are null */ public static @NonNull JsonParserStream create(final @NonNull NormalizedNodeStreamWriter writer, - final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) { - return new JsonParserStream(writer, codecFactory, validateParent(parentNode), false); + final @NonNull JSONCodecFactory codecFactory, final @NonNull EffectiveStatementInference parentNode) { + return new JsonParserStream(writer, codecFactory, SchemaInferenceStack.ofInference(parentNode), false); } /** @@ -131,7 +150,8 @@ public final class JsonParserStream implements Closeable, Flushable { */ public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer, final @NonNull JSONCodecFactory codecFactory) { - return new JsonParserStream(writer, codecFactory, codecFactory.getEffectiveModelContext(), true); + return new JsonParserStream(writer, codecFactory, + SchemaInferenceStack.of(codecFactory.getEffectiveModelContext()), true); } /** @@ -151,8 +171,8 @@ public final class JsonParserStream implements Closeable, Flushable { * @throws NullPointerException if any of the arguments are null */ public static @NonNull JsonParserStream createLenient(final @NonNull NormalizedNodeStreamWriter writer, - final @NonNull JSONCodecFactory codecFactory, final @NonNull SchemaNode parentNode) { - return new JsonParserStream(writer, codecFactory, validateParent(parentNode), true); + final @NonNull JSONCodecFactory codecFactory, final @NonNull EffectiveStatementInference parentNode) { + return new JsonParserStream(writer, codecFactory, SchemaInferenceStack.ofInference(parentNode), true); } public JsonParserStream parse(final JsonReader reader) { @@ -302,13 +322,15 @@ public final class JsonParserStream implements Closeable, Flushable { "Schema for node with name %s and namespace %s does not exist at %s", localName, getCurrentNamespace(), parentSchema); - + final QName qname = childDataSchemaNodes.peekLast().getQName(); final AbstractNodeDataWithSchema newChild = ((CompositeNodeDataWithSchema) parent) .addChild(childDataSchemaNodes, ChildReusePolicy.NOOP); if (newChild instanceof AnyXmlNodeDataWithSchema) { readAnyXmlValue(in, (AnyXmlNodeDataWithSchema) newChild, jsonElementName); } else { + stack.enterDataTree(qname); read(in, newChild); + stack.exit(); } removeNamespace(); } @@ -343,7 +365,7 @@ public final class JsonParserStream implements Closeable, Flushable { private Object translateValueByType(final String value, final DataSchemaNode node) { checkArgument(node instanceof TypedDataSchemaNode); - return codecs.codecFor((TypedDataSchemaNode) node).parseValue(null, value); + return codecs.codecFor((TypedDataSchemaNode) node, stack).parseValue(null, value); } private void removeNamespace() { @@ -424,16 +446,6 @@ public final class JsonParserStream implements Closeable, Flushable { return namespaces.peek(); } - private static DataSchemaNode validateParent(final SchemaNode parent) { - if (parent instanceof DataSchemaNode) { - return (DataSchemaNode) parent; - } else if (parent instanceof OperationDefinition) { - return OperationAsContainer.of((OperationDefinition) parent); - } else { - throw new IllegalArgumentException("Illegal parent node " + requireNonNull(parent)); - } - } - @Override public void flush() throws IOException { writer.flush(); diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java index 6146779275..773d159710 100644 --- a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java +++ b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java @@ -31,7 +31,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; /** * Each test tests whether json input is correctly transformed to normalized node structure. @@ -190,8 +190,8 @@ public class JsonStreamToNormalizedNodeTest extends AbstractComplexJsonTest { final NormalizedNodeResult result = new NormalizedNodeResult(); final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); - final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get(); - final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode); + final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, + Inference.ofDataTreePath(schemaContext, CONT_1)); jsonParser.parse(new JsonReader(new StringReader(inputJson))); final NormalizedNode transformedInput = result.getResult(); assertNotNull(transformedInput); @@ -203,8 +203,8 @@ public class JsonStreamToNormalizedNodeTest extends AbstractComplexJsonTest { final NormalizedNodeResult result = new NormalizedNodeResult(); final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); - final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get(); - final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, parentNode); + final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, lhotkaCodecFactory, + Inference.ofDataTreePath(schemaContext, CONT_1)); jsonParser.parse(new JsonReader(new StringReader(inputJson))); final NormalizedNode transformedInput = result.getResult(); assertNotNull(transformedInput); @@ -216,9 +216,8 @@ public class JsonStreamToNormalizedNodeTest extends AbstractComplexJsonTest { final NormalizedNodeResult result = new NormalizedNodeResult(); final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); - final SchemaNode parentNode = schemaContext.findDataChildByName(CONT_1).get(); - final QName augmentChoice1QName = QName.create(parentNode.getQName(), "augment-choice1"); + final QName augmentChoice1QName = QName.create(CONT_1, "augment-choice1"); final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2"); final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container"); final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf"); @@ -230,7 +229,7 @@ public class JsonStreamToNormalizedNodeTest extends AbstractComplexJsonTest { final NodeIdentifier containerId = new NodeIdentifier(containerQName); final NormalizedNode cont1Normalized = - containerBuilder().withNodeIdentifier(new NodeIdentifier(parentNode.getQName())) + containerBuilder().withNodeIdentifier(new NodeIdentifier(CONT_1)) .withChild(augmentationBuilder().withNodeIdentifier(aug1Id) .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id) .withChild(augmentationBuilder().withNodeIdentifier(aug2Id) diff --git a/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang b/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang index b594d3d0a0..f90488f9dc 100644 --- a/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang +++ b/yang/yang-data-codec-gson/src/test/resources/bug-4969/yang/bar.yang @@ -20,19 +20,19 @@ module bar { typedef ref1-2 { type leafref { - path "/root/l1"; + path "/bar:root/bar:l1"; } } typedef ref2-2 { type leafref { - path "/root/l2"; + path "/bar:root/bar:l2"; } } typedef ref3-2 { type leafref { - path "/root/l3"; + path "/bar:root/bar:l3"; } } diff --git a/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang b/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang index b8b57506ba..7e9dcc2636 100644 --- a/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang +++ b/yang/yang-data-codec-gson/src/test/resources/leafref/yang/augment-leafref-module.yang @@ -7,7 +7,7 @@ module augment-leafref-module { typedef leafreftype { type leafref { - path "/cont/lf3"; + path "/auglfrfmo:cont/auglfrfmo:lf3"; } } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java index 54be0c2622..f859479751 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java @@ -17,6 +17,7 @@ import java.util.Optional; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.dom.DOMSource; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; @@ -33,7 +34,9 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; final class SchemaAwareXMLStreamNormalizedNodeStreamWriter extends XMLStreamNormalizedNodeStreamWriter implements EffectiveModelContextProvider { @@ -50,7 +53,7 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter @Override String encodeValue(final ValueWriter xmlWriter, final Object value, final TypedDataSchemaNode schemaNode) throws XMLStreamException { - return streamUtils.encodeValue(xmlWriter, schemaNode, schemaNode.getType(), value, + return streamUtils.encodeValue(xmlWriter, resolveType(schemaNode.getType()), value, schemaNode.getQName().getModule()); } @@ -60,8 +63,8 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter final Optional optAnnotation = AnnotationSchemaNode.find(streamUtils.getEffectiveModelContext(), qname); if (optAnnotation.isPresent()) { - final AnnotationSchemaNode schema = optAnnotation.get(); - return streamUtils.encodeValue(xmlWriter, schema, schema.getType(), value, qname.getModule()); + return streamUtils.encodeValue(xmlWriter, resolveType(optAnnotation.get().getType()), value, + qname.getModule()); } checkArgument(!qname.getRevision().isPresent(), "Failed to find bound annotation %s", qname); @@ -171,4 +174,15 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter void startAnydata(final NodeIdentifier name) { tracker.startAnydataNode(name); } + + private @NonNull TypeDefinition resolveType(final @NonNull TypeDefinition type) throws XMLStreamException { + if (type instanceof LeafrefTypeDefinition) { + try { + return tracker.resolveLeafref((LeafrefTypeDefinition) type); + } catch (IllegalArgumentException e) { + throw new XMLStreamException("Cannot resolved type " + type, e); + } + } + return type; + } } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java index 32ad7d5233..d619ae6d62 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java @@ -7,7 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.xml; -import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import java.util.Map.Entry; @@ -17,10 +16,6 @@ import org.opendaylight.yangtools.yang.common.XMLNamespace; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider; -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; final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils implements EffectiveModelContextProvider { private final @NonNull EffectiveModelContext schemaContext; @@ -29,12 +24,6 @@ final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils impleme this.schemaContext = requireNonNull(schemaContext); } - @Override - TypeDefinition getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) { - final TypeDefinition ret = SchemaContextUtil.getBaseTypeForLeafRef(type, schemaContext, schemaNode); - return verifyNotNull(ret, "Unable to find base type for leafref node '%s'.", schemaNode); - } - @Override String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value) throws XMLStreamException { diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java index 7563327812..2825d4bd7b 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamWriterUtils.java @@ -8,9 +8,6 @@ package org.opendaylight.yangtools.yang.data.codec.xml; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -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; final class SchemalessXMLStreamWriterUtils extends XMLStreamWriterUtils { static final SchemalessXMLStreamWriterUtils INSTANCE = new SchemalessXMLStreamWriterUtils(); @@ -19,11 +16,6 @@ final class SchemalessXMLStreamWriterUtils extends XMLStreamWriterUtils { // Hidden on purpose } - @Override - TypeDefinition getBaseTypeForLeafRef(final SchemaNode schemaNode, final LeafrefTypeDefinition type) { - return type; - } - @Override String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value) { throw new UnsupportedOperationException("Schema context not present in " + this + ", cannot serialize " diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java index 9ac5106b52..d50bbe7b88 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamWriterUtils.java @@ -16,11 +16,9 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,26 +34,6 @@ abstract class XMLStreamWriterUtils { */ private static final Set IDENTITYREF_WARNED = ConcurrentHashMap.newKeySet(); - /** - * Encode a value into a String in the context of a XML stream writer. This method assumes the start and end of - * element is emitted by the caller. - * - * @param writer XML Stream writer - * @param schemaNode Schema node that describes the value - * @param type Schema type definition - * @param value data value - * @param parent module QName owning the leaf definition - * @return String characters to be written - * @throws XMLStreamException if an encoding problem occurs - */ - String encodeValue(final @NonNull ValueWriter writer,final @NonNull SchemaNode schemaNode, - final TypeDefinition type, final @NonNull Object value, final QNameModule parent) - throws XMLStreamException { - return type instanceof LeafrefTypeDefinition - ? encodeValue(writer, getBaseTypeForLeafRef(schemaNode, (LeafrefTypeDefinition) type), value, parent) - : encodeValue(writer, type, value, parent); - } - /** * Write a value into a XML stream writer. This method assumes the start and end of element is * emitted by the caller. @@ -67,7 +45,7 @@ abstract class XMLStreamWriterUtils { * @return String characters to be written * @throws XMLStreamException if an encoding problem occurs */ - private String encodeValue(final @NonNull ValueWriter writer, final @NonNull TypeDefinition type, + String encodeValue(final @NonNull ValueWriter writer, final @NonNull TypeDefinition type, final @NonNull Object value, final QNameModule parent) throws XMLStreamException { if (type instanceof IdentityrefTypeDefinition) { return encode(writer, (IdentityrefTypeDefinition) type, value, parent); @@ -161,9 +139,6 @@ abstract class XMLStreamWriterUtils { return value.toString(); } - abstract @NonNull TypeDefinition getBaseTypeForLeafRef(SchemaNode schemaNode, - @NonNull LeafrefTypeDefinition type); - abstract String encodeInstanceIdentifier(@NonNull ValueWriter writer, YangInstanceIdentifier value) throws XMLStreamException; } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java index 18a2d4c4da..c63009f8f9 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java @@ -8,7 +8,6 @@ package org.opendaylight.yangtools.yang.data.codec.xml; -import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; @@ -26,8 +25,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.StringStringCodec; import org.opendaylight.yangtools.yang.data.util.codec.AbstractCodecFactory; import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.TypeAware; import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; @@ -47,7 +44,6 @@ import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; /** * A thread-safe factory for instantiating {@link XmlCodec}s. @@ -179,10 +175,4 @@ public final class XmlCodecFactory extends AbstractCodecFactory> { protected XmlCodec unknownCodec(final UnknownTypeDefinition type) { return NullXmlCodec.INSTANCE; } - - XmlCodec codecFor(final T currentNode) { - return codecFor(currentNode, type -> verifyNotNull( - SchemaContextUtil.getBaseTypeForLeafRef(type, getEffectiveModelContext(), currentNode), - "Unable to find base type for leafref node %s type %s.", currentNode, type)); - } } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java index 5b31988345..397dc44323 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java @@ -362,7 +362,8 @@ public final class XmlParserStream implements Closeable, Flushable { codecs.getEffectiveModelContext(), qname); if (optAnnotation.isPresent()) { final AnnotationSchemaNode schema = optAnnotation.get(); - final Object value = codecs.codecFor(schema).parseValue(in.getNamespaceContext(), attrValue); + final Object value = codecs.codecFor(schema, stack) + .parseValue(in.getNamespaceContext(), attrValue); attributes.put(schema.getQName(), value); continue; } @@ -670,7 +671,7 @@ public final class XmlParserStream implements Closeable, Flushable { checkArgument(node instanceof TypedDataSchemaNode); checkArgument(value instanceof String); - return codecs.codecFor((TypedDataSchemaNode) node).parseValue(namespaceCtx, (String) value); + return codecs.codecFor((TypedDataSchemaNode) node, stack).parseValue(namespaceCtx, (String) value); } private static AbstractNodeDataWithSchema newEntryNode(final AbstractNodeDataWithSchema parent) { diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java index 47bd2ba769..4f74ecb4f0 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java @@ -25,6 +25,7 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.util.LeafrefResolver; final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec implements XmlCodec { @@ -60,10 +61,11 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc } @Override - protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) { + protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver, + final String value) { requireNonNull(schemaNode, "schemaNode cannot be null"); checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode"); - final XmlCodec objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode); + final XmlCodec objectXmlCodec = codecFactory.codecFor((LeafSchemaNode) schemaNode, resolver); return objectXmlCodec.parseValue(getNamespaceContext(), value); } diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java index 4536f99ca8..b0fd191b3a 100644 --- a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java +++ b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java @@ -8,17 +8,15 @@ package org.opendaylight.yangtools.yang.data.codec.xml; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.XMLStreamException; @@ -31,16 +29,16 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.common.XMLNamespace; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; public class XmlStreamUtilsTest { @@ -49,7 +47,7 @@ public class XmlStreamUtilsTest { void accept(XMLStreamWriter writer) throws XMLStreamException; } - private static SchemaContext schemaContext; + private static EffectiveModelContext schemaContext; private static Module leafRefModule; @BeforeClass @@ -112,17 +110,17 @@ public class XmlStreamUtilsTest { */ @Test public void testLeafRefRelativeChaining() { - getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class); + getTargetNodeForLeafRef(StringTypeDefinition.class, "cont3", "leafname3"); } @Test public void testLeafRefRelative() { - getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class); + getTargetNodeForLeafRef(StringTypeDefinition.class, "pointToStringLeaf"); } @Test public void testLeafRefAbsoluteWithSameTarget() { - getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class); + getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "absname"); } /** @@ -132,70 +130,29 @@ public class XmlStreamUtilsTest { @Ignore @Test public void testLeafRefWithDoublePointInPath() { - getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class); + getTargetNodeForLeafRef(StringTypeDefinition.class, "lf-with-double-point-inside"); } @Test public void testLeafRefRelativeAndAbsoluteWithSameTarget() { - final TypeDefinition targetNodeForAbsname = getTargetNodeForLeafRef("absname", - InstanceIdentifierTypeDefinition.class); - final TypeDefinition targetNodeForRelname = getTargetNodeForLeafRef("relname", - InstanceIdentifierTypeDefinition.class); - assertEquals(targetNodeForAbsname, targetNodeForRelname); + assertSame(getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "absname"), + getTargetNodeForLeafRef(InstanceIdentifierTypeDefinition.class, "relname")); } - private TypeDefinition getTargetNodeForLeafRef(final String nodeName, final Class clas) { - final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName); - assertNotNull(schemaNode); - final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode); - assertNotNull(leafrefTypedef); - final TypeDefinition targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext, - schemaNode); - assertTrue("Wrong class found.", clas.isInstance(targetBaseType)); - return targetBaseType; - } - - private static Map mapPrefixed(final Iterable> prefixes) { - final Map mappedPrefixes = new HashMap<>(); - for (final Map.Entry prefix : prefixes) { - mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue()); + private static TypeDefinition getTargetNodeForLeafRef(final Class clas, final String... names) { + final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext); + stack.enterDataTree(QName.create(leafRefModule.getQNameModule(), "cont2")); + for (String name : names) { + stack.enterDataTree(QName.create(leafRefModule.getQNameModule(), name)); } - return mappedPrefixes; - } - private static QName getAttrQName(final String namespace, final String revision, final String localName, - final Optional prefix) { - if (prefix.isPresent()) { - final QName moduleQName = QName.create(namespace, revision, "module"); - final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision()); - return QName.create(module, localName); - } - return QName.create(namespace, revision, localName); - } + final EffectiveStatement leaf = stack.currentStatement(); + assertThat(leaf, instanceOf(LeafSchemaNode.class)); + final TypeDefinition> type = ((TypedDataSchemaNode) leaf).getType(); + assertThat(type, instanceOf(LeafrefTypeDefinition.class)); - private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) { - for (final DataSchemaNode childNode : module.getChildNodes()) { - if (childNode instanceof DataNodeContainer) { - LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode, - nodeName); - if (leafrefFromRecursion != null) { - return leafrefFromRecursion; - } - } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) { - final TypeDefinition leafSchemaNodeType = ((LeafSchemaNode) childNode).getType(); - if (leafSchemaNodeType instanceof LeafrefTypeDefinition) { - return (LeafSchemaNode) childNode; - } - } - } - return null; - } - - private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) { - final TypeDefinition type = schemaNode.getType(); - if (type instanceof LeafrefTypeDefinition) { - return (LeafrefTypeDefinition) type; - } - return null; + final TypeDefinition resolved = stack.resolveLeafref((LeafrefTypeDefinition) type); + assertThat(resolved, instanceOf(clas)); + return resolved; } } diff --git a/yang/yang-data-util/src/main/java/module-info.java b/yang/yang-data-util/src/main/java/module-info.java index df98a7065a..77667498f2 100644 --- a/yang/yang-data-util/src/main/java/module-info.java +++ b/yang/yang-data-util/src/main/java/module-info.java @@ -15,11 +15,12 @@ module org.opendaylight.yangtools.yang.data.util { requires transitive org.opendaylight.yangtools.yang.model.util; requires com.google.common; + requires org.opendaylight.yangtools.concepts; + requires org.opendaylight.yangtools.yang.model.api; requires org.opendaylight.yangtools.yang.model.spi; requires org.opendaylight.yangtools.util; requires org.slf4j; // Annotations requires static org.eclipse.jdt.annotation; - requires org.opendaylight.yangtools.concepts; } diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java index 02a245523f..f59236ba90 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java @@ -23,6 +23,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithV import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.util.LeafrefResolver; /** * Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a @@ -89,7 +90,8 @@ public abstract class AbstractStringInstanceIdentifierCodec extends AbstractName */ protected abstract @NonNull DataSchemaContextTree getDataContextTree(); - protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) { + protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final LeafrefResolver resolver, + final String value) { return value; } diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java index 914333a1fb..dcc87fbcbb 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerSchemaNodes.java @@ -69,6 +69,7 @@ public final class ContainerSchemaNodes { } @Override + @Deprecated public boolean isAugmenting() { return false; } diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java index 3b35e13c60..19b4a5564b 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2021 PANTHEON.tech, s.r.o. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, @@ -20,7 +21,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.yangtools.concepts.Mutable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; @@ -46,13 +46,16 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; 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.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ActionEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema; +import org.opendaylight.yangtools.yang.model.util.LeafrefResolver; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; import org.slf4j.Logger; @@ -62,7 +65,7 @@ import org.slf4j.LoggerFactory; * Utility class for tracking schema state underlying a {@link NormalizedNode} structure. */ @Beta -public final class NormalizedNodeStreamWriterStack implements Mutable { +public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeStreamWriterStack.class); private final Deque schemaStack = new ArrayDeque<>(); @@ -70,8 +73,8 @@ public final class NormalizedNodeStreamWriterStack implements Mutable { private final DataNodeContainer root; private NormalizedNodeStreamWriterStack(final EffectiveModelContext context) { - root = requireNonNull(context); dataTree = SchemaInferenceStack.of(context); + root = requireNonNull(context); } private NormalizedNodeStreamWriterStack(final SchemaInferenceStack dataTree) { @@ -169,13 +172,9 @@ public final class NormalizedNodeStreamWriterStack implements Mutable { return new NormalizedNodeStreamWriterStack(stack); } - /** - * Return a copy of this tracker's state as an {@link SchemaInferenceStack}. - * - * @return A SchemaInferenceStack - */ - public @NonNull SchemaInferenceStack toSchemaInferenceStack() { - return dataTree.copy(); + @Override + public TypeDefinition resolveLeafref(final LeafrefTypeDefinition type) { + return dataTree.resolveLeafref(type); } public Object getParent() { diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java index c1ee5c252a..d9f2f516b0 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/XpathStringParsingPathArgumentBuilder.java @@ -14,12 +14,16 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.List; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; /** * Iterator which lazily parses {@link PathArgument} from string representation. @@ -60,6 +64,7 @@ final class XpathStringParsingPathArgumentBuilder implements Builder product = new ArrayList<>(); private final AbstractStringInstanceIdentifierCodec codec; + private final SchemaInferenceStack stack; private final String data; private DataSchemaContextNode current; @@ -69,8 +74,11 @@ final class XpathStringParsingPathArgumentBuilder implements Builder nextContextNode(final QName name) { current = current.getChild(name); - checkValid(current != null, "%s is not correct schema node identifier.",name); + checkValid(current != null, "%s is not correct schema node identifier.", name); while (current.isMixin()) { product.add(current.getIdentifier()); current = current.getChild(name); } + stack.enterDataTree(name); return current; } @@ -122,10 +131,10 @@ final class XpathStringParsingPathArgumentBuilder implements Builder currentNode = nextContextNode(name); + final DataSchemaContextNode currentNode = nextContextNode(name); checkValid(currentNode.isKeyedEntry(), "Entry %s does not allow specifying predicates.", name); - ImmutableMap.Builder keyValues = ImmutableMap.builder(); + ImmutableMap.Builder keyValues = ImmutableMap.builder(); while (!allCharactersConsumed() && PRECONDITION_START == currentChar()) { skipCurrentChar(); skipWhitespaces(); @@ -150,12 +159,18 @@ final class XpathStringParsingPathArgumentBuilder implements Builder keyNode = currentNode.getChild(key); checkValid(keyNode != null, "%s is not correct schema node identifier.", key); - final Object value = codec.deserializeKeyValue(keyNode.getDataSchemaNode(), keyValue); + final Object value = codec.deserializeKeyValue(keyNode.getDataSchemaNode(), + type -> resolveLeafref(key, type), keyValue); keyValues.put(key, value); } return NodeIdentifierWithPredicates.of(name, keyValues.build()); } + private @NonNull TypeDefinition resolveLeafref(final QName qname, final LeafrefTypeDefinition type) { + final SchemaInferenceStack tmp = stack.copy(); + tmp.enterDataTree(qname); + return tmp.resolveLeafref(type); + } private PathArgument computeIdentifier(final QName name) { DataSchemaContextNode currentNode = nextContextNode(name); diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java index d5103ae6a3..64e3bdc365 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/AbstractCodecFactory.java @@ -10,7 +10,6 @@ package org.opendaylight.yangtools.yang.data.util.codec; import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; -import com.google.common.annotations.Beta; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.annotation.NonNull; @@ -40,6 +39,7 @@ import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition; import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveModelContextProvider; +import org.opendaylight.yangtools.yang.model.util.LeafrefResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,21 +53,6 @@ import org.slf4j.LoggerFactory; */ public abstract class AbstractCodecFactory> extends AbstractEffectiveModelContextProvider { - /** - * Helper interface aiding resolution of leafref chains. - */ - @Beta - @FunctionalInterface - public interface LeafrefResolver { - /** - * Resolve specified {@link LeafrefTypeDefinition}. - * - * @param type leafref definition - * @return Resolved type - */ - @NonNull TypeDefinition resolveLeafref(@NonNull LeafrefTypeDefinition type); - } - private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class); private final @NonNull CodecCache cache; diff --git a/yang/yang-model-util/pom.xml b/yang/yang-model-util/pom.xml index bbedc0eeaf..a1a2ec0112 100644 --- a/yang/yang-model-util/pom.xml +++ b/yang/yang-model-util/pom.xml @@ -30,6 +30,10 @@ org.opendaylight.yangtools yang-model-spi + + org.opendaylight.yangtools + yang-xpath-api + org.opendaylight.yangtools @@ -50,5 +54,10 @@ logback-classic test + + org.opendaylight.yangtools + yang-xpath-impl + test + diff --git a/yang/yang-model-util/src/main/java/module-info.java b/yang/yang-model-util/src/main/java/module-info.java index 1767dcc1f0..aebab93582 100644 --- a/yang/yang-model-util/src/main/java/module-info.java +++ b/yang/yang-model-util/src/main/java/module-info.java @@ -10,6 +10,7 @@ module org.opendaylight.yangtools.yang.model.util { requires transitive org.opendaylight.yangtools.yang.model.api; requires transitive org.opendaylight.yangtools.yang.repo.api; + requires transitive org.opendaylight.yangtools.yang.xpath.api; requires com.google.common; requires org.opendaylight.yangtools.yang.common; requires org.opendaylight.yangtools.yang.model.spi; diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/LeafrefResolver.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/LeafrefResolver.java new file mode 100644 index 0000000000..8fc4be9101 --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/LeafrefResolver.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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; + +import com.google.common.annotations.Beta; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; + +/** + * Helper interface aiding resolution of leafref chains. + */ +@Beta +@FunctionalInterface +public interface LeafrefResolver { + /** + * Resolve specified {@link LeafrefTypeDefinition} until a non-{@code leafref} type is found. + * + * @param type leafref definition + * @return Resolved type + * @throws NullPointerException if {@code type} is null + * @throws IllegalArgumentException if the type definition cannot be resolved + */ + @NonNull TypeDefinition resolveLeafref(@NonNull LeafrefTypeDefinition type); +} \ No newline at end of file diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java deleted file mode 100644 index 5fe85198d7..0000000000 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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; - -import static java.util.Objects.requireNonNull; - -import com.google.common.base.MoreObjects.ToStringHelper; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.yangtools.yang.model.spi.AbstractPathExpression; - -/** - * A simple XPathExpression implementation. - * - * @deprecated Users are advised to supply their own implementation of PathExpression. - */ -@Deprecated(forRemoval = true) -@NonNullByDefault -public final class PathExpressionImpl extends AbstractPathExpression { - private final @Nullable Steps steps; - private final boolean absolute; - - @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD", justification = "Non-grok on SpotBugs part") - public PathExpressionImpl(final String xpath, final boolean absolute) { - super(xpath); - this.absolute = absolute; - this.steps = null; - } - - public PathExpressionImpl(final String xpath, final Steps steps) { - super(xpath); - this.steps = requireNonNull(steps); - this.absolute = steps instanceof LocationPathSteps - && ((LocationPathSteps) steps).getLocationPath().isAbsolute(); - } - - @Override - public boolean isAbsolute() { - return absolute; - } - - @Override - public Steps getSteps() { - final Steps loc = steps; - if (loc == null) { - throw new UnsupportedOperationException("Steps have not been provided"); - } - return loc; - } - - @Override - protected ToStringHelper addToStringAttributes(final ToStringHelper helper) { - return super.addToStringAttributes(helper.add("absolute", absolute).add("steps", steps)); - } -} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java index 742dab9e2f..5f5bb05dd3 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtil.java @@ -8,66 +8,36 @@ package org.opendaylight.yangtools.yang.model.util; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Splitter; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Deque; import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; 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.ContainerLike; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode; import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.api.ModuleLike; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; 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.DerefSteps; -import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps; -import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps; 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.Submodule; -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.Step; -import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,9 +48,7 @@ import org.slf4j.LoggerFactory; */ public final class SchemaContextUtil { private static final Logger LOG = LoggerFactory.getLogger(SchemaContextUtil.class); - private static final Splitter COLON_SPLITTER = Splitter.on(':'); - private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings(); - private static final Pattern GROUPS_PATTERN = Pattern.compile("\\[(.*?)\\]"); + private SchemaContextUtil() { // Hidden on purpose @@ -111,171 +79,6 @@ public final class SchemaContextUtil { return findNodeInSchemaContext(context, prefixedPath); } - /** - * Attempt to find a DataSchemaNode based on its path from root, similar to - * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression. - * - * @param context Schema Context - * @param path Path to search for - * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present. - * @throws NullPointerException if a any argument is null or if the path contains a null element - */ - @Beta - public static SchemaNode findDataSchemaNode(final SchemaContext context, final List path) { - return findTargetNode(context, null, YangLocationPath.absolute( - path.stream().map(YangXPathAxis.CHILD::asStep).collect(Collectors.toList()))); - } - - /** - * Attempt to find a DataSchemaNode based on its path from root, similar to - * {@link #findDataSchemaNode(SchemaContext, Module, PathExpression)} without requiring an expression. - * - * @param context Schema Context - * @param path Path to search for - * @return SchemaNode from the end of the Schema Path or {@code null} if the Node is not present. - * @throws NullPointerException if a any argument is null or if the path contains a null element - */ - @Beta - public static SchemaNode findDataSchemaNode(final SchemaContext context, final QName... path) { - return findDataSchemaNode(context, Arrays.asList(path)); - } - - /** - * Method attempts to find DataSchemaNode inside of provided Schema Context - * and Yang Module accordingly to Non-conditional Revision Aware XPath. The - * specified Module MUST be present in Schema Context otherwise the - * operation would fail and return null.
- * The Revision Aware XPath MUST be specified WITHOUT the conditional - * statement (i.e. without [cond]) in path, because in this state the Schema - * Context is completely unaware of data state and will be not able to - * properly resolve XPath. If the XPath contains condition the method will - * return IllegalArgumentException.
- * If the Revision Aware XPath is correct and desired Data Schema Node is - * present in Yang module or in depending module in Schema Context the - * method will return specified Data Schema Node, otherwise the operation - * will fail and method will return null. - * - * @param context - * Schema Context - * @param module - * Yang Module - * @param nonCondXPath - * Non Conditional Revision Aware XPath - * @return Returns Data Schema Node for specified Schema Context for given - * Non-conditional Revision Aware XPath, or null if the - * DataSchemaNode is not present in Schema Context. - * @throws NullPointerException if any of the arguments is null - * @deprecated Use {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, YangLocationPath)} or - * {@link #findDataTreeSchemaNode(SchemaContext, QNameModule, PathExpression)} instead. - */ - // FIXME: This entire method is ill-defined, as the resolution process depends on where the XPath is defined -- - // notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2 - // of RFC7950. - // - // Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of - // a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed - // via a derived SchemaContext (i.e. this class would have to have a - // - // SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif) - // - // 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. - @Deprecated - public static SchemaNode findDataSchemaNode(final SchemaContext context, final Module module, - final PathExpression nonCondXPath) { - requireNonNull(context, "context"); - requireNonNull(module, "module"); - - final String strXPath = nonCondXPath.getOriginalString(); - checkArgument(strXPath.indexOf('[') == -1, "Revision Aware XPath may not contain a condition"); - if (nonCondXPath.isAbsolute()) { - return findTargetNode(context, xpathToQNamePath(context, module, strXPath)); - } - return null; - } - - @Beta - public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule, - final YangLocationPath absPath) { - checkArgument(absPath.isAbsolute(), "Unsupported relative path %s", absPath); - return findTargetNode(ctx, localModule, absPath); - } - - @Beta - public static SchemaNode findDataTreeSchemaNode(final SchemaContext ctx, final QNameModule localModule, - final PathExpression absPath) { - final Steps pathSteps = absPath.getSteps(); - if (pathSteps instanceof LocationPathSteps) { - return findDataTreeSchemaNode(ctx, localModule, ((LocationPathSteps) pathSteps).getLocationPath()); - } - - // We would need a reference schema node and no, we do not want to use SchemaContext in its SchemaNode capacity - checkArgument(!(pathSteps instanceof DerefSteps), "No reference node for steps %s", pathSteps); - - // We are missing proper API alignment, if this ever triggers - throw new IllegalStateException("Unsupported path " + pathSteps); - } - - /** - * Method attempts to find DataSchemaNode inside of provided Schema Context - * and Yang Module accordingly to Non-conditional relative Revision Aware - * XPath. The specified Module MUST be present in Schema Context otherwise - * the operation would fail and return null.
- * The relative Revision Aware XPath MUST be specified WITHOUT the - * conditional statement (i.e. without [cond]) in path, because in this - * state the Schema Context is completely unaware of data state and will be - * not able to properly resolve XPath. If the XPath contains condition the - * method will return IllegalArgumentException.
- * The Actual Schema Node MUST be specified correctly because from this - * Schema Node will search starts. If the Actual Schema Node is not correct - * the operation will simply fail, because it will be unable to find desired - * DataSchemaNode.
- * If the Revision Aware XPath doesn't have flag - * isAbsolute == false the method will throw - * IllegalArgumentException.
- * If the relative Revision Aware XPath is correct and desired Data Schema - * Node is present in Yang module or in depending module in Schema Context - * the method will return specified Data Schema Node, otherwise the - * operation will fail and method will return null. - * - * @param context - * Schema Context - * @param module - * Yang Module - * @param actualSchemaNode - * Actual Schema Node - * @param relativeXPath - * Relative Non Conditional Revision Aware XPath - * @return DataSchemaNode if is present in specified Schema Context for - * given relative Revision Aware XPath, otherwise will return - * null. - * @throws NullPointerException if any argument is null - */ - // FIXME: This entire method is ill-defined, as the resolution process depends on where the XPath is defined -- - // notably RPCs, actions and notifications modify the data tree temporarily. See sections 6.4.1 and 9.9.2 - // of RFC7950. - // - // Most notably we need to understand whether the XPath is being resolved in the data tree, or as part of - // a notification/action/RPC, as then the SchemaContext grows tentative nodes ... which could be addressed - // via a derived SchemaContext (i.e. this class would have to have a - // - // SchemaContext notificationSchemaContext(SchemaContext delegate, NotificationDefinition notif) - // - // 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 a dedicated class - 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"); - return resolveRelativeXPath(context, module, removePredicatesFromXpath(relativeXPath.getOriginalString()), - actualSchemaNode); - } - - private static String removePredicatesFromXpath(final String xpath) { - return GROUPS_PATTERN.matcher(xpath).replaceAll(""); - } - /** * Returns parent Yang Module for specified Schema Context in which Schema * Node is declared. If the Schema Node is not present in Schema Context the @@ -554,423 +357,4 @@ public final class SchemaContextUtil { return null; } - /** - * Transforms string representation of XPath to Queue of QNames. The XPath - * is split by "/" and for each part of XPath is assigned correct module in - * Schema Path.
- * If Schema Context, Parent Module or XPath string contains - * null values, the method will throws IllegalArgumentException - * - * @param context - * Schema Context - * @param parentModule - * Parent Module - * @param xpath - * XPath String - * @return return a list of QName - */ - private static List xpathToQNamePath(final SchemaContext context, final Module parentModule, - final String xpath) { - final List path = new ArrayList<>(); - for (final String pathComponent : SLASH_SPLITTER.split(xpath)) { - path.add(stringPathPartToQName(context, parentModule, pathComponent)); - } - return path; - } - - /** - * Transforms part of Prefixed Path as java String to QName.
- * If the string contains module prefix separated by ":" (i.e. - * mod:container) this module is provided from from Parent Module list of - * imports. If the Prefixed module is present in Schema Context the QName - * can be constructed.
- * If the Prefixed Path Part does not contains prefix the Parent's Module - * namespace is taken for construction of QName.
- * - * @param context Schema Context - * @param parentModule Parent Module - * @param prefixedPathPart Prefixed Path Part string - * @return QName from prefixed Path Part String. - * @throws NullPointerException if any arguments are null - */ - private static QName stringPathPartToQName(final SchemaContext context, final Module parentModule, - final String prefixedPathPart) { - requireNonNull(context, "context"); - - if (prefixedPathPart.indexOf(':') != -1) { - final Iterator prefixedName = COLON_SPLITTER.split(prefixedPathPart).iterator(); - final String modulePrefix = prefixedName.next(); - - final Module module = resolveModuleForPrefix(context, parentModule, modulePrefix); - checkArgument(module != null, "Failed to resolve xpath: no module found for prefix %s in module %s", - modulePrefix, parentModule.getName()); - - return QName.create(module.getQNameModule(), prefixedName.next()); - } - - return QName.create(parentModule.getQNameModule(), prefixedPathPart); - } - - /** - * Method will attempt to resolve and provide Module reference for specified module prefix. Each Yang module could - * contains multiple imports which MUST be associated with corresponding module prefix. The method simply looks into - * module imports and returns the module that is bounded with specified prefix. If the prefix is not present - * in module or the prefixed module is not present in specified Schema Context, the method will return {@code null}. - *
- * If String prefix is the same as prefix of the specified Module the reference to this module is returned.
- * - * @param context Schema Context - * @param module Yang Module - * @param prefix Module Prefix - * @return Module for given prefix in specified Schema Context if is present, otherwise returns null - * @throws NullPointerException if any arguments are null - */ - private static Module resolveModuleForPrefix(final SchemaContext context, final Module module, - final String prefix) { - requireNonNull(context, "context"); - - if (prefix.equals(module.getPrefix())) { - return module; - } - - for (final ModuleImport mi : module.getImports()) { - if (prefix.equals(mi.getPrefix())) { - return context.findModule(mi.getModuleName(), mi.getRevision()).orElse(null); - } - } - return null; - } - - /** - * Resolve a relative XPath into a set of QNames. - * - * @param context - * Schema Context - * @param module - * Yang Module - * @param pathStr - * xPath of leafref - * @param actualSchemaNode - * actual schema node - * @return target schema node - * @throws IllegalArgumentException if any arguments are null - */ - private static @Nullable SchemaNode resolveRelativeXPath(final SchemaContext context, final Module module, - final String pathStr, final SchemaNode actualSchemaNode) { - checkState(actualSchemaNode.getPath() != null, "Schema Path reference for Leafref cannot be NULL"); - - return pathStr.startsWith("deref(") ? resolveDerefPath(context, module, actualSchemaNode, pathStr) - : findTargetNode(context, resolveRelativePath(context, module, actualSchemaNode, - doSplitXPath(pathStr))); - } - - private static Iterable resolveRelativePath(final SchemaContext context, final Module module, - final SchemaNode actualSchemaNode, final List steps) { - // Find out how many "parent" components there are and trim them - final int colCount = normalizeXPath(steps); - final List xpaths = colCount == 0 ? steps : steps.subList(colCount, steps.size()); - - final List walkablePath = createWalkablePath(actualSchemaNode.getPath().getPathFromRoot(), - context, colCount); - - if (walkablePath.size() - colCount >= 0) { - return Iterables.concat(Iterables.limit(walkablePath, walkablePath.size() - colCount), - Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input))); - } - return Iterables.concat(walkablePath, - Iterables.transform(xpaths, input -> stringPathPartToQName(context, module, input))); - } - - /** - * Return List of qNames that are walkable using xPath. When getting a path from schema node it will return path - * with parents like CaseSchemaNode and ChoiceSchemaNode as well if they are parents of the node. We need to get - * rid of these in order to find the node that xPath is pointing to. Also we can not remove any node beyond the - * amount of "../" because we will not be able to find the correct schema node from schema context - * - * @param schemaNodePath list of qNames as a path to the leaf of type leafref - * @param context create schema context - * @param colCount amount of "../" in the xPath expression - * @return list of QNames as a path where we should be able to find referenced node - */ - private static List createWalkablePath(final Iterable schemaNodePath, final SchemaContext context, - final int colCount) { - final List indexToRemove = new ArrayList<>(); - List schemaNodePathRet = Lists.newArrayList(schemaNodePath); - for (int j = 0, i = schemaNodePathRet.size() - 1; i >= 0 && j != colCount; i--, j++) { - final SchemaNode nodeIn = findTargetNode(context, schemaNodePathRet); - if (nodeIn instanceof CaseSchemaNode || nodeIn instanceof ChoiceSchemaNode) { - indexToRemove.add(i); - j--; - } - schemaNodePathRet.remove(i); - } - schemaNodePathRet = Lists.newArrayList(schemaNodePath); - for (int i : indexToRemove) { - schemaNodePathRet.remove(i); - } - return schemaNodePathRet; - } - - 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).strip(); - // 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.getQName().getModule(), - ((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 qnames = doSplitXPath(xpath.substring(paren + 1).stripLeading()); - return findTargetNode(context, resolveRelativePath(context, module, deref, qnames)); - } - - private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final QNameModule localNamespace, - final YangLocationPath path) { - final Deque 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); - ret.addLast(resolve(((QNameStep) step).getQName(), localNamespace)); - } - - return findTargetNode(context, ret); - } - - private static @Nullable SchemaNode findTargetNode(final SchemaContext context, final Iterable 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 pureData = context.findDataTreeChild(qnamePath); - return pureData.isPresent() ? pureData.get() : findNodeInSchemaContext(context, qnamePath); - } - - private static QName resolve(final AbstractQName toResolve, final QNameModule localNamespace) { - if (toResolve instanceof QName) { - return (QName) toResolve; - } else if (toResolve instanceof UnqualifiedQName) { - return ((UnqualifiedQName) toResolve).bindTo(localNamespace); - } else { - throw new IllegalStateException("Unhandled step " + toResolve); - } - } - - @VisibleForTesting - static int normalizeXPath(final List xpath) { - LOG.trace("Normalize {}", xpath); - - // We need to make multiple passes here, as the leading XPaths as we can have "../abc/../../def", which really - // is "../../def" - while (true) { - // Next up: count leading ".." components - int leadingParents = 0; - while (true) { - if (leadingParents == xpath.size()) { - return leadingParents; - } - if (!"..".equals(xpath.get(leadingParents))) { - break; - } - - ++leadingParents; - } - - // Now let's see if there there is a '..' in the rest - final int dots = findDots(xpath, leadingParents + 1); - if (dots == -1) { - return leadingParents; - } - - xpath.remove(dots - 1); - xpath.remove(dots - 1); - LOG.trace("Next iteration {}", xpath); - } - } - - private static int findDots(final List xpath, final int startIndex) { - for (int i = startIndex; i < xpath.size(); ++i) { - if ("..".equals(xpath.get(i))) { - return i; - } - } - - return -1; - } - - private static List doSplitXPath(final String xpath) { - final List ret = new ArrayList<>(); - for (String str : SLASH_SPLITTER.split(xpath)) { - ret.add(str); - } - return ret; - } - - /** - * Extracts the base type of node on which schema node points to. If target node is again of type - * LeafrefTypeDefinition, methods will be call recursively until it reach concrete type definition. - * - * @param typeDefinition - * type of node which will be extracted - * @param schemaContext - * Schema Context - * @param schema - * Schema Node - * @return recursively found type definition this leafref is pointing to or null if the xpath is incorrect (null - * is there to preserve backwards compatibility) - */ - public static TypeDefinition getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition, - final SchemaContext schemaContext, final SchemaNode schema) { - final PathExpression pathStatement = typeDefinition.getPathStatement(); - final String pathStr = stripConditionsFromXPathString(pathStatement); - - final DataSchemaNode dataSchemaNode; - if (pathStatement.isAbsolute()) { - SchemaNode baseSchema = schema; - while (baseSchema instanceof DerivableSchemaNode) { - final Optional basePotential = ((DerivableSchemaNode) baseSchema).getOriginal(); - if (basePotential.isPresent()) { - baseSchema = basePotential.get(); - } else { - break; - } - } - - final Module parentModule = findParentModuleOfReferencingType(schemaContext, baseSchema); - dataSchemaNode = (DataSchemaNode) findTargetNode(schemaContext, - xpathToQNamePath(schemaContext, parentModule, pathStr)); - } else { - Module parentModule = findParentModule(schemaContext, schema); - dataSchemaNode = (DataSchemaNode) resolveRelativeXPath(schemaContext, parentModule, pathStr, schema); - } - - // FIXME this is just to preserve backwards compatibility since yangtools do not mind wrong leafref xpaths - // and current expected behaviour for such cases is to just use pure string - // This should throw an exception about incorrect XPath in leafref - if (dataSchemaNode == null) { - return null; - } - - final TypeDefinition targetTypeDefinition = typeDefinition(dataSchemaNode); - - if (targetTypeDefinition instanceof LeafrefTypeDefinition) { - return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode); - } - - return targetTypeDefinition; - } - - /** - * Returns base type for {@code typeDefinition} which belongs to module specified via {@code qname}. This handle - * the case when leafref type isn't specified as type substatement of leaf or leaf-list but is defined in other - * module as typedef which is then imported to referenced module. - * - *

- * Because {@code typeDefinition} is definied via typedef statement, only absolute path is meaningful. - */ - public static TypeDefinition getBaseTypeForLeafRef(final LeafrefTypeDefinition typeDefinition, - final SchemaContext schemaContext, final QName qname) { - final PathExpression pathStatement = typeDefinition.getPathStatement(); - if (!pathStatement.isAbsolute()) { - return null; - } - - final Optional parentModule = schemaContext.findModule(qname.getModule()); - checkArgument(parentModule.isPresent(), "Failed to find parent module for %s", qname); - - final DataSchemaNode dataSchemaNode = (DataSchemaNode) findTargetNode(schemaContext, - xpathToQNamePath(schemaContext, parentModule.get(), stripConditionsFromXPathString(pathStatement))); - final TypeDefinition targetTypeDefinition = typeDefinition(dataSchemaNode); - if (targetTypeDefinition instanceof LeafrefTypeDefinition) { - return getBaseTypeForLeafRef((LeafrefTypeDefinition) targetTypeDefinition, schemaContext, dataSchemaNode); - } - - return targetTypeDefinition; - } - - private static Module findParentModuleOfReferencingType(final SchemaContext schemaContext, - final SchemaNode schemaNode) { - checkArgument(schemaContext != null, "Schema Context reference cannot be NULL!"); - checkArgument(schemaNode instanceof TypedDataSchemaNode, "Unsupported node %s", schemaNode); - - TypeDefinition nodeType = ((TypedDataSchemaNode) schemaNode).getType(); - if (nodeType.getBaseType() != null) { - while (nodeType.getBaseType() != null) { - nodeType = nodeType.getBaseType(); - } - - return schemaContext.findModule(nodeType.getQName().getModule()).orElse(null); - } - - return findParentModule(schemaContext, schemaNode); - } - - private static final Pattern STRIP_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]"); - - /** - * Removes conditions from xPath pointed to target node. - * - * @param pathStatement - * xPath to target node - * @return string representation of xPath without conditions - */ - @VisibleForTesting - static String stripConditionsFromXPathString(final PathExpression pathStatement) { - return STRIP_PATTERN.matcher(pathStatement.getOriginalString()).replaceAll(""); - } - - /** - * Gets the base type of DataSchemaNode value. - * - * @param node - * a node representing DataSchemaNode - * @return concrete type definition of node value - */ - private static TypeDefinition typeDefinition(final DataSchemaNode node) { - checkArgument(node instanceof TypedDataSchemaNode, "Unhandled parameter type %s", node); - - TypeDefinition current = ((TypedDataSchemaNode) node).getType(); - // TODO: don't we have a utility for this? - TypeDefinition base = current.getBaseType(); - while (base != null) { - current = base; - base = current.getBaseType(); - } - return current; - } } diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaInferenceStack.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaInferenceStack.java index caef0311ac..40f0203c63 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaInferenceStack.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaInferenceStack.java @@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.model.util; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; +import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; @@ -26,12 +27,23 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.concepts.Mutable; +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.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider; import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.PathExpression; +import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps; +import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps; +import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference; +import org.opendaylight.yangtools.yang.model.api.TypeAware; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement; @@ -42,8 +54,15 @@ import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveStatementInference; import org.opendaylight.yangtools.yang.model.spi.DefaultSchemaTreeInference; +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.Step; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis; /** * A state tracking utility for walking {@link EffectiveModelContext}'s contents along schema/grouping namespaces. This @@ -57,7 +76,7 @@ import org.opendaylight.yangtools.yang.model.spi.DefaultSchemaTreeInference; * This class is designed for single-threaded uses and does not make any guarantees around concurrent access. */ @Beta -public final class SchemaInferenceStack implements Mutable, EffectiveModelContextProvider { +public final class SchemaInferenceStack implements Mutable, EffectiveModelContextProvider, LeafrefResolver { /** * Semantic binding of {@link EffectiveStatementInference} produced by {@link SchemaInferenceStack}. Sequence of * {@link #statementPath()} is implementation-specific. @@ -420,6 +439,121 @@ public final class SchemaInferenceStack implements Mutable, EffectiveModelContex return (DataTreeEffectiveStatement) child; } + + @Override + public TypeDefinition resolveLeafref(final LeafrefTypeDefinition type) { + final SchemaInferenceStack tmp = copy(); + + LeafrefTypeDefinition current = type; + while (true) { + final EffectiveStatement resolved = tmp.resolvePathExpression(current.getPathStatement()); + checkState(resolved instanceof TypeAware, "Unexpected result %s resultion of %s", resolved, type); + final TypeDefinition result = ((TypedDataSchemaNode) resolved).getType(); + if (result instanceof LeafrefTypeDefinition) { + checkArgument(result != type, "Resolution of %s loops back onto itself via %s", type, current); + current = (LeafrefTypeDefinition) result; + } else { + return result; + } + } + } + + /** + * Resolve a {@link PathExpression}. + * + *

+ * Note if this method throws, this stack may be in an undefined state. + * + * @param path Requested path + * @return Resolved schema tree child + * @throws NullPointerException if {@code path} is null + * @throws IllegalArgumentException if the target node cannot be found + * @throws VerifyException if path expression is invalid + */ + public @NonNull EffectiveStatement resolvePathExpression(final PathExpression path) { + final Steps steps = path.getSteps(); + if (steps instanceof LocationPathSteps) { + return resolveLocationPath(((LocationPathSteps) steps).getLocationPath()); + } else if (steps instanceof DerefSteps) { + return resolveDeref((DerefSteps) steps); + } else { + throw new VerifyException("Unhandled steps " + steps); + } + } + + private @NonNull EffectiveStatement resolveDeref(final DerefSteps deref) { + final EffectiveStatement leafRefSchemaNode = currentStatement(); + final YangLocationPath.Relative derefArg = deref.getDerefArgument(); + final EffectiveStatement derefStmt = resolveLocationPath(derefArg); + checkArgument(derefStmt != null, "Cannot find deref(%s) target node %s in context of %s", + derefArg, leafRefSchemaNode); + checkArgument(derefStmt instanceof TypedDataSchemaNode, "deref(%s) resolved to non-typed %s", derefArg, + derefStmt); + + // We have a deref() target, decide what to do about it + final TypeDefinition targetType = ((TypedDataSchemaNode) derefStmt).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 defined only for instance-identifier and leafref types, handle the latter + checkArgument(targetType instanceof LeafrefTypeDefinition, "Illegal target type %s", targetType); + + final PathExpression dereferencedLeafRefPath = ((LeafrefTypeDefinition) targetType).getPathStatement(); + EffectiveStatement derefNode = resolvePathExpression(dereferencedLeafRefPath); + checkArgument(derefStmt != null, "Can not find target node of dereferenced node %s", derefStmt); + checkArgument(derefNode instanceof LeafSchemaNode, "Unexpected %s reference in %s", deref, + dereferencedLeafRefPath); + return resolveLocationPath(deref.getRelativePath()); + } + + private @NonNull EffectiveStatement resolveLocationPath(final YangLocationPath path) { + // get the default namespace before we clear and loose our deque + final QNameModule defaultNamespace = deque.isEmpty() ? null : ((QName) deque.peek().argument()).getModule(); + if (path.isAbsolute()) { + clear(); + } + + EffectiveStatement current = null; + for (Step step : path.getSteps()) { + final YangXPathAxis axis = step.getAxis(); + switch (axis) { + case PARENT: + verify(step instanceof AxisStep, "Unexpected parent step %s", step); + try { + current = exitToDataTree(); + } catch (IllegalStateException e) { + throw new IllegalArgumentException("Illegal parent access in " + path, e); + } + break; + case CHILD: + verify(step instanceof QNameStep, "Unexpected child step %s", step); + current = enterChild((QNameStep) step, defaultNamespace); + break; + default: + throw new VerifyException("Unexpected step " + step); + } + } + + return verifyNotNull(current); + } + + private @NonNull EffectiveStatement enterChild(final QNameStep step, final QNameModule defaultNamespace) { + final AbstractQName toResolve = step.getQName(); + final QName qname; + if (toResolve instanceof QName) { + qname = (QName) toResolve; + } else if (toResolve instanceof UnqualifiedQName) { + checkArgument(defaultNamespace != null, "Can not find target module of step %s", step); + qname = ((UnqualifiedQName) toResolve).bindTo(defaultNamespace); + } else { + throw new VerifyException("Unexpected child step QName " + toResolve); + } + return enterDataTree(qname); + } + /** * Return an {@link Inference} equivalent of current state. * diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java deleted file mode 100644 index d7c7fe943b..0000000000 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/Bug4969Test.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2015 Cisco Systems, Inc. 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; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import org.opendaylight.yangtools.yang.model.api.PathExpression; - -public class Bug4969Test { - @Test - public void testRegex() { - PathExpression xpath = new PathExpressionImpl( - "nd:network[nd:network-id=current()/../network-ref]/nd:node[nd:node-id=current()/../node-ref]" - + "/termination-point/tp-id", true); - assertEquals("nd:network/nd:node/termination-point/tp-id", - SchemaContextUtil.stripConditionsFromXPathString(xpath)); - } -} diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefStaticAnalysisTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefStaticAnalysisTest.java index b651a5237a..65da97f894 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefStaticAnalysisTest.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/LeafrefStaticAnalysisTest.java @@ -7,10 +7,12 @@ */ package org.opendaylight.yangtools.yang.model.util; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import org.junit.BeforeClass; import org.junit.Test; @@ -22,7 +24,6 @@ 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.test.util.YangParserTestUtils; @@ -49,35 +50,43 @@ public class LeafrefStaticAnalysisTest { 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())); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(grp.getQName()); + stack.enterSchemaTree(QName.create(FOO, "outer-id")); + assertThrowsInvalidPath(stack, leaf); } @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()); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName()); + stack.enterSchemaTree(QName.create(FOO, "outer-id")); + final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()) + .getPathStatement()); assertThat(found, isA(LeafSchemaNode.class)); - assertEquals(SchemaPath.create(true, FOO, QName.create(FOO, "id")), found.getPath()); + assertEquals(QName.create(FOO, "id"), found.getQName()); } @Test public void testGrpOuterIndirectProp() { final LeafSchemaNode leaf = (LeafSchemaNode) grp.findDataChildByName( QName.create(FOO, "outer-indirect-prop")).get(); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(grp.getQName()); + stack.enterSchemaTree(QName.create(FOO, "outer-indirect-prop")); // Cannot resolve deref outer-id - assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf, - ((LeafrefTypeDefinition) leaf.getType()).getPathStatement())); + assertThrowsInvalidPath(stack, leaf); } @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()); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName()); + stack.enterSchemaTree(QName.create(FOO, "outer-indirect-prop")); + final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()) + .getPathStatement()); assertThat(found, isA(LeafSchemaNode.class)); assertEquals(QName.create(FOO, "prop"), found.getQName()); @@ -86,8 +95,11 @@ public class LeafrefStaticAnalysisTest { @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()); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(grp.getQName()); + stack.enterSchemaTree(QName.create(FOO, "indirect")); + final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()) + .getPathStatement()); assertThat(found, isA(LeafSchemaNode.class)); assertEquals(QName.create(FOO, "prop"), found.getQName()); @@ -96,8 +108,10 @@ public class LeafrefStaticAnalysisTest { @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()); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName()); + stack.enterSchemaTree(QName.create(FOO, "indirect")); + final SchemaNode found = (SchemaNode) stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()) + .getPathStatement()); assertThat(found, isA(LeafSchemaNode.class)); assertEquals(QName.create(FOO, "prop"), found.getQName()); @@ -107,39 +121,69 @@ public class LeafrefStaticAnalysisTest { 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())); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(grp.getQName()); + stack.enterSchemaTree(QName.create(FOO, "deref-non-existent")); + assertThrowsMissingXyzzy(stack, leaf); } @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())); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName()); + stack.enterSchemaTree(QName.create(FOO, "deref-non-existent")); + assertThrowsMissingXyzzy(stack, leaf); } @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())); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(grp.getQName()); + stack.enterSchemaTree(QName.create(FOO, "non-existent-deref")); + assertThrowsMissingXyzzy(stack, leaf); } @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())); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, foo.getQName(), bar.getQName()); + stack.enterSchemaTree(QName.create(FOO, "non-existent-deref")); + assertThrowsMissingXyzzy(stack, leaf); } @Test public void testNonExistentRelativeXpath() { final LeafSchemaNode leaf = (LeafSchemaNode) bar.findDataChildByName( QName.create(FOO, "indirect-with-current")).get(); - assertNull(SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf, - ((LeafrefTypeDefinition) leaf.getType()).getPathStatement())); + final SchemaInferenceStack stack = SchemaInferenceStack.ofDataTreePath(context, + foo.getQName(), bar.getQName(), QName.create(FOO, "indirect-with-current")); + assertThrowsMissingChild(stack, leaf, "(leafrefs)n"); + } + + private static void assertThrowsInvalidPath(final SchemaInferenceStack stack, final LeafSchemaNode leaf) { + final IllegalArgumentException ex = assertThrowsIAE(stack, leaf); + assertThat(ex.getMessage(), startsWith("Illegal parent access in ")); + final Throwable cause = ex.getCause(); + assertThat(cause, instanceOf(IllegalStateException.class)); + assertEquals("Unexpected current GroupingEffectiveStatementImpl[qname=(leafrefs)grp]", cause.getMessage()); + } + + private static void assertThrowsMissingXyzzy(final SchemaInferenceStack stack, final LeafSchemaNode leaf) { + assertThrowsMissingChild(stack, leaf, "(leafrefs)xyzzy"); + } + + private static void assertThrowsMissingChild(final SchemaInferenceStack stack, final LeafSchemaNode leaf, + final String childName) { + assertEquals("Data tree child " + childName + " not present", assertThrowsIAE(stack, leaf).getMessage()); + } + + private static IllegalArgumentException assertThrowsIAE(final SchemaInferenceStack stack, + final LeafSchemaNode leaf) { + return assertThrows(IllegalArgumentException.class, + () -> stack.resolvePathExpression(((LeafrefTypeDefinition) leaf.getType()).getPathStatement())); } } diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilIntegrationTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilIntegrationTest.java index 7676fad6f3..644dd18aac 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilIntegrationTest.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilIntegrationTest.java @@ -10,6 +10,9 @@ package org.opendaylight.yangtools.yang.model.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import org.junit.BeforeClass; import org.junit.Test; @@ -20,19 +23,22 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; 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.test.util.YangParserTestUtils; +import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis; public class SchemaContextUtilIntegrationTest { - private static SchemaContext context; + private static EffectiveModelContext context; private static Module myModule; @BeforeClass @@ -286,29 +292,33 @@ public class SchemaContextUtilIntegrationTest { final Module importedModule = context.findModule(XMLNamespace.of("uri:imported-module"), Revision.of("2014-10-07")).get(); - final SchemaNode testNode = ((ContainerSchemaNode) importedModule.getDataChildByName(QName.create( - importedModule.getQNameModule(), "my-imported-container"))).getDataChildByName(QName.create( - importedModule.getQNameModule(), "my-imported-leaf")); + final QName myImportedContainer = QName.create(importedModule.getQNameModule(), "my-imported-container"); + final QName myImportedLeaf = QName.create(importedModule.getQNameModule(), "my-imported-leaf"); - final PathExpression xpath = new PathExpressionImpl("imp:my-imported-container/imp:my-imported-leaf", true); + final SchemaNode testNode = ((ContainerSchemaNode) importedModule.getDataChildByName(myImportedContainer)) + .getDataChildByName(myImportedLeaf); - final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, myModule, xpath); - assertNotNull(foundNode); - assertEquals(testNode, foundNode); + final PathExpression expr = mock(PathExpression.class); + doReturn(true).when(expr).isAbsolute(); + doReturn(new LocationPathSteps(YangLocationPath.absolute( + YangXPathAxis.CHILD.asStep(myImportedContainer), YangXPathAxis.CHILD.asStep(myImportedLeaf)))) + .when(expr).getSteps(); + + assertEquals(testNode, SchemaInferenceStack.of(context).resolvePathExpression(expr)); } @Test public void findDataSchemaNodeTest2() { - final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping"); - final SchemaNode testNode = grouping.getDataChildByName(QName.create(myModule.getQNameModule(), - "my-leaf-in-gouping2")); - - final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true); + final QName myLeafInGrouping2 = QName.create(myModule.getQNameModule(), "my-leaf-in-gouping2"); + final PathExpression expr = mock(PathExpression.class); + doReturn(true).when(expr).isAbsolute(); + doReturn(new LocationPathSteps(YangLocationPath.relative(YangXPathAxis.CHILD.asStep(myLeafInGrouping2)))) + .when(expr).getSteps(); - final SchemaNode foundNode = SchemaContextUtil.findDataSchemaNode(context, myModule, xpath); - - assertNotNull(foundNode); - assertEquals(testNode, foundNode); + final GroupingDefinition grouping = getGroupingByName(myModule, "my-grouping"); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + assertSame(grouping, stack.enterGrouping(grouping.getQName())); + assertEquals(grouping.getDataChildByName(myLeafInGrouping2), stack.resolvePathExpression(expr)); } @Test diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java index be19429bea..5f1682578e 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/SchemaContextUtilTest.java @@ -7,7 +7,6 @@ */ package org.opendaylight.yangtools.yang.model.util; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -15,12 +14,8 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Optional; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -30,11 +25,9 @@ import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.common.XMLNamespace; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.PathExpression; 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.ri.type.BaseTypes; @RunWith(MockitoJUnitRunner.StrictStubs.class) public class SchemaContextUtilTest { @@ -49,72 +42,16 @@ public class SchemaContextUtilTest { public SchemaNode schemaNode; @Test - @Ignore - // FIXME: YANGTOOLS-1127: rewrite this test in terms of a real PathExpression public void testFindDummyData() { doReturn(Optional.empty()).when(mockSchemaContext).findModule(any(QNameModule.class)); - doReturn(Optional.empty()).when(mockSchemaContext).findDataTreeChild(any(Iterable.class)); - - doReturn("test").when(mockModule).getName(); - doReturn("test").when(mockModule).getPrefix(); - doReturn(QNameModule.create(NAMESPACE)).when(mockModule).getQNameModule(); QName qname = QName.create("namespace", "localname"); SchemaPath schemaPath = SchemaPath.create(Collections.singletonList(qname), true); assertNull("Should be null. Module TestQName not found", SchemaContextUtil.findDataSchemaNode(mockSchemaContext, schemaPath)); - PathExpression xpath = new PathExpressionImpl("/test:bookstore/test:book/test:title", true); - assertNull("Should be null. Module bookstore not found", - SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xpath)); - - - final PathExpression xPath = new PathExpressionImpl("/bookstore/book/title", true); - assertEquals("Should be null. Module bookstore not found", null, - SchemaContextUtil.findDataSchemaNode(mockSchemaContext, mockModule, xPath)); - - SchemaNode int32node = BaseTypes.int32Type(); - PathExpression xpathRelative = new PathExpressionImpl("../prefix", false); - assertNull("Should be null, Module prefix not found", - SchemaContextUtil.findDataSchemaNodeForRelativeXPath( - mockSchemaContext, mockModule, int32node, xpathRelative)); - assertNull("Should be null. Module TestQName not found", SchemaContextUtil.findNodeInSchemaContext(mockSchemaContext, Collections.singleton(qname))); - - assertNull("Should be null.", SchemaContextUtil.findParentModule(mockSchemaContext, int32node)); - } - - @Test - public void findDataSchemaNodeFromXPathIllegalArgumentTest() { - assertThrows(NullPointerException.class, - () -> SchemaContextUtil.findDataSchemaNode(mock(SchemaContext.class), mock(Module.class), null)); - } - - @Test - public void findDataSchemaNodeFromXPathIllegalArgumentTest2() { - final SchemaContext mockContext = mock(SchemaContext.class); - final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true); - - assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(mockContext, null, xpath)); - } - - @Test - public void findDataSchemaNodeFromXPathIllegalArgumentTest3() { - final Module module = mock(Module.class); - final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", true); - - assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(null, module, xpath)); - } - - @Test - public void findDataSchemaNodeFromXPathIllegalArgumentTest4() { - final SchemaContext mockContext = mock(SchemaContext.class); - final Module module = mock(Module.class); - final PathExpression xpath = new PathExpressionImpl("my:my-grouping[@con='NULL']/my:my-leaf-in-gouping2", true); - - assertThrows(IllegalArgumentException.class, - () -> SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath)); } @Test @@ -140,32 +77,4 @@ public class SchemaContextUtilTest { assertThrows(NullPointerException.class, () -> SchemaContextUtil.findDataSchemaNode(null, SchemaPath.create(true, QName.create(XMLNamespace.of("uri:my-module"), Revision.of("2014-10-07"), "foo")))); } - - @Test - public void findDataSchemaNodeFromXPathNullTest2() { - final SchemaContext mockContext = mock(SchemaContext.class); - final Module module = mock(Module.class); - final PathExpression xpath = new PathExpressionImpl("my:my-grouping/my:my-leaf-in-gouping2", false); - - assertNull(SchemaContextUtil.findDataSchemaNode(mockContext, module, xpath)); - } - - @Test - public void testNormalizeXPath() { - assertNormalizedPath(0, ImmutableList.of(""), ""); - assertNormalizedPath(0, ImmutableList.of("a"), "a"); - assertNormalizedPath(0, ImmutableList.of("a", "b"), "a b"); - assertNormalizedPath(1, ImmutableList.of("..", "b"), ".. b"); - assertNormalizedPath(0, ImmutableList.of(), "a .."); - assertNormalizedPath(0, ImmutableList.of("b"), "a .. b"); - assertNormalizedPath(2, ImmutableList.of("..", "..", "a", "c"), ".. .. a b .. c"); - assertNormalizedPath(3, ImmutableList.of("..", "..", "..", "b"), ".. .. a .. .. b"); - } - - private static void assertNormalizedPath(final int expectedLead, final List expectedList, - final String input) { - final List list = new ArrayList<>(SPACE_SPLITTER.splitToList(input)); - assertEquals(expectedLead, SchemaContextUtil.normalizeXPath(list)); - assertEquals(expectedList, list); - } } diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1050Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1050Test.java index cb124aa3e5..6d5092e32a 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1050Test.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1050Test.java @@ -19,8 +19,8 @@ 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.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; @@ -52,8 +52,12 @@ public class YT1050Test { final TypeDefinition typeNodeType = secondaryType.getType(); assertThat(typeNodeType, isA(LeafrefTypeDefinition.class)); - final SchemaNode found = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, secondaryType, - ((LeafrefTypeDefinition) typeNodeType).getPathStatement()); + final SchemaInferenceStack stack = SchemaInferenceStack.of(context); + stack.enterGrouping(QName.create(module.getQNameModule(), "grp")); + stack.enterSchemaTree(QName.create(module.getQNameModule(), "secondary")); + stack.enterSchemaTree(secondaryType.getQName()); + final EffectiveStatement found = stack.resolvePathExpression(((LeafrefTypeDefinition) typeNodeType) + .getPathStatement()); assertSame(primaryType, found); } } diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1060Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1060Test.java index 693884dc25..2e274d42b5 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1060Test.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1060Test.java @@ -10,12 +10,14 @@ package org.opendaylight.yangtools.yang.model.util; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.XMLNamespace; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; @@ -23,8 +25,7 @@ import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.PathExpression; import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps; import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath; @@ -58,10 +59,14 @@ public class YT1060Test { } @Test - public void testFindDataSchemaNode() { - final SchemaNode found = SchemaContextUtil.findDataTreeSchemaNode(context, CONT.getModule(), path); - assertThat(found, isA(LeafSchemaNode.class)); - assertEquals(SchemaPath.create(true, QName.create("imported", "root"), QName.create("imported", "leaf1")), - found.getPath()); + public void testFindDataSchemaNodeAbsolutePathImportedModule() { + final EffectiveStatement foundStmt = SchemaInferenceStack.ofDataTreePath(context, CONT, LEAF1) + .resolvePathExpression(path); + assertThat(foundStmt, isA(LeafSchemaNode.class)); + assertEquals(QName.create(XMLNamespace.of("imported"), "leaf1"), ((LeafSchemaNode) foundStmt).getQName()); + + // since this is absolute path with prefixes stack should be able to resolve it from any state + final EffectiveStatement foundStmtSecond = SchemaInferenceStack.of(context).resolvePathExpression(path); + assertSame(foundStmt, foundStmtSecond); } } diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1100Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1100Test.java index 9de18c8594..5a4de9514a 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1100Test.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1100Test.java @@ -19,8 +19,8 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.PathExpression; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition; import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; @@ -31,18 +31,22 @@ public class YT1100Test { final EffectiveModelContext context = YangParserTestUtils.parseYangResource("/yt1100.yang"); final Module module = context.findModule("yt1100").orElseThrow(); final QNameModule qnm = module.getQNameModule(); - final DataSchemaNode leaf = module.findDataTreeChild( - QName.create(qnm, "foo"), QName.create(qnm, "scheduler-node"), QName.create(qnm, "child-scheduler-nodes"), - QName.create(qnm, "name")).orElseThrow(); + final QName foo = QName.create(qnm, "foo"); + final QName schedulerNode = QName.create(qnm, "scheduler-node"); + final QName childSchedulerNodes = QName.create(qnm, "child-scheduler-nodes"); + final QName name = QName.create(qnm, "name"); + final DataSchemaNode leaf = module.findDataTreeChild(foo, schedulerNode, childSchedulerNodes, name) + .orElseThrow(); assertThat(leaf, instanceOf(LeafSchemaNode.class)); final TypeDefinition type = ((LeafSchemaNode) leaf).getType(); assertThat(type, instanceOf(LeafrefTypeDefinition.class)); final PathExpression leafref = ((LeafrefTypeDefinition) type).getPathStatement(); - final SchemaNode ref = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(context, module, leaf, leafref); - assertThat(ref, instanceOf(LeafSchemaNode.class)); - final LeafSchemaNode targetLeaf = (LeafSchemaNode) ref; + final EffectiveStatement resolvedLeafRef = SchemaInferenceStack.ofDataTreePath( + context, foo, schedulerNode,childSchedulerNodes, name).resolvePathExpression(leafref); + assertThat(resolvedLeafRef, instanceOf(LeafSchemaNode.class)); + final LeafSchemaNode targetLeaf = (LeafSchemaNode) resolvedLeafRef; assertEquals(QName.create(qnm, "name"), targetLeaf.getQName()); assertThat(targetLeaf.getType(), instanceOf(StringTypeDefinition.class)); } diff --git a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT588Test.java b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT588Test.java index c75b93ad59..429ac32d88 100644 --- a/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT588Test.java +++ b/yang/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT588Test.java @@ -12,10 +12,11 @@ import static org.hamcrest.Matchers.isA; 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.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; @@ -26,8 +27,8 @@ public class YT588Test { private static final String REV = "2016-03-01"; @Test - public void test() throws Exception { - SchemaContext context = YangParserTestUtils.parseYangResource("/yt588.yang"); + public void test() { + EffectiveModelContext context = YangParserTestUtils.parseYangResource("/yt588.yang"); QName root = QName.create(NS, REV, "root"); QName leafRef2 = QName.create(NS, REV, "leaf-ref-2"); @@ -45,12 +46,12 @@ public class YT588Test { assertThat(leafRefNode.getType(), isA(LeafrefTypeDefinition.class)); assertThat(leafRefNode2.getType(), isA(LeafrefTypeDefinition.class)); - TypeDefinition baseTypeForLeafRef = SchemaContextUtil.getBaseTypeForLeafRef( - (LeafrefTypeDefinition) leafRefNode.getType(), context, leafRefNode); - TypeDefinition baseTypeForLeafRef2 = SchemaContextUtil.getBaseTypeForLeafRef( - (LeafrefTypeDefinition) leafRefNode2.getType(), context, leafRefNode2); + EffectiveStatement found = SchemaInferenceStack.ofDataTreePath(context, root, conGrp, leafRef) + .resolvePathExpression(((LeafrefTypeDefinition) leafRefNode.getType()).getPathStatement()); + assertThat(((TypedDataSchemaNode)found).getType(), isA(BinaryTypeDefinition.class)); - assertThat(baseTypeForLeafRef, isA(BinaryTypeDefinition.class)); - assertThat(baseTypeForLeafRef2, isA(Int16TypeDefinition.class)); + found = SchemaInferenceStack.ofDataTreePath(context, root, leafRef2) + .resolvePathExpression(((LeafrefTypeDefinition) leafRefNode2.getType()).getPathStatement()); + assertThat(((TypedDataSchemaNode)found).getType(), isA(Int16TypeDefinition.class)); } } diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java index d9b63b792e..dd7f258dd1 100644 --- a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java +++ b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/Bug5335Test.java @@ -17,7 +17,6 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; @@ -89,7 +88,7 @@ public class Bug5335Test { @Test public void correctTest4() throws Exception { - final SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-4"); + final EffectiveModelContext context = StmtTestUtils.parseYangSources("/bugs/bug5335/correct/case-4"); final DataSchemaNode mandatoryLeaf = context.findDataTreeChild(ROOT, NON_PRESENCE_CONTAINER_F, MANDATORY_LEAF_F) .orElse(null); assertThat(mandatoryLeaf, instanceOf(LeafSchemaNode.class)); diff --git a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java index 6d308571d7..50fcda68d9 100644 --- a/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java +++ b/yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/RpcStmtTest.java @@ -5,7 +5,6 @@ * 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.stmt; import static org.junit.Assert.assertEquals; @@ -21,6 +20,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode; @@ -32,10 +32,9 @@ import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors; import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; public class RpcStmtTest { - @Test public void rpcTest() throws ReactorException { - final SchemaContext result = RFC7950Reactors.defaultReactor().newBuild() + final EffectiveModelContext result = RFC7950Reactors.defaultReactor().newBuild() .addSource(sourceForResource("/model/baz.yang")) .addSource(sourceForResource("/model/bar.yang")) .addSource(sourceForResource("/rpc-stmt-test/foo.yang")) -- 2.36.6