From 8f5f0782ffb25f903946d23816b199bdfb41fcda Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 5 Apr 2024 11:49:23 +0200 Subject: [PATCH] Allow emission of operation input/output Both XML and JSON codecs should be in a place where they can emit RPC/action input/outputs -- except NormalizedNodeStreamWriterStack cannot initialize there. Rework NormalizedNodeStreamWriterStack internal tracking to work on EffectiveStatements and lift this restriction. JIRA: YANGTOOLS-1570 Change-Id: If45bb096ee30cc35bf2a40ec26e12217f71ca06c Signed-off-by: Robert Varga --- .../gson/JSONNormalizedNodeStreamWriter.java | 35 +++-- .../yang/data/codec/gson/YT1570Test.java | 139 ++++++++++++++++++ ...reXMLStreamNormalizedNodeStreamWriter.java | 36 +++-- .../yang/data/codec/xml/YT1570Test.java | 125 ++++++++++++++++ .../util/NormalizedNodeStreamWriterStack.java | 127 ++++++++-------- 5 files changed, 368 insertions(+), 94 deletions(-) create mode 100644 codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1570Test.java create mode 100644 codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1570Test.java diff --git a/codec/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/codec/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java index 465b895f20..950c171a7d 100644 --- a/codec/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java +++ b/codec/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java @@ -30,12 +30,13 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.MountPointExtension; import org.opendaylight.yangtools.yang.data.util.NormalizedNodeStreamWriterStack; -import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode; -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.EffectiveStatementInference; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.PresenceEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.w3c.dom.Element; @@ -77,8 +78,7 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt } /** - * RFC6020 deviation: we are not required to emit empty containers unless they - * are marked as 'presence'. + * RFC6020 deviation: we are not required to emit empty containers unless they are marked as 'presence'. */ private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true; @@ -94,10 +94,12 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt private final JSONCodecFactory codecs; private final JsonWriter writer; private final DefaultJSONValueWriter valueWriter; + private JSONStreamWriterContext context; - JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final NormalizedNodeStreamWriterStack tracker, - final JsonWriter writer, final JSONStreamWriterRootContext rootContext) { + private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, + final NormalizedNodeStreamWriterStack tracker, final JsonWriter writer, + final JSONStreamWriterRootContext rootContext) { this.writer = requireNonNull(writer); codecs = requireNonNull(codecFactory); this.tracker = requireNonNull(tracker); @@ -335,15 +337,12 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt context = new JSONStreamWriterListContext(context, name); } - /* - * Warning suppressed due to static final constant which triggers a warning - * for the call to schema.isPresenceContainer(). - */ @Override public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException { - final boolean isPresence = tracker.startContainerNode(name) instanceof ContainerSchemaNode container - ? container.isPresenceContainer() : DEFAULT_EMIT_EMPTY_CONTAINERS; - context = new JSONStreamWriterNamedObjectContext(context, name, isPresence); + final boolean emitIfEmpty = tracker.startContainerNode(name) instanceof ContainerEffectiveStatement container + ? container.findFirstEffectiveSubstatement(PresenceEffectiveStatement.class).isPresent() + : DEFAULT_EMIT_EMPTY_CONTAINERS; + context = new JSONStreamWriterNamedObjectContext(context, name, emitIfEmpty); } @Override @@ -436,10 +435,10 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt @Override public void scalarValue(final Object value) throws IOException { - final Object current = tracker.getParent(); + final var current = tracker.currentStatement(); if (current instanceof TypedDataSchemaNode typed) { writeValue(value, codecs.codecFor(typed, tracker)); - } else if (current instanceof AnydataSchemaNode) { + } else if (current instanceof AnydataEffectiveStatement) { writeAnydataValue(value); } else { throw new IllegalStateException(String.format("Cannot emit scalar %s for %s", value, current)); @@ -448,8 +447,8 @@ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeSt @Override public void domSourceValue(final DOMSource value) throws IOException { - final Object current = tracker.getParent(); - checkState(current instanceof AnyxmlSchemaNode, "Cannot emit DOMSource %s for %s", value, current); + final var current = tracker.currentStatement(); + checkState(current instanceof AnyxmlEffectiveStatement, "Cannot emit DOMSource %s for %s", value, current); // FIXME: should have a codec based on this :) writeAnyXmlValue(value); } diff --git a/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1570Test.java b/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1570Test.java new file mode 100644 index 0000000000..31a14b607a --- /dev/null +++ b/codec/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/YT1570Test.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 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.data.codec.gson; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; +import org.junit.jupiter.api.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Uint64; +import org.opendaylight.yangtools.yang.common.Uint8; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +class YT1570Test { + private static final QName INPUT = QName.create("foo", "input"); + private static final QName OUTPUT = QName.create("foo", "output"); + private static final QName FOO = QName.create("foo", "foo"); + private static final QName BAR = QName.create("foo", "bar"); + private static final QName BAZ = QName.create("foo", "baz"); + private static final QName UINT = QName.create("foo", "uint"); + + private static final EffectiveModelContext MODEL_CONTEXT = YangParserTestUtils.parseYang(""" + module foo { + namespace foo; + prefix foo; + yang-version 1.1; + + rpc foo { + input { + leaf uint { + type uint8; + } + } + output { + leaf uint { + type uint64; + } + } + } + + container bar { + action baz { + input { + leaf uint { + type uint8; + } + } + output { + leaf uint { + type uint64; + } + } + } + } + }"""); + + @Test + void testRpcInput() { + assertEquals(""" + { + "foo:input": { + "uint": 1 + } + }""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(INPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint8.ONE)) + .build(), FOO)); + } + + @Test + void testRpcOutput() { + assertEquals(""" + { + "foo:output": { + "uint": "1" + } + }""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(OUTPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint64.ONE)) + .build(), FOO)); + } + + @Test + void testActionInput() { + assertEquals(""" + { + "foo:input": { + "uint": 2 + } + }""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(INPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint8.TWO)) + .build(), BAR, BAZ)); + } + + @Test + void testActionOutput() { + assertEquals(""" + { + "foo:output": { + "uint": "2" + } + }""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(OUTPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint64.TWO)) + .build(), BAR, BAZ)); + } + + private static String serialize(final ContainerNode container, final QName... nodeIdentifiers) { + final var writer = new StringWriter(); + final var jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter( + JSONCodecFactorySupplier.RFC7951.getShared(MODEL_CONTEXT), + SchemaInferenceStack.of(MODEL_CONTEXT, Absolute.of(nodeIdentifiers)).toInference(), null, + JsonWriterFactory.createJsonWriter(writer, 2)); + try (var nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream)) { + nodeWriter.write(container); + } catch (IOException e) { + throw new AssertionError(e); + } + return writer.toString(); + } +} diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java index 9bcf452041..2d73959ab0 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java @@ -24,15 +24,18 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.util.NormalizedNodeStreamWriterStack; -import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ContainerLike; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -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.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; final class SchemaAwareXMLStreamNormalizedNodeStreamWriter @@ -92,15 +95,16 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter @Override public void endNode() throws IOException { - final Object schema = tracker.endNode(); - if (schema instanceof ListSchemaNode || schema instanceof LeafListSchemaNode) { + final var schema = tracker.endNode(); + if (schema instanceof ListEffectiveStatement || schema instanceof LeafListEffectiveStatement) { // For lists, we only emit end element on the inner frame - final Object parent = tracker.getParent(); - if (parent == schema) { + if (tracker.currentStatement() == schema) { endElement(); } - } else if (schema instanceof ContainerLike || schema instanceof LeafSchemaNode - || schema instanceof AnydataSchemaNode || schema instanceof AnyxmlSchemaNode) { + } else if (schema instanceof ContainerEffectiveStatement || schema instanceof LeafEffectiveStatement + || schema instanceof AnydataEffectiveStatement || schema instanceof AnyxmlEffectiveStatement + || schema instanceof InputEffectiveStatement || schema instanceof OutputEffectiveStatement + || schema instanceof NotificationEffectiveStatement) { endElement(); } } @@ -150,10 +154,10 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter @Override public void scalarValue(final Object value) throws IOException { - final Object current = tracker.getParent(); + final var current = tracker.currentStatement(); if (current instanceof TypedDataSchemaNode typedSchema) { writeValue(value, typedSchema); - } else if (current instanceof AnydataSchemaNode) { + } else if (current instanceof AnydataEffectiveStatement) { anydataValue(value); } else { throw new IllegalStateException("Unexpected scalar value " + value + " with " + current); @@ -162,8 +166,8 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter @Override public void domSourceValue(final DOMSource value) throws IOException { - final Object current = tracker.getParent(); - checkState(current instanceof AnyxmlSchemaNode, "Unexpected value %s with %s", value, current); + final var current = tracker.currentStatement(); + checkState(current instanceof AnyxmlEffectiveStatement, "Unexpected value %s with %s", value, current); anyxmlValue(value); } diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1570Test.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1570Test.java new file mode 100644 index 0000000000..9be0a7651e --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1570Test.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024 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.data.codec.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; +import javax.xml.stream.XMLStreamException; +import org.junit.jupiter.api.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Uint64; +import org.opendaylight.yangtools.yang.common.Uint8; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +class YT1570Test { + private static final QName INPUT = QName.create("foo", "input"); + private static final QName OUTPUT = QName.create("foo", "output"); + private static final QName FOO = QName.create("foo", "foo"); + private static final QName BAR = QName.create("foo", "bar"); + private static final QName BAZ = QName.create("foo", "baz"); + private static final QName UINT = QName.create("foo", "uint"); + + private static final EffectiveModelContext MODEL_CONTEXT = YangParserTestUtils.parseYang(""" + module foo { + namespace foo; + prefix foo; + yang-version 1.1; + + rpc foo { + input { + leaf uint { + type uint8; + } + } + output { + leaf uint { + type uint64; + } + } + } + + container bar { + action baz { + input { + leaf uint { + type uint8; + } + } + output { + leaf uint { + type uint64; + } + } + } + } + }"""); + + @Test + void testRpcInput() { + assertEquals(""" + 1""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(INPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint8.ONE)) + .build(), FOO)); + } + + @Test + void testRpcOutput() { + assertEquals(""" + 1""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(OUTPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint64.ONE)) + .build(), FOO)); + } + + @Test + void testActionInput() { + assertEquals(""" + 2""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(INPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint8.TWO)) + .build(), BAR, BAZ)); + } + + @Test + void testActionOutput() { + assertEquals(""" + 2""", + serialize(ImmutableNodes.newContainerBuilder() + .withNodeIdentifier(new NodeIdentifier(OUTPUT)) + .withChild(ImmutableNodes.leafNode(UINT, Uint64.TWO)) + .build(), BAR, BAZ)); + } + + private static String serialize(final ContainerNode container, final QName... nodeIdentifiers) { + final var writer = new StringWriter(); + try { + final var xmlStream = XMLStreamNormalizedNodeStreamWriter.create( + TestFactories.DEFAULT_OUTPUT_FACTORY.createXMLStreamWriter(writer), + SchemaInferenceStack.of(MODEL_CONTEXT, Absolute.of(nodeIdentifiers)).toInference()); + try (var nodeWriter = NormalizedNodeWriter.forStreamWriter(xmlStream)) { + nodeWriter.write(container); + } + } catch (IOException | XMLStreamException e) { + throw new AssertionError(e); + } + return writer.toString(); + } +} diff --git a/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java b/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java index c03aae08a5..1b48afe728 100644 --- a/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java +++ b/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeStreamWriterStack.java @@ -13,11 +13,11 @@ import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; -import com.google.common.base.VerifyException; import java.io.IOException; import java.util.ArrayDeque; import java.util.Deque; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; @@ -25,22 +25,23 @@ 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.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; -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.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference; -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.NotificationDefinition; -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.stmt.ActionEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement; +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.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement; 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; @@ -57,9 +58,9 @@ import org.slf4j.LoggerFactory; public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeStreamWriterStack.class); - private final Deque schemaStack = new ArrayDeque<>(); + private final Deque> schemaStack = new ArrayDeque<>(); private final SchemaInferenceStack dataTree; - private final DataNodeContainer root; + private final Object root; private NormalizedNodeStreamWriterStack(final EffectiveModelContext context) { dataTree = SchemaInferenceStack.of(context); @@ -70,7 +71,7 @@ public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { this.dataTree = requireNonNull(dataTree); if (!dataTree.isEmpty()) { final var current = dataTree.currentStatement(); - if (current instanceof DataNodeContainer container) { + if (current instanceof DataTreeAwareEffectiveStatement container) { root = container; } else { throw new IllegalArgumentException("Cannot instantiate on " + current); @@ -170,28 +171,29 @@ public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { return dataTree.resolveLeafref(type); } - public Object getParent() { - final var schema = schemaStack.peek(); - return schema == null ? root : schema; + /** + * Return the current {@link EffectiveStatement}, or {@code null}. + * + * @return the current {@link EffectiveStatement}, or {@code null} + */ + public @Nullable EffectiveStatement currentStatement() { + return schemaStack.peek(); } - private @NonNull SchemaNode enterDataTree(final PathArgument name) { + private @NonNull DataTreeEffectiveStatement enterDataTree(final PathArgument name) { final var qname = name.getNodeType(); final var stmt = dataTree.enterDataTree(qname); - if (!(stmt instanceof SchemaNode ret)) { - throw new VerifyException("Unexpected result " + stmt); + if (currentStatement() instanceof ChoiceEffectiveStatement choice) { + final var check = choice.findDataTreeNode(qname).orElse(null); + verify(check == stmt, "Data tree result %s does not match choice result %s", stmt, check); } - if (getParent() instanceof ChoiceSchemaNode choice) { - final var check = choice.findDataSchemaChild(qname).orElse(null); - verify(check == ret, "Data tree result %s does not match choice result %s", ret, check); - } - return ret; + return stmt; } - private @NonNull T enterDataTree(final PathArgument name, + private > @NonNull T enterDataTree(final PathArgument name, final @NonNull Class expectedClass, final @NonNull String humanString) { final var schema = enterDataTree(name); - final T casted; + final @NonNull T casted; try { casted = expectedClass.cast(schema); } catch (ClassCastException e) { @@ -202,32 +204,36 @@ public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { } public void startList(final PathArgument name) { - enterDataTree(name, ListSchemaNode.class, "a list"); + enterDataTree(name, ListEffectiveStatement.class, "a list"); } public void startListItem(final PathArgument name) throws IOException { - if (!(getParent() instanceof ListSchemaNode parentList)) { + if (!(currentStatement() instanceof ListEffectiveStatement parentList)) { throw new IllegalArgumentException("List item is not appropriate"); } schemaStack.push(parentList); } public void startLeafNode(final NodeIdentifier name) throws IOException { - enterDataTree(name, LeafSchemaNode.class, "a leaf"); + enterDataTree(name, LeafEffectiveStatement.class, "a leaf"); } public void startLeafSet(final NodeIdentifier name) { - enterDataTree(name, LeafListSchemaNode.class, "a leaf-list"); + enterDataTree(name, LeafListEffectiveStatement.class, "a leaf-list"); + } + + public void startLeafSetEntryNode(final NodeWithValue name) { + schemaStack.push(leafSetEntryNode(name.getNodeType())); } - private @NonNull LeafListSchemaNode leafSetEntryNode(final QName qname) { - final Object parent = getParent(); - if (parent instanceof LeafListSchemaNode leafList) { + private @NonNull LeafListEffectiveStatement leafSetEntryNode(final QName qname) { + final var parent = currentStatement(); + if (parent instanceof LeafListEffectiveStatement leafList) { return leafList; } - if (parent instanceof DataNodeContainer parentContainer) { - final var child = parentContainer.dataChildByName(qname); - if (child instanceof LeafListSchemaNode childLeafList) { + if (parent instanceof DataTreeAwareEffectiveStatement parentContainer) { + final var child = parentContainer.findDataTreeNode(qname).orElse(null); + if (child instanceof LeafListEffectiveStatement childLeafList) { return childLeafList; } throw new IllegalArgumentException( @@ -236,52 +242,53 @@ public final class NormalizedNodeStreamWriterStack implements LeafrefResolver { throw new IllegalArgumentException("Cannot lookup " + qname + " in parent " + parent); } - public void startLeafSetEntryNode(final NodeWithValue name) { - schemaStack.push(leafSetEntryNode(name.getNodeType())); - } - public void startChoiceNode(final NodeIdentifier name) { LOG.debug("Enter choice {}", name); - final var stmt = dataTree.enterChoice(name.getNodeType()); - if (stmt instanceof ChoiceSchemaNode choice) { - schemaStack.push(choice); - } else { - throw new VerifyException("Node " + stmt + " is not a choice"); - } + schemaStack.push(dataTree.enterChoice(name.getNodeType())); } - public @NonNull ContainerLike startContainerNode(final NodeIdentifier name) { + public @NonNull DataTreeAwareEffectiveStatement startContainerNode(final NodeIdentifier name) { LOG.debug("Enter container {}", name); - final ContainerLike schema; - if (schemaStack.isEmpty() && root instanceof NotificationDefinition notification - && name.getNodeType().equals(notification.getQName())) { + final DataTreeAwareEffectiveStatement ret; + if (schemaStack.isEmpty() && root instanceof NotificationEffectiveStatement notification + && name.getNodeType().equals(notification.argument())) { // Special case for stacks initialized at notification. We pretend the first container is contained within // itself. // FIXME: 8.0.0: factor this special case out to something more reasonable, like being initialized at the // Notification's parent and knowing to enterSchemaTree() instead of enterDataTree(). - schema = notification.toContainerLike(); - schemaStack.push(schema); + ret = notification; } else { - schema = enterDataTree(name, ContainerLike.class, "a container"); + final var child = enterDataTree(name); + if (child instanceof ContainerEffectiveStatement container) { + ret = container; + } else if (child instanceof InputEffectiveStatement input) { + ret = input; + } else if (child instanceof OutputEffectiveStatement output) { + ret = output; + } else { + dataTree.exitToDataTree(); + throw new IllegalArgumentException("Node " + child + " is not a container"); + } } - return schema; + schemaStack.push(ret); + return ret; } public void startAnyxmlNode(final NodeIdentifier name) { - enterDataTree(name, AnyxmlSchemaNode.class, "anyxml"); + enterDataTree(name, AnyxmlEffectiveStatement.class, "anyxml"); } public void startAnydataNode(final NodeIdentifier name) { - enterDataTree(name, AnydataSchemaNode.class, "anydata"); + enterDataTree(name, AnydataEffectiveStatement.class, "anydata"); } - public Object endNode() { + public EffectiveStatement endNode() { final var ret = schemaStack.pop(); // If this is a data tree node, make sure it is updated. Before that, though, we need to check if this is not // actually listEntry -> list or leafListEntry -> leafList exit. - if (getParent() != ret) { + if (currentStatement() != ret) { dataTree.exit(); } return ret; -- 2.36.6