From: Robert Varga Date: Fri, 17 May 2019 00:07:37 +0000 (+0200) Subject: Add support for opaque anydata XML output X-Git-Tag: v3.0.1~17 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=bf0f2aba74948d14ad68cc26949b80b527f1e38f;p=yangtools.git Add support for opaque anydata XML output Streaming opaque anydata content requires simple element emission, which is implemented by this patch along with a very simple unit test. JIRA: YANGTOOLS-991 Change-Id: Ia1730b7415a34fcabbb936c7ef06e5a8522a2299 Signed-off-by: Robert Varga --- 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 a4510dc8a3..63d8cb8c00 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 @@ -24,6 +24,7 @@ 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.impl.codec.SchemaTracker; +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.LeafListSchemaNode; @@ -85,7 +86,8 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter extends XMLStreamNorm if (parent == schema) { endElement(); } - } else if (schema instanceof ContainerSchemaNode || schema instanceof LeafSchemaNode) { + } else if (schema instanceof ContainerSchemaNode || schema instanceof LeafSchemaNode + || schema instanceof AnyDataSchemaNode) { endElement(); } } @@ -159,4 +161,9 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter extends XMLStreamNorm checkState(current instanceof AnyXmlSchemaNode, "Unexpected scala value %s with %s", value, current); anyxmlValue(value); } + + @Override + SchemaNode startAnydata(final NodeIdentifier name) { + return tracker.startAnydataNode(name); + } } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java index cb1c4a7312..0e5b4c4be7 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java @@ -138,4 +138,10 @@ final class SchemalessXMLStreamNormalizedNodeStreamWriter extends XMLStreamNorma break; } } + + @Override + Object startAnydata(final NodeIdentifier name) { + nodeTypeStack.push(NodeType.ANY_XML); + return null; + } } diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java index 435b3a8778..e239845e23 100644 --- a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java @@ -10,9 +10,10 @@ package org.opendaylight.yangtools.yang.data.codec.xml; import static java.util.Objects.requireNonNull; import com.google.common.collect.ClassToInstanceMap; -import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableMap; import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -20,13 +21,16 @@ 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.concepts.ObjectExtensions; import org.opendaylight.yangtools.rfc7952.data.api.NormalizedMetadataStreamWriter; +import org.opendaylight.yangtools.rfc7952.data.api.OpaqueAnydataStreamWriter; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension; +import org.opendaylight.yangtools.yang.data.api.schema.stream.OpaqueAnydataExtension; import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -49,10 +53,16 @@ import org.w3c.dom.Node; * removed in a future version. */ public abstract class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter, - NormalizedMetadataStreamWriter { + NormalizedMetadataStreamWriter, OpaqueAnydataExtension { private static final Logger LOG = LoggerFactory.getLogger(XMLStreamNormalizedNodeStreamWriter.class); private static final Set BROKEN_ATTRIBUTES = ConcurrentHashMap.newKeySet(); + @SuppressWarnings("rawtypes") + static final ObjectExtensions.Factory EXTENSIONS_BUILDER = ObjectExtensions.factory( + XMLStreamNormalizedNodeStreamWriter.class, NormalizedMetadataStreamWriter.class, + OpaqueAnydataExtension.class); + private final @NonNull StreamWriterFacade facade; XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) { @@ -111,9 +121,11 @@ public abstract class XMLStreamNormalizedNodeStreamWriter implements Normaliz @Override public final ClassToInstanceMap getExtensions() { - return ImmutableClassToInstanceMap.of(NormalizedMetadataStreamWriter.class, this); + return EXTENSIONS_BUILDER.newInstance(this); } + abstract T startAnydata(NodeIdentifier name); + abstract void startList(NodeIdentifier name); abstract void startListItem(PathArgument name) throws IOException; @@ -237,4 +249,58 @@ public abstract class XMLStreamNormalizedNodeStreamWriter implements Normaliz } } } + + @Override + public final OpaqueAnydataExtension.StreamWriter startOpaqueAnydataNode(final NodeIdentifier name, + final boolean accurateLists) throws IOException { + final T schema = startAnydata(name); + startElement(name.getNodeType()); + return new XMLOpaqueStreamWriter(schema); + } + + private final class XMLOpaqueStreamWriter implements OpaqueAnydataStreamWriter { + private final Deque stack = new ArrayDeque<>(); + private final T valueContext; + + XMLOpaqueStreamWriter(final T valueContext) { + this.valueContext = valueContext; + } + + @Override + public void startOpaqueList(final NodeIdentifier name, final int childSizeHint) throws IOException { + stack.push(Boolean.FALSE); + } + + @Override + public void startOpaqueContainer(final NodeIdentifier name, final int childSizeHint) throws IOException { + stack.push(Boolean.TRUE); + startElement(name.getNodeType()); + } + + @Override + public void opaqueValue(final Object value) throws IOException { + writeValue(value, valueContext); + } + + @Override + public void endOpaqueNode() throws IOException { + if (stack.pop()) { + endElement(); + } + } + + @Override + public boolean requireMetadataFirst() { + return true; + } + + @Override + public void metadata(final ImmutableMap metadata) throws IOException { + if (stack.peek()) { + XMLStreamNormalizedNodeStreamWriter.this.metadata(metadata); + } else { + LOG.debug("Ignoring metadata {}", metadata); + } + } + } } diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java new file mode 100644 index 0000000000..58c94a729b --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractAnydataTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.codec.xml; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public abstract class AbstractAnydataTest { + static final QName FOO_QNAME = QName.create("test-anydata", "foo"); + static final QName BAR_QNAME = QName.create(FOO_QNAME, "bar"); + static final NodeIdentifier FOO_NODEID = NodeIdentifier.create(FOO_QNAME); + static final NodeIdentifier BAR_NODEID = NodeIdentifier.create(BAR_QNAME); + + static SchemaContext SCHEMA_CONTEXT; + + @BeforeClass + public static void beforeClass() throws Exception { + SCHEMA_CONTEXT = YangParserTestUtils.parseYangResource("/test-anydata.yang"); + } + + @AfterClass + public static void afterClass() { + SCHEMA_CONTEXT = null; + } + + +} diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java new file mode 100644 index 0000000000..f62d88d304 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/AnydataSerializeTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.codec.xml; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collection; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.util.schema.opaque.OpaqueDataBuilder; +import org.opendaylight.yangtools.yang.data.util.schema.opaque.OpaqueDataContainerBuilder; +import org.opendaylight.yangtools.yang.data.util.schema.opaque.OpaqueDataListBuilder; + +@RunWith(Parameterized.class) +public class AnydataSerializeTest extends AbstractAnydataTest { + + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return TestFactories.junitParameters(); + } + + private final XMLOutputFactory factory; + + public AnydataSerializeTest(final String factoryMode, final XMLOutputFactory factory) { + this.factory = factory; + } + + @Test + public void testOpaqueAnydata() throws XMLStreamException, IOException { + final StringWriter writer = new StringWriter(); + final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(writer); + + final NormalizedNodeStreamWriter xmlNormalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create( + xmlStreamWriter, SCHEMA_CONTEXT); + final NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter( + xmlNormalizedNodeStreamWriter); + normalizedNodeWriter.write(Builders.opaqueAnydataBuilder().withNodeIdentifier(FOO_NODEID) + .withValue(new OpaqueDataBuilder().withAccurateLists(false) + .withRoot( + new OpaqueDataListBuilder().withIdentifier(BAR_NODEID) + .withChild(new OpaqueDataContainerBuilder().withIdentifier(BAR_NODEID).build()) + .build()) + .build()) + .build()); + normalizedNodeWriter.flush(); + + final String serializedXml = writer.toString(); + assertEquals("", serializedXml); + } +} diff --git a/yang/yang-data-codec-xml/src/test/resources/test-anydata.yang b/yang/yang-data-codec-xml/src/test/resources/test-anydata.yang new file mode 100644 index 0000000000..d1410150bf --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/test-anydata.yang @@ -0,0 +1,8 @@ +module test-anydata { + yang-version 1.1; + namespace test-anydata; + prefix ta; + + anydata foo; +} + diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java index d0475b64cc..f30c6fb098 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java @@ -26,6 +26,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.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; +import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; @@ -252,6 +253,18 @@ public final class SchemaTracker { schemaStack.push(anyxmlNode(name)); } + public AnyDataSchemaNode anydataNode(final NodeIdentifier name) { + final SchemaNode schema = getSchema(name); + checkArgument(schema instanceof AnyDataSchemaNode, "Node %s is not anydata", schema.getPath()); + return (AnyDataSchemaNode)schema; + } + + public AnyDataSchemaNode startAnydataNode(final NodeIdentifier name) { + final AnyDataSchemaNode ret = anydataNode(name); + schemaStack.push(ret); + return ret; + } + public Object endNode() { return schemaStack.pop(); }