From 1f2a1155b4ea207bdea255b794beb0b4d0ba4971 Mon Sep 17 00:00:00 2001 From: Peter Suna Date: Thu, 26 May 2022 11:58:49 +0200 Subject: [PATCH] Fix yang-data extension definition The definition on ietf-restconf's yang-data is being rather exploited in the wild, with a license to contain choices and anydata. These are essentially allowed by other tooling, hence we need to also relax our requirements. This implies we cannot really expose the container directly, but rather have to implement DataNodeContainer instead -- with the implied API breakage. This is fine due to @Beta-level contract of all interfaces involved. Since we are breaking semantics here, also ditch the notion that the yang-data argument can be parsed into a QName and instead reuse the schema tree child's QName as the value required by SchemaNode contract. JIRA: YANGTOOLS-1443 Change-Id: I3f78f71ea2980bf84f409d1ec89979e9e7b200ae Signed-off-by: Peter Suna Signed-off-by: Robert Varga (cherry picked from commit 6dec3b24ef9055b777e7ea09b2006a1f867da827) --- .../model/api/YangDataEffectiveStatement.java | 9 -- .../rfc8040/model/api/YangDataSchemaNode.java | 14 +-- .../parser/YangDataArgumentNamespace.java | 27 ------ .../YangDataEffectiveStatementImpl.java | 53 +++++------ .../parser/YangDataStatementSupport.java | 93 ++++++++++++++----- .../rfc8040/parser/AbstractYangDataTest.java | 1 - .../yangtools/rfc8040/parser/YT1443Test.java | 26 ++++++ .../rfc8040/parser/YangDataExtensionTest.java | 58 ++++++------ .../src/test/resources/yt1443.yang | 11 +++ .../yang/parser/impl/DefaultReactors.java | 2 - 10 files changed, 167 insertions(+), 127 deletions(-) delete mode 100644 parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataArgumentNamespace.java create mode 100644 parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1443Test.java create mode 100644 parser/rfc8040-parser-support/src/test/resources/yt1443.yang diff --git a/model/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java b/model/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java index b10ebb7c35..e437cf2135 100644 --- a/model/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java +++ b/model/rfc8040-model-api/src/main/java/org/opendaylight/yangtools/rfc8040/model/api/YangDataEffectiveStatement.java @@ -8,9 +8,7 @@ package org.opendaylight.yangtools.rfc8040.model.api; import com.google.common.annotations.Beta; -import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; -import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.UnknownEffectiveStatement; @@ -25,11 +23,4 @@ public interface YangDataEffectiveStatement extends UnknownEffectiveStatementRFC8040. This statement must appear as a top-level - * statement, otherwise it is ignored and does not appear in the final schema context. It must contain exactly one - * top-level container node (directly or indirectly via a uses statement). + * statement, otherwise it is ignored and does not appear in the final schema context. */ @Beta -public interface YangDataSchemaNode extends UnknownSchemaNode { - /** - * Returns container schema node container within this yang-data definition. - * - * @return container schema node - */ - ContainerSchemaNode getContainerSchemaNode(); - +public interface YangDataSchemaNode extends UnknownSchemaNode, DataNodeContainer { @Override YangDataEffectiveStatement asEffectiveStatement(); } diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataArgumentNamespace.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataArgumentNamespace.java deleted file mode 100644 index c05187235e..0000000000 --- a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataArgumentNamespace.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020 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.rfc8040.parser; - -import com.google.common.annotations.Beta; -import org.eclipse.jdt.annotation.NonNull; -import org.opendaylight.yangtools.yang.common.Empty; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour; -import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace; - -/** - * Namespace for remembering the {@code yang-data} argument's QName. - */ -@Beta -// FIXME: We should not be needing this namespace, as yang-data's argument is not documented anywhere to be compatible -// with 'identifier', hence we cannot safely form a QName. -public interface YangDataArgumentNamespace extends ParserNamespace { - NamespaceBehaviour BEHAVIOUR = - NamespaceBehaviour.statementLocal(YangDataArgumentNamespace.class); - -} diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java index 3b08371514..13440a8367 100644 --- a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java +++ b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataEffectiveStatementImpl.java @@ -19,45 +19,34 @@ import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement; import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode; import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace; -import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; import org.opendaylight.yangtools.yang.model.spi.meta.AbstractEffectiveUnknownSchmemaNode; +import org.opendaylight.yangtools.yang.model.spi.meta.EffectiveStatementMixins.DataNodeContainerMixin; import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; @Beta final class YangDataEffectiveStatementImpl extends AbstractEffectiveUnknownSchmemaNode - implements YangDataEffectiveStatement, YangDataSchemaNode { - private final @NonNull QName argumentQName; - private final @NonNull ContainerEffectiveStatement container; + implements YangDataEffectiveStatement, YangDataSchemaNode, DataNodeContainerMixin { + private final @NonNull DataSchemaNode child; YangDataEffectiveStatementImpl(final Current stmt, - final ImmutableList> substatements, final QName qname) { + final ImmutableList> substatements, final DataSchemaNode child) { super(stmt.declared(), stmt.argument(), stmt.history(), substatements); - argumentQName = requireNonNull(qname); - - container = findFirstEffectiveSubstatement(ContainerEffectiveStatement.class).get(); - - // TODO: this is strong binding of two API contracts. Unfortunately ContainerEffectiveStatement design is - // incomplete. - verify(container instanceof ContainerSchemaNode, "Incompatible container %s", container); + this.child = requireNonNull(child); } @Override public QName getQName() { - return argumentQName; - } - - @Override - public ContainerEffectiveStatement getContainer() { - return container; + return child.getQName(); } @Override - public ContainerSchemaNode getContainerSchemaNode() { - // Verified in the constructor - return (ContainerSchemaNode) container; + public DataSchemaNode dataChildByName(final QName name) { + return name.equals(child.getQName()) ? child : null; } @Override @@ -68,11 +57,23 @@ final class YangDataEffectiveStatementImpl extends AbstractEffectiveUnknownSchme @Override protected > Optional> getNamespaceContents( final Class namespace) { - if (SchemaTreeNamespace.class.equals(namespace) || DataTreeNamespace.class.equals(namespace)) { - @SuppressWarnings("unchecked") - final Map ns = (Map)Map.of(container.argument(), container); - return Optional.of(ns); + if (SchemaTreeNamespace.class.equals(namespace)) { + return castChild(); + } + if (DataTreeNamespace.class.equals(namespace)) { + if (child instanceof DataTreeEffectiveStatement) { + return castChild(); + } + + // A schema tree statement which *has to* know about data tree -- just forward it + verify(child instanceof DataTreeAwareEffectiveStatement, "Unexpected child %s", child); + return Optional.of(((DataTreeAwareEffectiveStatement) child).getAll(namespace)); } return super.getNamespaceContents(namespace); } + + @SuppressWarnings("unchecked") + private Optional> castChild() { + return Optional.of((Map) Map.of(child.getQName(), child)); + } } diff --git a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java index 6c18978c6c..7b316290cc 100644 --- a/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java +++ b/parser/rfc8040-parser-support/src/main/java/org/opendaylight/yangtools/rfc8040/parser/YangDataStatementSupport.java @@ -7,20 +7,22 @@ */ package org.opendaylight.yangtools.rfc8040.parser; -import static com.google.common.base.Verify.verifyNotNull; +import static com.google.common.base.Verify.verify; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; +import java.util.stream.Collectors; import org.opendaylight.yangtools.rfc8040.model.api.YangDataEffectiveStatement; import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatement; import org.opendaylight.yangtools.rfc8040.model.api.YangDataStatements; -import org.opendaylight.yangtools.yang.common.Empty; -import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +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.SchemaTreeEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement; import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStringStatementSupport; @@ -29,16 +31,39 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current; import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException; import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; -import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; @Beta public final class YangDataStatementSupport extends AbstractStringStatementSupport { + // As per RFC8040 page 81: + // + // The substatements of this extension MUST follow the + // 'data-def-stmt' rule in the YANG ABNF. + // + // As per RFC7950 page 185: + // + // data-def-stmt = container-stmt / + // leaf-stmt / + // leaf-list-stmt / + // list-stmt / + // choice-stmt / + // anydata-stmt / + // anyxml-stmt / + // uses-stmt + // + // The cardinality is not exactly constrained, but the entirety of substatements are required to resolve to a single + // XML document (page 80). This is enforced when we arrive at full declaration. private static final SubstatementValidator VALIDATOR = SubstatementValidator.builder(YangDataStatements.YANG_DATA) - .addMandatory(YangStmtMapping.CONTAINER) - .addOptional(YangStmtMapping.USES) + .addAny(YangStmtMapping.CONTAINER) + .addAny(YangStmtMapping.LEAF) + .addAny(YangStmtMapping.LEAF_LIST) + .addAny(YangStmtMapping.LIST) + .addAny(YangStmtMapping.CHOICE) + .addAny(YangStmtMapping.ANYDATA) + .addAny(YangStmtMapping.ANYXML) + .addAny(YangStmtMapping.USES) .build(); public YangDataStatementSupport(final YangParserConfiguration config) { @@ -56,10 +81,13 @@ public final class YangDataStatementSupport @Override public void onFullDefinitionDeclared(final Mutable ctx) { - // Parse and populate our argument to be picked up when we build the effective statement - final String argument = SourceException.throwIfNull(ctx.argument(), ctx, "yang-data requires an argument"); - final QName qname = StmtContextUtils.parseIdentifier(ctx, argument); - ctx.addToNs(YangDataArgumentNamespace.class, Empty.value(), qname); + // If we are declared in an illegal place, this becomes a no-op + if (!ctx.isSupportedToBuildEffective()) { + return; + } + + // Run SubstatementValidator-based validation first + super.onFullDefinitionDeclared(ctx); // Support for 'operations' container semantics. For this we need to recognize when the model at hand matches // RFC8040 ietf-restconf module. In ordered to do that we hook onto this particular definition: @@ -69,7 +97,7 @@ public final class YangDataStatementSupport // } // // If we find it, we hook an inference action which performs the next step when the module is fully declared. - if (ctx.isSupportedToBuildEffective() && "yang-api".equals(ctx.argument())) { + if ("yang-api".equals(ctx.argument())) { final var stmts = ctx.declaredSubstatements(); if (stmts.size() == 1) { final var stmt = stmts.iterator().next(); @@ -107,19 +135,36 @@ public final class YangDataStatementSupport @Override protected YangDataEffectiveStatement createEffective(final Current stmt, final ImmutableList> substatements) { - // So now we need to deal with effective validation. The requirement is that: - // It MUST contain data definition statements - // that result in exactly one container data node definition. - final long dataDefs = substatements.stream().filter(DataTreeEffectiveStatement.class::isInstance).count(); - if (dataDefs == 0) { - throw new MissingSubstatementException("yang-data requires exactly one container", stmt.sourceReference()); - } - if (dataDefs > 1) { - throw new InvalidSubstatementException(stmt, - "yang-data requires exactly one data definition node, found %s", dataDefs); - } + // RFC8040 page 80 requires that: + // It MUST contain data definition statements + // that result in exactly one container data node definition. + // An instance of a YANG data template can thus be translated + // into an XML instance document, whose top-level element + // corresponds to the top-level container. + // + // We validate this additional constraint when we arrive at the effective model, with the view that + // 'container data node definition' is really meant to say 'XML element'. + // + // This really boils down to the requirement to have a single schema tree substatement, which needs to either + // be a data tree statement or a choice statement. + final var schemaSub = substatements.stream() + .filter(SchemaTreeEffectiveStatement.class::isInstance) + .map(SchemaTreeEffectiveStatement.class::cast) + .collect(Collectors.toUnmodifiableList()); + final var child = switch (schemaSub.size()) { + case 0 -> throw new MissingSubstatementException(stmt, "yang-data requires at least one substatement"); + case 1 -> { + final SchemaTreeEffectiveStatement substmt = schemaSub.get(0); + SourceException.throwIf( + !(substmt instanceof ChoiceEffectiveStatement) && !(substmt instanceof DataTreeEffectiveStatement), + stmt, "%s is not a recognized container data node definition", substmt); + verify(substmt instanceof DataSchemaNode, "Unexpected single child %s", substmt); + yield (DataSchemaNode) substmt; + } + default -> throw new InvalidSubstatementException(stmt, + "yang-data requires exactly one container data node definition, found %s", schemaSub); + }; - return new YangDataEffectiveStatementImpl(stmt, substatements, - verifyNotNull(stmt.namespaceItem(YangDataArgumentNamespace.class, Empty.value()))); + return new YangDataEffectiveStatementImpl(stmt, substatements, child); } } diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java index 5a67d59727..43943ebd27 100644 --- a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/AbstractYangDataTest.java @@ -28,7 +28,6 @@ public abstract class AbstractYangDataTest { @BeforeClass public static void createReactor() { REACTOR = RFC7950Reactors.vanillaReactorBuilder() - .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataArgumentNamespace.BEHAVIOUR) .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, new YangDataStatementSupport(YangParserConfiguration.DEFAULT)) .build(); diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1443Test.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1443Test.java new file mode 100644 index 0000000000..622cb51408 --- /dev/null +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YT1443Test.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 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.rfc8040.parser; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class YT1443Test extends AbstractYangDataTest { + @Test + public void buildEffectiveModelTest() throws ReactorException { + final var module = REACTOR.newBuild() + .addSources(IETF_RESTCONF_MODULE, sourceForResource("/yt1443.yang")) + .buildEffective() + .findModuleStatement(QName.create("yt1443", "yt1443")) + .orElseThrow(); + assertNotNull(module); + } +} diff --git a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java index f06eb4cf75..6fc35f83da 100644 --- a/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java +++ b/parser/rfc8040-parser-support/src/test/java/org/opendaylight/yangtools/rfc8040/parser/YangDataExtensionTest.java @@ -13,7 +13,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableSet; import java.util.Collection; @@ -25,6 +24,7 @@ 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.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -54,8 +54,6 @@ public class YangDataExtensionTest extends AbstractYangDataTest { private static final Revision REVISION = Revision.of("2017-06-01"); private static final QNameModule FOO_QNAMEMODULE = QNameModule.create(XMLNamespace.of("foo"), REVISION); - private static final QName MY_YANG_DATA_A = QName.create(FOO_QNAMEMODULE, "my-yang-data-a"); - private static final QName MY_YANG_DATA_B = QName.create(FOO_QNAMEMODULE, "my-yang-data-b"); @Test public void testYangData() throws Exception { @@ -73,20 +71,17 @@ public class YangDataExtensionTest extends AbstractYangDataTest { YangDataSchemaNode myYangDataANode = null; YangDataSchemaNode myYangDataBNode = null; for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) { - assertTrue(unknownSchemaNode instanceof YangDataSchemaNode); + assertThat(unknownSchemaNode, instanceOf(YangDataSchemaNode.class)); final YangDataSchemaNode yangDataSchemaNode = (YangDataSchemaNode) unknownSchemaNode; - if (MY_YANG_DATA_A.equals(yangDataSchemaNode.getQName())) { + if ("my-yang-data-a".equals(yangDataSchemaNode.getNodeParameter())) { myYangDataANode = yangDataSchemaNode; - } else if (MY_YANG_DATA_B.equals(yangDataSchemaNode.getQName())) { + } else if ("my-yang-data-b".equals(yangDataSchemaNode.getNodeParameter())) { myYangDataBNode = yangDataSchemaNode; } } assertNotNull(myYangDataANode); assertNotNull(myYangDataBNode); - - assertNotNull(myYangDataANode.getContainerSchemaNode()); - assertNotNull(myYangDataBNode.getContainerSchemaNode()); } @Test @@ -100,19 +95,23 @@ public class YangDataExtensionTest extends AbstractYangDataTest { assertEquals(1, unknownSchemaNodes.size()); final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next(); - assertTrue(unknownSchemaNode instanceof YangDataSchemaNode); + assertThat(unknownSchemaNode, instanceOf(YangDataSchemaNode.class)); final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode; assertNotNull(myYangDataNode); - final ContainerSchemaNode contInYangData = myYangDataNode.getContainerSchemaNode(); - assertNotNull(contInYangData); + final Collection yangDataChildren = myYangDataNode.getChildNodes(); + assertEquals(1, yangDataChildren.size()); + + final DataSchemaNode childInYangData = yangDataChildren.iterator().next(); + assertThat(childInYangData, instanceOf(ContainerSchemaNode.class)); + final ContainerSchemaNode contInYangData = (ContainerSchemaNode) childInYangData; assertEquals(Optional.empty(), contInYangData.effectiveConfig()); - final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.findDataChildByName( - QName.create(baz.getQNameModule(), "inner-cont")).get(); + final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.getDataChildByName( + QName.create(baz.getQNameModule(), "inner-cont")); assertNotNull(innerCont); assertEquals(Optional.empty(), innerCont.effectiveConfig()); - final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.findDataChildByName( - QName.create(baz.getQNameModule(), "grp-cont")).get(); + final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.getDataChildByName( + QName.create(baz.getQNameModule(), "grp-cont")); assertNotNull(grpCont); assertEquals(Optional.empty(), grpCont.effectiveConfig()); } @@ -128,17 +127,21 @@ public class YangDataExtensionTest extends AbstractYangDataTest { assertEquals(1, unknownSchemaNodes.size()); final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next(); - assertTrue(unknownSchemaNode instanceof YangDataSchemaNode); + assertThat(unknownSchemaNode, instanceOf(YangDataSchemaNode.class)); final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode; assertNotNull(myYangDataNode); - final ContainerSchemaNode contInYangData = myYangDataNode.getContainerSchemaNode(); - assertNotNull(contInYangData); - final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.findDataChildByName( - QName.create(foobar.getQNameModule(), "inner-cont")).get(); + final Collection yangDataChildren = myYangDataNode.getChildNodes(); + assertEquals(1, yangDataChildren.size()); + + final DataSchemaNode childInYangData = yangDataChildren.iterator().next(); + assertThat(childInYangData, instanceOf(ContainerSchemaNode.class)); + final ContainerSchemaNode contInYangData = (ContainerSchemaNode) childInYangData; + final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.getDataChildByName( + QName.create(foobar.getQNameModule(), "inner-cont")); assertNotNull(innerCont); - final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.findDataChildByName( - QName.create(foobar.getQNameModule(), "grp-cont")).get(); + final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.getDataChildByName( + QName.create(foobar.getQNameModule(), "grp-cont")); assertNotNull(grpCont); } @@ -151,8 +154,8 @@ public class YangDataExtensionTest extends AbstractYangDataTest { assertNotNull(schemaContext); final Module bar = schemaContext.findModule("bar", REVISION).get(); - final ContainerSchemaNode cont = (ContainerSchemaNode) bar.findDataChildByName( - QName.create(bar.getQNameModule(), "cont")).get(); + final ContainerSchemaNode cont = (ContainerSchemaNode) bar.getDataChildByName( + QName.create(bar.getQNameModule(), "cont")); assertNotNull(cont); final Collection extensions = schemaContext.getExtensions(); @@ -168,7 +171,7 @@ public class YangDataExtensionTest extends AbstractYangDataTest { final ReactorException ex = assertThrows(ReactorException.class, () -> build.buildEffective()); final Throwable cause = ex.getCause(); assertThat(cause, instanceOf(MissingSubstatementException.class)); - assertThat(cause.getMessage(), startsWith("yang-data requires exactly one container")); + assertThat(cause.getMessage(), startsWith("yang-data requires at least one substatement [at ")); } @Test @@ -177,6 +180,7 @@ public class YangDataExtensionTest extends AbstractYangDataTest { final ReactorException ex = assertThrows(ReactorException.class, () -> build.buildEffective()); final Throwable cause = ex.getCause(); assertThat(cause, instanceOf(InvalidSubstatementException.class)); - assertThat(cause.getMessage(), startsWith("yang-data requires exactly one data definition node, found 2")); + assertThat(cause.getMessage(), + startsWith("yang-data requires exactly one container data node definition, found [")); } } diff --git a/parser/rfc8040-parser-support/src/test/resources/yt1443.yang b/parser/rfc8040-parser-support/src/test/resources/yt1443.yang new file mode 100644 index 0000000000..28925025be --- /dev/null +++ b/parser/rfc8040-parser-support/src/test/resources/yt1443.yang @@ -0,0 +1,11 @@ +module yt1443 { + yang-version 1.1; + namespace "yt1443"; + prefix "yt1443"; + + import ietf-restconf { prefix rc; } + + rc:yang-data support-save-data { + anydata support-save-data; + } +} diff --git a/parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java b/parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java index cced7ae9dc..d4c6803419 100644 --- a/parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java +++ b/parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/DefaultReactors.java @@ -28,7 +28,6 @@ import org.opendaylight.yangtools.rfc6643.parser.MaxAccessStatementSupport; import org.opendaylight.yangtools.rfc6643.parser.OidStatementSupport; import org.opendaylight.yangtools.rfc6643.parser.SubIdStatementSupport; import org.opendaylight.yangtools.rfc7952.parser.AnnotationStatementSupport; -import org.opendaylight.yangtools.rfc8040.parser.YangDataArgumentNamespace; import org.opendaylight.yangtools.rfc8040.parser.YangDataStatementSupport; import org.opendaylight.yangtools.rfc8528.parser.MountPointStatementSupport; import org.opendaylight.yangtools.rfc8639.parser.SubscriptionStateNotificationStatementSupport; @@ -150,7 +149,6 @@ public final class DefaultReactors { .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, new AnnotationStatementSupport(config)) // RFC8040 yang-data support - .addNamespaceSupport(ModelProcessingPhase.FULL_DECLARATION, YangDataArgumentNamespace.BEHAVIOUR) .addStatementSupport(ModelProcessingPhase.FULL_DECLARATION, new YangDataStatementSupport(config)) // RFC8528 mount-point support -- 2.36.6