From 31d5ed0c664e8fbd793538c4e4546204847c779e Mon Sep 17 00:00:00 2001 From: Igor Foltin Date: Sat, 2 Apr 2016 13:06:34 +0200 Subject: [PATCH] Bug 1441: Implement XML Stream Reader to Normalized Node Writer This patch introduces new XML Parser which walks the DOM tree and emits events into a NormalizedNodeStreamWriter. The parser is found in the newly created module yang-data-codec-xml. Classes which are shared by XML and JSON Parser were moved to module yang-data-util. Added documentation for the XML parser. Change-Id: I5b7de341333f94ad75bc2ff93b0f47f2c007e793 Signed-off-by: Igor Foltin --- common/artifacts/pom.xml | 5 + common/features/pom.xml | 4 + .../features/src/main/features/features.xml | 2 + .../main/asciidoc/developer/introduction.adoc | 50 ++- yang/pom.xml | 1 + .../data/codec/gson/JsonParserStream.java | 68 +-- yang/yang-data-codec-xml/pom.xml | 63 +++ .../yang/data/codec/xml/AbstractXmlCodec.java | 66 +++ .../yang/data/codec/xml/BooleanXmlCodec.java | 31 ++ .../yang/data/codec/xml/NumberXmlCodec.java | 31 ++ .../yang/data/codec/xml/QuotedXmlCodec.java | 32 ++ .../yang/data/codec/xml/XmlCodec.java | 24 + .../yang/data/codec/xml/XmlCodecFactory.java | 135 ++++++ .../yang/data/codec/xml/XmlEmptyCodec.java | 36 ++ .../yang/data/codec/xml/XmlParserStream.java | 267 +++++++++++ .../codec/xml/XmlStringIdentityrefCodec.java | 57 +++ .../xml/XmlStringInstanceIdentifierCodec.java | 72 +++ .../codec/xml/XmlToNormalizedNodesTest.java | 424 ++++++++++++++++++ .../src/test/resources/baz.xml | 40 ++ .../src/test/resources/baz.yang | 82 ++++ .../src/test/resources/foo.xml | 18 + .../src/test/resources/foo.yang | 54 +++ .../src/test/resources/invalid-baz-2.xml | 40 ++ .../src/test/resources/invalid-baz-3.xml | 40 ++ .../src/test/resources/invalid-baz-4.xml | 40 ++ .../src/test/resources/invalid-baz.xml | 40 ++ .../src/test/resources/invalid-foo-2.xml | 27 ++ .../src/test/resources/invalid-foo-3.xml | 27 ++ .../src/test/resources/invalid-foo.xml | 23 + .../util}/AbstractNodeDataWithSchema.java | 8 +- .../data/util}/AnyXmlNodeDataWithSchema.java | 9 +- .../data/util}/CaseNodeDataWithSchema.java | 7 +- .../data/util}/ChoiceNodeDataWithSchema.java | 6 +- .../util}/CompositeNodeDataWithSchema.java | 54 ++- .../util}/ContainerNodeDataWithSchema.java | 6 +- .../LeafListEntryNodeDataWithSchema.java | 6 +- .../util}/LeafListNodeDataWithSchema.java | 6 +- .../data/util}/LeafNodeDataWithSchema.java | 6 +- .../util}/ListEntryNodeDataWithSchema.java | 6 +- .../data/util}/ListNodeDataWithSchema.java | 6 +- .../yang/data/util/ParserStreamUtils.java | 79 ++++ .../yang/data/util}/RpcAsContainer.java | 8 +- .../data/util}/SimpleNodeDataWithSchema.java | 8 +- .../YangModeledAnyXmlNodeDataWithSchema.java | 4 +- 44 files changed, 1903 insertions(+), 115 deletions(-) create mode 100644 yang/yang-data-codec-xml/pom.xml create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlEmptyCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringIdentityrefCodec.java create mode 100644 yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java create mode 100644 yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java create mode 100644 yang/yang-data-codec-xml/src/test/resources/baz.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/baz.yang create mode 100644 yang/yang-data-codec-xml/src/test/resources/foo.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/foo.yang create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml create mode 100644 yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/AbstractNodeDataWithSchema.java (89%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/AnyXmlNodeDataWithSchema.java (69%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/CaseNodeDataWithSchema.java (68%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/ChoiceNodeDataWithSchema.java (88%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/CompositeNodeDataWithSchema.java (77%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/ContainerNodeDataWithSchema.java (81%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/LeafListEntryNodeDataWithSchema.java (80%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/LeafListNodeDataWithSchema.java (85%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/LeafNodeDataWithSchema.java (80%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/ListEntryNodeDataWithSchema.java (93%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/ListNodeDataWithSchema.java (86%) create mode 100644 yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/RpcAsContainer.java (93%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/SimpleNodeDataWithSchema.java (69%) rename yang/{yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson => yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util}/YangModeledAnyXmlNodeDataWithSchema.java (89%) diff --git a/common/artifacts/pom.xml b/common/artifacts/pom.xml index 023785f9b5..d25f0fcb43 100644 --- a/common/artifacts/pom.xml +++ b/common/artifacts/pom.xml @@ -79,6 +79,11 @@ yang-data-codec-gson ${project.version} + + org.opendaylight.yangtools + yang-data-codec-xml + ${project.version} + org.opendaylight.yangtools yang-model-api diff --git a/common/features/pom.xml b/common/features/pom.xml index cbc6b24323..82fb08fed0 100644 --- a/common/features/pom.xml +++ b/common/features/pom.xml @@ -50,6 +50,10 @@ org.opendaylight.yangtools yang-data-codec-gson + + org.opendaylight.yangtools + yang-data-codec-xml + org.opendaylight.yangtools yang-model-api diff --git a/common/features/src/main/features/features.xml b/common/features/src/main/features/features.xml index b984c6b1d9..631a2e60fa 100644 --- a/common/features/src/main/features/features.xml +++ b/common/features/src/main/features/features.xml @@ -20,6 +20,8 @@ mvn:com.google.code.gson/gson/{{VERSION}} mvn:org.opendaylight.yangtools/yang-data-codec-gson/{{VERSION}} + + mvn:org.opendaylight.yangtools/yang-data-codec-xml/{{VERSION}} diff --git a/docs/src/main/asciidoc/developer/introduction.adoc b/docs/src/main/asciidoc/developer/introduction.adoc index 4e2a96a0c9..7542f3c862 100644 --- a/docs/src/main/asciidoc/developer/introduction.adoc +++ b/docs/src/main/asciidoc/developer/introduction.adoc @@ -37,13 +37,14 @@ YANG Tools project consists of following logical subsystems: wants to generate code or other artefacts based on YANG model. === Concepts -Project defines base concepts and helper classes which are project-agnostic and could be used outside of YANG Tools project scope. +Project defines base concepts and helper classes which are project-agnostic and could be used outside of YANG Tools project scope. === Components - yang-common - yang-data-api - yang-data-codec-gson +- yang-data-codec-xml - yang-data-impl - yang-data-jaxen - yang-data-transform @@ -66,7 +67,7 @@ image:models/yang-model-api.png[] Yang Statement Parser works on the idea of statement concepts as defined in RFC6020, section 6.3. We come up here with basic ModelStatement and StatementDefinition, following RFC6020 idea of having sequence of statements, where every statement contains keyword and zero or one argument. ModelStatement is extended by DeclaredStatement (as it comes from source, e.g. YANG source) -and EffectiveStatement, which contains other substatements and tends to represent result of semantic processing of other statements (uses, augment for YANG). +and EffectiveStatement, which contains other substatements and tends to represent result of semantic processing of other statements (uses, augment for YANG). IdentifierNamespace represents common superclass for YANG model namespaces. Input of the Yang Statement Parser is a collection of StatementStreamSource objects. @@ -88,6 +89,7 @@ Class diagram of yang data API image:models/yang-data-api.png[] ==== YANG Data Codecs +Codecs which enable serialization of NormalizedNodes into YANG-modeled data in XML or JSON format and deserialization of YANG-modeled data in XML or JSON format into NormalizedNodes. ==== YANG Maven Plugin Maven plugin which integrates YANG parser into Maven @@ -97,11 +99,11 @@ Maven plugin which integrates YANG parser into Maven == How to / Tutorials === Working with YANG Model -First thing you need to do if you want to work with YANG models is to instantiate a SchemaContext object. This object type describes one or more parsed YANG modules. +First thing you need to do if you want to work with YANG models is to instantiate a SchemaContext object. This object type describes one or more parsed YANG modules. -In order to create it you need to utilize YANG statement parser which takes one or more StatementStreamSource objects as input and then produces the SchemaContext object. +In order to create it you need to utilize YANG statement parser which takes one or more StatementStreamSource objects as input and then produces the SchemaContext object. -StatementStreamSource object contains the source file information. It has two implementations, one for YANG sources - YangStatementSourceImpl, and one for YIN sources - YinStatementSourceImpl. +StatementStreamSource object contains the source file information. It has two implementations, one for YANG sources - YangStatementSourceImpl, and one for YIN sources - YinStatementSourceImpl. Here is an example of creating StatementStreamSource objects for YANG files, providing them to the YANG statement parser and building the SchemaContext: @@ -116,9 +118,9 @@ reactor.addSources(yangModuleSource, yangModuleSource2); SchemaContext schemaContext = reactor.buildEffective(); ---- -First, StatementStreamSource objects with two constructor arguments should be instantiated: path to the yang source file (which is a regular String object) and a boolean which determines if the path is absolute or relative. +First, StatementStreamSource objects with two constructor arguments should be instantiated: path to the yang source file (which is a regular String object) and a boolean which determines if the path is absolute or relative. -Next comes the initiation of new yang parsing cycle - which is represented by CrossSourceStatementReactor.BuildAction object. You can get it by calling method newBuild() on CrossSourceStatementReactor object (RFC6020_REACTOR) in YangInferencePipeline class. +Next comes the initiation of new yang parsing cycle - which is represented by CrossSourceStatementReactor.BuildAction object. You can get it by calling method newBuild() on CrossSourceStatementReactor object (RFC6020_REACTOR) in YangInferencePipeline class. Then you should feed yang sources to it by calling method addSources() that takes one or more StatementStreamSource objects as arguments. @@ -130,7 +132,7 @@ Let us explain how to work with models contained in the newly created SchemaCont Set modules = schemaContext.getModules(); Set dataSchemaNodes = schemaContext.getDataDefinitions(); -Usually you want to access specific modules. Getting a concrete module from SchemaContext is a matter of calling one of these methods: +Usually you want to access specific modules. Getting a concrete module from SchemaContext is a matter of calling one of these methods: * findModuleByName(), * findModuleByNamespace(), @@ -187,7 +189,7 @@ ContainerNode containerNode = Builders.containerBuilder().withNodeIdentifier(new \\ example 2 ContainerNode containerNode2 = Builders.containerBuilder(containerSchemaNode).build(); ---- -Both examples produce the same result. NodeIdentifier is one of the four types of YangInstanceIdentifier (these types are described in the javadoc of YangInstanceIdentifier). The purpose of YangInstanceIdentifier is to uniquely identify a particular node in the data tree. In the first example, you have to add NodeIdentifier before building the resulting node. In the second example it is also added using the provided ContainerSchemaNode object. +Both examples produce the same result. NodeIdentifier is one of the four types of YangInstanceIdentifier (these types are described in the javadoc of YangInstanceIdentifier). The purpose of YangInstanceIdentifier is to uniquely identify a particular node in the data tree. In the first example, you have to add NodeIdentifier before building the resulting node. In the second example it is also added using the provided ContainerSchemaNode object. ImmutableNodes class offers similar builder methods and also adds an overloaded method called fromInstanceId() which allows you to create a NormalizedNode object based on YangInstanceIdentifier and SchemaContext. Below is an example which shows the use of this method. @@ -307,6 +309,36 @@ Write operation is performed with both normalized nodes mentioned earlier. It co Now you can access the written nodes. In order to do this, you have to create a new DataTreeSnapshot instance and call the method readNode() with path argument pointing to a particular node in the tree. === Serialization / deserialization of YANG Data +If you want to deserialize YANG-modeled data which have the form of an XML document, you can use the XML parser found in the module yang-data-codec-xml. The parser walks through the XML document containing YANG-modeled data based on the provided SchemaContext and emits node events into a NormalizedNodeStreamWriter. The parser disallows multiple instances of the same element except for leaf-list and list entries. The parser also expects that the YANG-modeled data in the XML source are wrapped in a root element. Otherwise it will not work correctly. + +Here is an example of using the XML parser. +[source, java] +---- +InputStream resourceAsStream = ExampleClass.class.getResourceAsStream("/example-module.yang"); + +XMLInputFactory factory = XMLInputFactory.newInstance(); +XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + +NormalizedNodeResult result = new NormalizedNodeResult(); +NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + +XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); +xmlParser.parse(reader); + +NormalizedNode transformedInput = result.getResult(); +---- +The XML parser utilizes the javax.xml.stream.XMLStreamReader for parsing an XML document. First, you should create an instance of this reader using XMLInputFactory and then load an XML document (in the form of InputStream object) into it. + +In order to emit node events while parsing the data you need to instantiate a NormalizedNodeStreamWriter. This writer is actually an interface and therefore you need to use a concrete implementation of it. In this example it is the ImmutableNormalizedNodeStreamWriter, which constructs immutable instances of NormalizedNodes. + +There are two ways how to create an instance of this writer using the static overloaded method from(). One version of this method takes a NormalizedNodeResult as argument. This object type is a result holder in which the resulting NormalizedNode will be stored. The other version takes a +NormalizedNodeContainerBuilder as argument. All created nodes will be written to this builder. + +Next step is to create an instance of the XML parser. The parser itself is represented by a class named XmlParserStream. You can use one of two versions of the static overloaded method create() to construct this object. One version accepts a NormalizedNodeStreamWriter and a SchemaContext as arguments, the other version takes the same arguments plus a SchemaNode. Node events are emitted to the writer. The SchemaContext is used to check if the YANG data in the XML source comply with the provided YANG model(s). The last argument, a SchemaNode object, describes the node that is the parent of nodes defined in the XML data. If you do not provide this argument, the parser sets the SchemaContext as the parent node. + +The parser is now ready to walk through the XML. Parsing is initiated by calling the method parse() on the XmlParserStream object with XMLStreamReader as its argument. + +Finally you can access the result of parsing - a tree of NormalizedNodes containg the data as they are defined in the parsed XML document - by calling the method getResult() on the NormalizedNodeResult object. === Introducing schema source repositories diff --git a/yang/pom.xml b/yang/pom.xml index 335af8721a..b8d2a7a95d 100644 --- a/yang/pom.xml +++ b/yang/pom.xml @@ -27,6 +27,7 @@ yang-data-impl yang-data-transform yang-data-codec-gson + yang-data-codec-xml yang-model-api yang-maven-plugin yang-maven-plugin-it 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 f184ce7b5e..99ac2809bd 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 @@ -20,16 +20,24 @@ import java.io.Flushable; import java.io.IOException; import java.net.URI; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.HashSet; -import java.util.List; import java.util.Set; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.schema.stream.DataSchemaNodeAwareAdaptor; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafListEntryNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ListEntryNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils; +import org.opendaylight.yangtools.yang.data.util.RpcAsContainer; +import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; @@ -168,8 +176,8 @@ public final class JsonParserStream implements Closeable, Flushable { } namesakes.add(jsonElementName); - final Deque childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parentSchema, - localName, getCurrentNamespace()); + final Deque childDataSchemaNodes = + ParserStreamUtils.findSchemaNodeByNameAndNamespace(parentSchema, localName, getCurrentNamespace()); if (childDataSchemaNodes.isEmpty()) { throw new IllegalStateException("Schema for node with name " + localName + " and namespace " + getCurrentNamespace() + " doesn't exist."); @@ -302,57 +310,7 @@ public final class JsonParserStream implements Closeable, Flushable { return namespaces.peek(); } - /** - * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified - * {@code childName} and {@code namespace} - * - * @param dataSchemaNode - * @param childName - * @param namespace - * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack - * contains only one node. If it is found under choice and case then stack should contains 2*n+1 element - * (where n is number of choices through it was passed) - */ - private Deque findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode, - final String childName, final URI namespace) { - final Deque result = new ArrayDeque<>(); - final List childChoices = new ArrayList<>(); - DataSchemaNode potentialChildNode = null; - if (dataSchemaNode instanceof DataNodeContainer) { - for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { - if (childNode instanceof ChoiceSchemaNode) { - childChoices.add((ChoiceSchemaNode) childNode); - } else { - final QName childQName = childNode.getQName(); - - if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) { - if (potentialChildNode == null || - childQName.getRevision().after(potentialChildNode.getQName().getRevision())) { - potentialChildNode = childNode; - } - } - } - } - } - if (potentialChildNode != null) { - result.push(potentialChildNode); - return result; - } - // try to find data schema node in choice (looking for first match) - for (final ChoiceSchemaNode choiceNode : childChoices) { - for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { - final Deque resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName, - namespace); - if (!resultFromRecursion.isEmpty()) { - resultFromRecursion.push(concreteCase); - resultFromRecursion.push(choiceNode); - return resultFromRecursion; - } - } - } - return result; - } private static class NamespaceAndName { private final URI uri; diff --git a/yang/yang-data-codec-xml/pom.xml b/yang/yang-data-codec-xml/pom.xml new file mode 100644 index 0000000000..750e497f7a --- /dev/null +++ b/yang/yang-data-codec-xml/pom.xml @@ -0,0 +1,63 @@ + + + + + + org.opendaylight.yangtools + yangtools-parent + 1.0.0-SNAPSHOT + /../../common/parent/pom.xml + + + 4.0.0 + yang-data-codec-xml + ${project.artifactId} + ${project.artifactId} + + + + org.opendaylight.yangtools + yang-data-api + + + org.opendaylight.yangtools + yang-data-impl + + + org.opendaylight.yangtools + yang-data-util + + + org.opendaylight.yangtools + yang-parser-impl + test + + + junit + junit + test + + + + + ${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/ + + + + opendaylight-site + ${nexus.site.url}/${project.artifactId}/ + + + diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java new file mode 100644 index 0000000000..2568cf6f31 --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import com.google.common.base.Preconditions; +import org.opendaylight.yangtools.concepts.Codec; +import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec; +import org.opendaylight.yangtools.yang.data.api.codec.DecimalCodec; +import org.opendaylight.yangtools.yang.data.api.codec.Int16Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int32Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int64Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Int8Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint16Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint32Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint64Codec; +import org.opendaylight.yangtools.yang.data.api.codec.Uint8Codec; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; + +/** + * Abstract base implementation of {@link XmlCodec}, which wraps a {@link TypeDefinitionAwareCodec}. + * + * @param Deserialized object type + */ +abstract class AbstractXmlCodec implements XmlCodec { + + private final Codec codec; + + protected AbstractXmlCodec(final Codec codec) { + this.codec = Preconditions.checkNotNull(codec); + } + + /** + * Create a proper XmlCodec based on the underlying codec type + * @param codec underlying codec + * @return An XmlCodec instance + */ + public static XmlCodec create(final Codec codec) { + if (codec instanceof BooleanCodec) { + return new BooleanXmlCodec((BooleanCodec) codec); + } else if (codec instanceof DecimalCodec || codec instanceof Int8Codec + || codec instanceof Int16Codec || codec instanceof Int32Codec + || codec instanceof Int64Codec || codec instanceof Uint8Codec + || codec instanceof Uint16Codec || codec instanceof Uint32Codec + || codec instanceof Uint64Codec) { + return new NumberXmlCodec(codec); + } else { + return new QuotedXmlCodec(codec); + } + } + + @Override + public final T deserialize(final String input) { + return codec.deserialize(input); + } + + @Override + public final String serialize(final T input) { + return codec.serialize(input); + } +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java new file mode 100644 index 0000000000..b83df131bc --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/BooleanXmlCodec.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.concepts.Codec; + +final class BooleanXmlCodec extends AbstractXmlCodec { + + BooleanXmlCodec(final Codec codec) { + super(codec); + } + + /** + * Serialize specified value with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value value which will be serialized to the writer + */ + @Override + public void serializeToWriter(XMLStreamWriter writer, Boolean value) throws XMLStreamException { + writer.writeCharacters(String.valueOf(value)); + } +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java new file mode 100644 index 0000000000..3343832edd --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NumberXmlCodec.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.concepts.Codec; + +final class NumberXmlCodec extends AbstractXmlCodec{ + + NumberXmlCodec(final Codec codec) { + super(codec); + } + + /** + * Serialize specified value with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value value which will be serialized to the writer + */ + @Override + public void serializeToWriter(XMLStreamWriter writer, T value) throws XMLStreamException { + writer.writeCharacters(String.valueOf(value)); + } +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java new file mode 100644 index 0000000000..200094bf3f --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.concepts.Codec; + +final class QuotedXmlCodec extends AbstractXmlCodec { + + QuotedXmlCodec(final Codec codec) { + super(codec); + } + + /** + * Serialize specified value with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value value which will be serialized to the writer + */ + @Override + public void serializeToWriter(XMLStreamWriter writer, T value) throws XMLStreamException { + writer.writeCharacters(serialize(value)); + } + +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java new file mode 100644 index 0000000000..fe591358ab --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodec.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.concepts.Codec; + +interface XmlCodec extends Codec { + + /** + * Serialize specified value with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value value which will be serialized to the writer + */ + void serializeToWriter(XMLStreamWriter writer, T 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 new file mode 100644 index 0000000000..6cdb638663 --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; +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.util.SchemaContextUtil; +import org.opendaylight.yangtools.yang.model.util.type.DerivedTypes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class XmlCodecFactory { + + private static final Logger LOG = LoggerFactory.getLogger(XmlCodecFactory.class); + private static final XmlCodec NULL_CODEC = new XmlCodec() { + @Override + public Object deserialize(final String input) { + return null; + } + + @Override + public String serialize(final Object input) { + return null; + } + + @Override + public void serializeToWriter(final XMLStreamWriter writer, final Object value) throws XMLStreamException { + // NOOP since codec is unkwown. + LOG.warn("Call of the serializeToWriter method on XmlCodecFactory.NULL_CODEC object. No operation " + + "performed."); + } + }; + + private final LoadingCache> codecs = + CacheBuilder.newBuilder().softValues().build(new CacheLoader>() { + @Override + public XmlCodec load(final DataSchemaNode key) throws Exception { + final TypeDefinition type; + if (key instanceof LeafSchemaNode) { + type = ((LeafSchemaNode) key).getType(); + } else if (key instanceof LeafListSchemaNode) { + type = ((LeafListSchemaNode) key).getType(); + } else { + throw new IllegalArgumentException("Not supported node type " + key.getClass().getName()); + } + return createCodec(key,type); + } + }); + + private final SchemaContext schemaContext; + private final XmlCodec iidCodec; + + private XmlCodecFactory(final SchemaContext context) { + this.schemaContext = Preconditions.checkNotNull(context); + iidCodec = new XmlStringInstanceIdentifierCodec(context, this); + } + + /** + * Instantiate a new codec factory attached to a particular context. + * + * @param context SchemaContext to which the factory should be bound + * @return A codec factory instance. + */ + public static XmlCodecFactory create(final SchemaContext context) { + return new XmlCodecFactory(context); + } + + @SuppressWarnings("unchecked") + private XmlCodec createCodec(final DataSchemaNode key, final TypeDefinition type) { + final TypeDefinition normalizedType = DerivedTypes.derivedTypeBuilder(type, type.getPath()).build(); + if (normalizedType instanceof LeafrefTypeDefinition) { + return createReferencedTypeCodec(key, (LeafrefTypeDefinition) normalizedType); + } else if (normalizedType instanceof IdentityrefTypeDefinition) { + final XmlCodec xmlStringIdentityrefCodec = + new XmlStringIdentityrefCodec(schemaContext, key.getQName().getModule()); + return (XmlCodec) xmlStringIdentityrefCodec; + } + return createFromSimpleType(normalizedType); + } + + private XmlCodec createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type) { + // FIXME: Verify if this does indeed support leafref of leafref + final TypeDefinition referencedType = + SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema); + Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath()); + return createCodec(schema, referencedType); + } + + @SuppressWarnings("unchecked") + private XmlCodec createFromSimpleType(final TypeDefinition type) { + if (type instanceof InstanceIdentifierTypeDefinition) { + return (XmlCodec) iidCodec; + } + if (type instanceof EmptyTypeDefinition) { + return XmlEmptyCodec.INSTANCE; + } + + final TypeDefinitionAwareCodec codec = TypeDefinitionAwareCodec.from(type); + if (codec == null) { + LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName() + .getLocalName()); + return NULL_CODEC; + } + return (XmlCodec) AbstractXmlCodec.create(codec); + } + + SchemaContext getSchemaContext() { + return schemaContext; + } + + XmlCodec codecFor(final DataSchemaNode schema) { + return codecs.getUnchecked(schema); + } +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlEmptyCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlEmptyCodec.java new file mode 100644 index 0000000000..1d7abc628f --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlEmptyCodec.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +final class XmlEmptyCodec implements XmlCodec { + + static final XmlEmptyCodec INSTANCE = new XmlEmptyCodec(); + + private XmlEmptyCodec() { + + } + + @Override + public Object deserialize(final String input) { + return null; + } + + @Override + public String serialize(final Object input) { + return null; + } + + @Override + public void serializeToWriter(final XMLStreamWriter writer, final Object value) throws XMLStreamException { + writer.writeCharacters(""); + } +} 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 new file mode 100644 index 0000000000..f9e491b4e4 --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import com.google.common.base.Preconditions; +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Deque; +import java.util.HashSet; +import java.util.Set; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.yangtools.yang.data.api.schema.stream.DataSchemaNodeAwareAdaptor; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.util.AbstractNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.AnyXmlNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.CompositeNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafListEntryNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.LeafNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ListEntryNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ListNodeDataWithSchema; +import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils; +import org.opendaylight.yangtools.yang.data.util.RpcAsContainer; +import org.opendaylight.yangtools.yang.data.util.SimpleNodeDataWithSchema; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +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.YangModeledAnyXmlSchemaNode; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * This class provides functionality for parsing an XML source containing YANG-modeled data. It disallows multiple + * instances of the same element except for leaf-list and list entries. It also expects that the YANG-modeled data in + * the XML source are wrapped in a root element. + */ +public final class XmlParserStream implements Closeable, Flushable { + + private String rootElement = null; + private final SchemaAwareNormalizedNodeStreamWriter writer; + private final XmlCodecFactory codecs; + private final SchemaContext schema; + private final DataSchemaNode parentNode; + + private XmlParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, + final DataSchemaNode parentNode) { + this.schema = Preconditions.checkNotNull(schemaContext); + this.writer = DataSchemaNodeAwareAdaptor.forWriter(writer); + this.codecs = XmlCodecFactory.create(schemaContext); + this.parentNode = parentNode; + } + + public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, + final SchemaNode parentNode ) { + if (parentNode instanceof RpcDefinition) { + return new XmlParserStream(writer, schemaContext, new RpcAsContainer((RpcDefinition) parentNode)); + } + Preconditions.checkArgument(parentNode instanceof DataSchemaNode, "Instance of DataSchemaNode class awaited."); + return new XmlParserStream(writer, schemaContext, (DataSchemaNode) parentNode); + } + + public static XmlParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { + return new XmlParserStream(writer, schemaContext, schemaContext); + } + + /** + * This method parses the XML source and emits node events into a NormalizedNodeStreamWriter based on the + * YANG-modeled data contained in the XML source. + * + * @param reader + * StAX reader which is to used to walk through the XML source + * @return + * instance of XmlParserStream + * @throws XMLStreamException + * if a well-formedness error or an unexpected processing condition occurs while parsing the XML + * @throws URISyntaxException + * if the namespace URI of an XML element contains a syntax error + * @throws IOException + * if an error occurs while parsing the value of an anyxml node + * @throws ParserConfigurationException + * if an error occurs while parsing the value of an anyxml node + * @throws SAXException + * if an error occurs while parsing the value of an anyxml node + */ + public XmlParserStream parse(final XMLStreamReader reader) throws XMLStreamException, URISyntaxException, + IOException, ParserConfigurationException, SAXException { + if (reader.hasNext()) { + final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode); + reader.nextTag(); + rootElement = reader.getLocalName(); + read(reader, compositeNodeDataWithSchema); + compositeNodeDataWithSchema.write(writer); + } + + return this; + } + + private String readAnyXmlValue(XMLStreamReader in) throws XMLStreamException { + String result = ""; + String anyXmlElementName = in.getLocalName(); + + while (in.hasNext()) { + int eventType = in.next(); + + if (eventType == XMLStreamConstants.START_ELEMENT) { + result += "<" + in.getLocalName() + ">"; + } else if (eventType == XMLStreamConstants.END_ELEMENT) { + if (in.getLocalName().equals(anyXmlElementName)) { + break; + } + + result += ""; + } else if (eventType == XMLStreamConstants.CHARACTERS) { + result += in.getText(); + } + } + + return result; + } + + private void read(final XMLStreamReader in, AbstractNodeDataWithSchema parent) throws XMLStreamException, + URISyntaxException, ParserConfigurationException, SAXException, IOException { + if (in.hasNext()) { + if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) { + setValue(parent, in.getElementText().trim()); + in.nextTag(); + return; + } else if (parent instanceof LeafListNodeDataWithSchema || parent instanceof ListNodeDataWithSchema) { + String parentSchemaName = parent.getSchema().getQName().getLocalName(); + String xmlElementName = in.getLocalName(); + while (xmlElementName.equals(parentSchemaName)) { + AbstractNodeDataWithSchema newChild = newEntryNode(parent); + read(in, newChild); + xmlElementName = in.getLocalName(); + } + + return; + } else if (parent instanceof AnyXmlNodeDataWithSchema) { + setValue(parent, readAnyXmlValue(in)); + in.nextTag(); + return; + } + + switch (in.nextTag()) { + case XMLStreamConstants.START_ELEMENT: + final Set namesakes = new HashSet<>(); + while (in.hasNext()) { + String xmlElementName = in.getLocalName(); + String xmlElementNamespace = in.getNamespaceURI(); + + if (xmlElementName.equals(rootElement)) { + break; + } + + DataSchemaNode parentSchema = parent.getSchema(); + if (parentSchema instanceof YangModeledAnyXmlSchemaNode) { + parentSchema = ((YangModeledAnyXmlSchemaNode) parentSchema).getSchemaOfAnyXmlData(); + } + + String parentSchemaName = parentSchema.getQName().getLocalName(); + if (parentSchemaName.equals(xmlElementName) + && in.getEventType() == XMLStreamConstants.END_ELEMENT) { + in.nextTag(); + break; + } + + if (namesakes.contains(xmlElementName)) { + int lineNumber = in.getLocation().getLineNumber(); + int columnNumber = in.getLocation().getColumnNumber(); + throw new IllegalStateException("Duplicate element \"" + xmlElementName + "\" in XML " + + "input at: line " + lineNumber + " column " + columnNumber); + } + namesakes.add(xmlElementName); + + Deque childDataSchemaNodes = ParserStreamUtils.findSchemaNodeByNameAndNamespace( + parentSchema, xmlElementName, new URI(xmlElementNamespace)); + + if (childDataSchemaNodes.isEmpty()) { + throw new IllegalStateException("Schema for node with name " + xmlElementName + + " and namespace " + xmlElementNamespace + " doesn't exist."); + } + + AbstractNodeDataWithSchema newChild = + ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes); + + read(in, newChild); + } + break; + case XMLStreamConstants.END_ELEMENT: + in.nextTag(); + break; + } + } + } + + private void setValue(final AbstractNodeDataWithSchema parent, final String value) throws + ParserConfigurationException, SAXException, IOException { + Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", + parent.getSchema().getQName()); + final SimpleNodeDataWithSchema parentSimpleNode = (SimpleNodeDataWithSchema) parent; + Preconditions.checkArgument(parentSimpleNode.getValue() == null, "Node '%s' has already set its value to '%s'", + parentSimpleNode.getSchema().getQName(), parentSimpleNode.getValue()); + + final Object translatedValue = translateValueByType(value, parentSimpleNode.getSchema()); + parentSimpleNode.setValue(translatedValue); + } + + private Object translateValueByType(final String value, final DataSchemaNode node) throws IOException, + SAXException, ParserConfigurationException { + if (node instanceof AnyXmlSchemaNode) { + /* + * FIXME: Figure out some YANG extension dispatch, which will + * reuse JSON parsing or XML parsing - anyxml is not well-defined in + * JSON. + */ + DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = db.parse( new InputSource(new StringReader(value))); + doc.normalize(); + DOMSource anyXmlValueSource = new DOMSource(doc); + + return anyXmlValueSource; + } + return codecs.codecFor(node).deserialize(value); + } + + private AbstractNodeDataWithSchema newEntryNode(final AbstractNodeDataWithSchema parent) { + AbstractNodeDataWithSchema newChild; + if (parent instanceof ListNodeDataWithSchema) { + newChild = new ListEntryNodeDataWithSchema(parent.getSchema()); + } else { + newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema()); + } + ((CompositeNodeDataWithSchema) parent).addChild(newChild); + return newChild; + } + + @Override + public void close() throws IOException { + writer.flush(); + writer.close(); + } + + @Override + public void flush() throws IOException { + writer.flush(); + } +} diff --git a/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringIdentityrefCodec.java b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringIdentityrefCodec.java new file mode 100644 index 0000000000..2ea18600cd --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringIdentityrefCodec.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import com.google.common.base.Preconditions; +import java.net.URI; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringIdentityrefCodec; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +final class XmlStringIdentityrefCodec extends AbstractModuleStringIdentityrefCodec implements XmlCodec { + + private final SchemaContext context; + private final QNameModule parentModuleQname; + + XmlStringIdentityrefCodec(final SchemaContext context, final QNameModule parentModule) { + this.context = Preconditions.checkNotNull(context); + this.parentModuleQname = Preconditions.checkNotNull(parentModule); + } + + @Override + protected Module moduleForPrefix(final String prefix) { + if (prefix.isEmpty()) { + return context.findModuleByNamespaceAndRevision(parentModuleQname.getNamespace(), + parentModuleQname.getRevision()); + } else { + return context.findModuleByName(prefix, null); + } + } + + @Override + protected String prefixForNamespace(final URI namespace) { + final Module module = context.findModuleByNamespaceAndRevision(namespace, null); + return module == null ? null : module.getName(); + } + + /** + * Serialize QName with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value QName + */ + @Override + public void serializeToWriter(XMLStreamWriter writer, QName value) throws XMLStreamException { + writer.writeCharacters(serialize(value)); + } +} 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 new file mode 100644 index 0000000000..b3d0578c95 --- /dev/null +++ b/yang/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import com.google.common.base.Preconditions; +import java.net.URI; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec + implements XmlCodec { + + private final DataSchemaContextTree dataContextTree; + private final XmlCodecFactory codecFactory; + private final SchemaContext context; + + XmlStringInstanceIdentifierCodec(final SchemaContext context, final XmlCodecFactory jsonCodecFactory) { + this.context = Preconditions.checkNotNull(context); + this.dataContextTree = DataSchemaContextTree.from(context); + this.codecFactory = Preconditions.checkNotNull(jsonCodecFactory); + } + + @Override + protected Module moduleForPrefix(final String prefix) { + return context.findModuleByName(prefix, null); + } + + @Override + protected String prefixForNamespace(final URI namespace) { + final Module module = context.findModuleByNamespaceAndRevision(namespace, null); + return module == null ? null : module.getName(); + } + + @Override + protected DataSchemaContextTree getDataContextTree() { + return dataContextTree; + } + + @Override + protected Object deserializeKeyValue(final DataSchemaNode schemaNode, final String value) { + Preconditions.checkNotNull(schemaNode, "schemaNode cannot be null"); + Preconditions.checkArgument(schemaNode instanceof LeafSchemaNode, "schemaNode must be of type LeafSchemaNode"); + final XmlCodec objectXmlCodec = codecFactory.codecFor(schemaNode); + return objectXmlCodec.deserialize(value); + } + + /** + * Serialize YangInstanceIdentifier with specified XMLStreamWriter. + * + * @param writer XMLStreamWriter + * @param value YangInstanceIdentifier + */ + @Override + public void serializeToWriter(final XMLStreamWriter writer, final YangInstanceIdentifier value) + throws XMLStreamException { + writer.writeCharacters(serialize(value)); + } + +} diff --git a/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java new file mode 100644 index 0000000000..c57228e7a8 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlToNormalizedNodesTest.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2016 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.data.codec.xml; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.collect.Sets; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +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.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +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.Builders; +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.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; +import org.xml.sax.SAXException; + +public class XmlToNormalizedNodesTest { + + private QNameModule bazModule; + + private QName outerContainer; + + private QName myContainer1; + private QName myKeyedList; + private QName myKeyLeaf; + private QName myLeafInList1; + private QName myLeafInList2; + private QName myLeaf1; + private QName myLeafList; + + private QName myContainer2; + private QName innerContainer; + private QName myLeaf2; + private QName myLeaf3; + private QName myChoice; + private QName myLeafInCase2; + + private QName myContainer3; + private QName myDoublyKeyedList; + private QName myFirstKeyLeaf; + private QName mySecondKeyLeaf; + private QName myLeafInList3; + + @Before + public void setup() throws URISyntaxException, ParseException { + bazModule = QNameModule.create(new URI("baz-namespace"), SimpleDateFormatUtil.getRevisionFormat().parse + ("1970-01-01")); + + outerContainer = QName.create(bazModule, "outer-container"); + + myContainer1 = QName.create(bazModule, "my-container-1"); + myKeyedList = QName.create(bazModule, "my-keyed-list"); + myKeyLeaf = QName.create(bazModule, "my-key-leaf"); + myLeafInList1 = QName.create(bazModule, "my-leaf-in-list-1"); + myLeafInList2 = QName.create(bazModule, "my-leaf-in-list-2"); + myLeaf1 = QName.create(bazModule, "my-leaf-1"); + myLeafList = QName.create(bazModule, "my-leaf-list"); + + myContainer2 = QName.create(bazModule, "my-container-2"); + innerContainer = QName.create(bazModule, "inner-container"); + myLeaf2 = QName.create(bazModule, "my-leaf-2"); + myLeaf3 = QName.create(bazModule, "my-leaf-3"); + myChoice = QName.create(bazModule, "my-choice"); + myLeafInCase2 = QName.create(bazModule, "my-leaf-in-case-2"); + + myContainer3 = QName.create(bazModule, "my-container-3"); + myDoublyKeyedList = QName.create(bazModule, "my-doubly-keyed-list"); + myFirstKeyLeaf = QName.create(bazModule, "my-first-key-leaf"); + mySecondKeyLeaf = QName.create(bazModule, "my-second-key-leaf"); + myLeafInList3 = QName.create(bazModule, "my-leaf-in-list-3"); + } + + @Test + public void testComplexXmlParsing() throws IOException, URISyntaxException, ReactorException, XMLStreamException, + ParserConfigurationException, SAXException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/baz.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + xmlParser.parse(reader); + + final NormalizedNode transformedInput = result.getResult(); + assertNotNull(transformedInput); + + final NormalizedNode expectedNormalizedNode = buildOuterContainerNode(); + assertNotNull(expectedNormalizedNode); + + assertEquals(expectedNormalizedNode, transformedInput); + } + + @Test + public void testSimpleXmlParsing() throws IOException, URISyntaxException, ReactorException, XMLStreamException, + ParserConfigurationException, SAXException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/foo.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + xmlParser.parse(reader); + + final NormalizedNode transformedInput = result.getResult(); + assertNotNull(transformedInput); + } + + @Test + public void shouldFailOnDuplicateLeaf() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/foo.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("IllegalStateException should have been thrown because of duplicate leaf."); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("Duplicate element \"decimal64-leaf\" in XML input")); + } + + } + + @Test + public void shouldFailOnDuplicateAnyXml() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/foo.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("IllegalStateException should have been thrown because of duplicate anyxml"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("Duplicate element \"my-anyxml\" in XML input")); + } + } + + @Test + public void shouldFailOnDuplicateContainer() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/foo.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("IllegalStateException should have been thrown because of duplicate container"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("Duplicate element \"leaf-container\" in XML input")); + } + } + + @Test + public void shouldFailOnUnterminatedLeafElement() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/baz.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("XMLStreamException should have been thrown because of unterminated leaf element."); + } catch (XMLStreamException ex) { + assertTrue(ex.getMessage().contains("elementGetText() function expects text only elment but " + + "START_ELEMENT was encountered.")); + } + } + + @Test + public void shouldFailOnUnterminatedLeafElement2() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/baz.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("XMLStreamException should have been thrown because of unterminated leaf element."); + } catch (XMLStreamException ex) { + assertTrue(ex.getMessage().contains("The element type \"my-leaf-1\" must be terminated by the matching " + + "end-tag \"\".")); + } + } + + @Test + public void shouldFailOnUnterminatedContainerElement() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/baz.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("XMLStreamException should have been thrown because of unterminated container element."); + } catch (XMLStreamException ex) { + assertTrue(ex.getMessage().contains("The element type \"my-container-1\" must be terminated by the " + + "matching end-tag \"\".")); + } + } + + @Test + public void shouldFailOnUnexistingContainerElement() throws ReactorException, XMLStreamException, IOException, + ParserConfigurationException, SAXException, URISyntaxException { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + reactor.addSource(new YangStatementSourceImpl("/baz.yang", false)); + + SchemaContext schemaContext = reactor.buildEffective(); + + final InputStream resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml"); + + final XMLInputFactory factory = XMLInputFactory.newInstance(); + final XMLStreamReader reader = factory.createXMLStreamReader(resourceAsStream); + + final NormalizedNodeResult result = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + + final XmlParserStream xmlParser = XmlParserStream.create(streamWriter, schemaContext); + try { + xmlParser.parse(reader); + fail("IllegalStateException should have been thrown because of an unexisting container element."); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("Schema for node with name my-container-1 and namespace baz-namespace" + + " doesn't exist.")); + } + } + + private NormalizedNode buildOuterContainerNode() { + // my-container-1 + MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myKeyedList)) + .withChild(Builders.mapEntryBuilder().withNodeIdentifier( + new NodeIdentifierWithPredicates(myKeyedList, myKeyLeaf, "listkeyvalue1")) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1)) + .withValue("listleafvalue1").build()) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2)) + .withValue("listleafvalue2").build()).build()) + .withChild(Builders.mapEntryBuilder().withNodeIdentifier( + new NodeIdentifierWithPredicates(myKeyedList, myKeyLeaf, "listkeyvalue2")) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList1)) + .withValue("listleafvalue12").build()) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInList2)) + .withValue("listleafvalue22").build()).build()).build(); + + LeafNode myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf1)) + .withValue("value1").build(); + + LeafSetNode myLeafListNode = Builders.leafSetBuilder().withNodeIdentifier(new NodeIdentifier(myLeafList)) + .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier( + new NodeWithValue<>(myLeafList, "lflvalue1")).withValue("lflvalue1").build()) + .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier( + new NodeWithValue<>(myLeafList, "lflvalue2")).withValue("lflvalue2").build()).build(); + + ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(myContainer1)) + .withChild(myKeyedListNode) + .withChild(myLeaf1Node) + .withChild(myLeafListNode).build(); + + // my-container-2 + ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(innerContainer)) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf2)) + .withValue("value2").build()).build(); + + LeafNode myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeaf3)) + .withValue("value3").build(); + + ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(myChoice)) + .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(myLeafInCase2)) + .withValue("case2value").build()).build(); + + ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(myContainer2)) + .withChild(innerContainerNode) + .withChild(myLeaf3Node) + .withChild(myChoiceNode).build(); + + // my-container-3 + Map keys = new HashMap<>(); + keys.put(myFirstKeyLeaf, "listkeyvalue1"); + keys.put(mySecondKeyLeaf, "listkeyvalue2"); + + MapNode myDoublyKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(myDoublyKeyedList)) + .withChild(Builders.mapEntryBuilder().withNodeIdentifier( + new NodeIdentifierWithPredicates(myDoublyKeyedList, keys)) + .withChild(Builders.leafBuilder().withNodeIdentifier( + new NodeIdentifier(myLeafInList3)).withValue("listleafvalue1").build()).build()) + .build(); + + AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier( + new AugmentationIdentifier(Sets.newHashSet(myDoublyKeyedList))) + .withChild(myDoublyKeyedListNode).build(); + + ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(myContainer3)) + .withChild(myDoublyKeyedListAugNode).build(); + + AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier( + new AugmentationIdentifier(Sets.newHashSet(myContainer3))) + .withChild(myContainer3Node).build(); + + ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(outerContainer)) + .withChild(myContainer1Node) + .withChild(myContainer2Node) + .withChild(myContainer3AugNode).build(); + + return outerContainerNode; + } +} diff --git a/yang/yang-data-codec-xml/src/test/resources/baz.xml b/yang/yang-data-codec-xml/src/test/resources/baz.xml new file mode 100644 index 0000000000..48f4510bca --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/baz.xml @@ -0,0 +1,40 @@ + + + + + + + listkeyvalue1 + listleafvalue1 + listleafvalue2 + + + + listkeyvalue2 + listleafvalue12 + listleafvalue22 + + + value1 + + lflvalue1 + lflvalue2 + + + + + value2 + + value3 + case2value + + + + + listkeyvalue1 + listkeyvalue2 + listleafvalue1 + + + + diff --git a/yang/yang-data-codec-xml/src/test/resources/baz.yang b/yang/yang-data-codec-xml/src/test/resources/baz.yang new file mode 100644 index 0000000000..d61077ef84 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/baz.yang @@ -0,0 +1,82 @@ +module baz { + namespace "baz-namespace"; + prefix bz; + + container outer-container { + container my-container-1 { + list my-keyed-list { + key "my-key-leaf"; + + leaf my-key-leaf { + type string; + } + + leaf my-leaf-in-list-1 { + type string; + } + + leaf my-leaf-in-list-2 { + type string; + } + } + + leaf my-leaf-1 { + type string; + } + + leaf-list my-leaf-list { + type string; + } + } + + container my-container-2 { + container inner-container { + leaf my-leaf-2 { + type string; + } + } + + leaf my-leaf-3 { + type string; + } + + choice my-choice { + case my-case-1 { + leaf my-leaf-in-case-1 { + type string; + } + } + + case my-case-2 { + leaf my-leaf-in-case-2 { + type string; + } + } + } + } + } + + augment "/outer-container" { + container my-container-3 { + + } + } + + augment "/outer-container/my-container-3" { + list my-doubly-keyed-list { + key "my-first-key-leaf my-second-key-leaf"; + + leaf "my-first-key-leaf" { + type string; + } + + leaf my-second-key-leaf { + type string; + } + + leaf my-leaf-in-list-3 { + type string; + } + } + } +} \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/foo.xml b/yang/yang-data-codec-xml/src/test/resources/foo.xml new file mode 100644 index 0000000000..9af808eb14 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/foo.xml @@ -0,0 +1,18 @@ + + + + + + 1500 + 150.45 + hello world + hello world + + true + five + + + sub-element value + + + diff --git a/yang/yang-data-codec-xml/src/test/resources/foo.yang b/yang/yang-data-codec-xml/src/test/resources/foo.yang new file mode 100644 index 0000000000..d089245ece --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/foo.yang @@ -0,0 +1,54 @@ +module foo { + namespace "foo-namespace"; + prefix "f"; + + container parent-container { + + container leaf-container { + + leaf int32-leaf { + type int32; + } + + leaf decimal64-leaf { + type decimal64 { + fraction-digits 2; + } + } + + leaf string-leaf { + type string; + } + + leaf leafref-leaf { + type leafref { + path "../string-leaf"; + } + } + + leaf empty-leaf { + type empty; + } + + leaf boolean-leaf { + type boolean; + } + + leaf enum-leaf { + type enumeration { + enum zero; + enum one; + enum five { + value 5; + } + } + } + } + + container anyxml-container { + anyxml my-anyxml; + } + } + + +} \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml new file mode 100644 index 0000000000..ef9fc35414 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-2.xml @@ -0,0 +1,40 @@ + + + + + + + listkeyvalue1 + listleafvalue1 + listleafvalue2 + + + + listkeyvalue2 + listleafvalue12 + listleafvalue22 + + + value1 + + lflvalue1 + lflvalue2 + + + + + value2 + + value3 + case2value + + + + + listkeyvalue1 + listkeyvalue2 + listleafvalue3 + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml new file mode 100644 index 0000000000..2219b1b2a4 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-3.xml @@ -0,0 +1,40 @@ + + + + + + + listkeyvalue1 + listleafvalue1 + listleafvalue2 + + + + listkeyvalue2 + listleafvalue12 + listleafvalue22 + + + value1 + + lflvalue1 + lflvalue2 + + + + + value2 + + value3 + case2value + + + + + listkeyvalue1 + listkeyvalue2 + listleafvalue3 + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml new file mode 100644 index 0000000000..f4eae6f2e5 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-baz-4.xml @@ -0,0 +1,40 @@ + + + + + + + listkeyvalue1 + listleafvalue1 + listleafvalue2 + + + + listkeyvalue2 + listleafvalue12 + listleafvalue22 + + + value1 + + lflvalue1 + lflvalue2 + + + + + value2 + + value3 + case2value + + + + + listkeyvalue1 + listkeyvalue2 + listleafvalue3 + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml new file mode 100644 index 0000000000..51da202271 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-baz.xml @@ -0,0 +1,40 @@ + + + + + + + listkeyvalue1 + listleafvalue1 + listleafvalue2 + + + + listkeyvalue2 + listleafvalue12 + listleafvalue22 + + + value1 + + lflvalue1 + lflvalue2 + + + + + value2 + + value3 + case2value + + + + + listkeyvalue1 + listkeyvalue2 + listleafvalue3 + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml new file mode 100644 index 0000000000..69deaae02d --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-2.xml @@ -0,0 +1,27 @@ + + + + + + 1500 + 150.45 + hello world + hello world + + true + five + + + + + sub-element value + + + + + sub-element value 2 + + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml new file mode 100644 index 0000000000..b043f49c7a --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-foo-3.xml @@ -0,0 +1,27 @@ + + + + + + 1500 + 150.45 + hello world + hello world + + true + five + + + 2500 + 250.45 + goodbye world + + + + + sub-element value + + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml b/yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml new file mode 100644 index 0000000000..3f18dcf466 --- /dev/null +++ b/yang/yang-data-codec-xml/src/test/resources/invalid-foo.xml @@ -0,0 +1,23 @@ + + + + + + 1500 + 150.45 + 240.35 + hello world + hello world + + true + five + + + + + sub-element value + + + + + \ No newline at end of file diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java similarity index 89% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java index fd4fabfe89..0d5234bdca 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNodeDataWithSchema.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; @@ -19,10 +19,10 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; * Utility abstract class for tracking parser state, as needed by StAX-like parser. */ @Beta -abstract class AbstractNodeDataWithSchema { +public abstract class AbstractNodeDataWithSchema { private final DataSchemaNode schema; - protected AbstractNodeDataWithSchema(final DataSchemaNode schema) { + public AbstractNodeDataWithSchema(final DataSchemaNode schema) { this.schema = Preconditions.checkNotNull(schema); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java similarity index 69% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java index 76a960bfd8..873e280574 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AnyXmlNodeDataWithSchema.java @@ -1,17 +1,17 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema { +public class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema { public AnyXmlNodeDataWithSchema(final DataSchemaNode dataSchemaNode) { super(dataSchemaNode); @@ -20,7 +20,6 @@ class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema { @Override public void write(final SchemaAwareNormalizedNodeStreamWriter writer) throws IOException { writer.nextDataSchemaNode(getSchema()); - // FIXME: should be changed according to format of value - writer.anyxmlNode(provideNodeIdentifier(), getValue()); + writer.leafNode(provideNodeIdentifier(), getValue()); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java similarity index 68% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java index 67e3c59389..b04d387588 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CaseNodeDataWithSchema.java @@ -1,16 +1,17 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; + import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema { - public CaseNodeDataWithSchema(final ChoiceCaseNode schema) { + CaseNodeDataWithSchema(final ChoiceCaseNode schema) { super(schema); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java similarity index 88% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java index b246959835..e4813a9a55 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ChoiceNodeDataWithSchema.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; @@ -22,7 +22,7 @@ class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema { private CaseNodeDataWithSchema caseNodeDataWithSchema; - public ChoiceNodeDataWithSchema(final ChoiceSchemaNode schema) { + ChoiceNodeDataWithSchema(final ChoiceSchemaNode schema) { super(schema); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java similarity index 77% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java index 4d40db27f7..d3a53bd520 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java @@ -1,14 +1,17 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; +import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import java.io.IOException; import java.util.ArrayList; @@ -16,10 +19,13 @@ import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Map.Entry; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; @@ -32,7 +38,7 @@ import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode; /** * A node which is composed of multiple simpler nodes. */ -class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { +public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { /** * nodes which were added to schema via augmentation and are present in data input @@ -71,7 +77,7 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { AugmentationSchema augSchema = null; if (choiceCandidate.isAugmenting()) { - augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), choiceCandidate); + augSchema = findCorrespondingAugment(getSchema(), choiceCandidate); } // looking for existing choice @@ -108,7 +114,7 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { AugmentationSchema augSchema = null; if (schema.isAugmenting()) { - augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), schema); + augSchema = findCorrespondingAugment(getSchema(), schema); } if (augSchema != null) { augmentationsToChild.put(augSchema, newChild); @@ -157,7 +163,7 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } void addCompositeChild(final CompositeNodeDataWithSchema newChild) { - AugmentationSchema augSchema = SchemaUtils.findCorrespondingAugment(getSchema(), newChild.getSchema()); + AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema()); if (augSchema != null) { augmentationsToChild.put(augSchema, newChild); } else { @@ -191,7 +197,7 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { final Collection childsFromAgumentation = augmentationToChild.getValue(); if (!childsFromAgumentation.isEmpty()) { // FIXME: can we get the augmentation schema? - writer.startAugmentationNode(SchemaUtils.getNodeIdentifierForAugmentation(augmentationToChild.getKey())); + writer.startAugmentationNode(getNodeIdentifierForAugmentation(augmentationToChild.getKey())); for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) { nodeDataWithSchema.write(writer); @@ -201,4 +207,36 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } } } + + /** + * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such + * node is found then it is returned, else null. + * + * @param parent parent node + * @param child child node + * @return augmentation schema + */ + private AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) { + if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) { + for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) { + DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName()); + if (childInAugmentation != null) { + return augmentation; + } + } + } + return null; + } + + public static YangInstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) { + final Collection qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION); + return new YangInstanceIdentifier.AugmentationIdentifier(ImmutableSet.copyOf(qnames)); + } + + private static final Function QNAME_FUNCTION = new Function() { + @Override + public QName apply(@Nonnull final DataSchemaNode input) { + return input.getQName(); + } + }; } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java similarity index 81% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java index 68371878fe..ef16192c79 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ContainerNodeDataWithSchema.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; @@ -13,7 +13,7 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema { - public ContainerNodeDataWithSchema(final DataSchemaNode schema) { + ContainerNodeDataWithSchema(final DataSchemaNode schema) { super(schema); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java similarity index 80% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java index 623b82af65..24ce4b9b69 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListEntryNodeDataWithSchema.java @@ -1,17 +1,17 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema { +public class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema { public LeafListEntryNodeDataWithSchema(final DataSchemaNode dataSchemaNode) { super(dataSchemaNode); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java similarity index 85% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java index 5033ddf275..7f1f3e7f01 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafListNodeDataWithSchema.java @@ -1,18 +1,18 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema { +public class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema { public LeafListNodeDataWithSchema(final DataSchemaNode schema) { super(schema); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java similarity index 80% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java index 32263d68dc..6f241cb7b5 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafNodeDataWithSchema.java @@ -1,17 +1,17 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema { +public class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema { public LeafNodeDataWithSchema(final DataSchemaNode schema) { super(schema); diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java similarity index 93% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java index e3581354ad..e9f06f9999 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListEntryNodeDataWithSchema.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import com.google.common.base.Preconditions; import java.io.IOException; @@ -21,7 +21,7 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { +public class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { private final Map qNameToKeys = new HashMap<>(); diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java similarity index 86% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java index abbf1eaec4..36690e4522 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ListNodeDataWithSchema.java @@ -1,18 +1,18 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -class ListNodeDataWithSchema extends CompositeNodeDataWithSchema { +public class ListNodeDataWithSchema extends CompositeNodeDataWithSchema { public ListNodeDataWithSchema(final DataSchemaNode schema) { super(schema); diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java new file mode 100644 index 0000000000..bc3228a6de --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/ParserStreamUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 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.data.util; + +import java.net.URI; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +public final class ParserStreamUtils { + + private ParserStreamUtils() { + throw new UnsupportedOperationException("Utility class should not be instantiated."); + } + + /** + * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified + * {@code childName} and {@code namespace} + * + * @param dataSchemaNode + * @param childName + * @param namespace + * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack + * contains only one node. If it is found under choice and case then stack should contains 2*n+1 element + * (where n is number of choices through it was passed) + */ + public static Deque findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode, + final String childName, final URI namespace) { + final Deque result = new ArrayDeque<>(); + final List childChoices = new ArrayList<>(); + DataSchemaNode potentialChildNode = null; + if (dataSchemaNode instanceof DataNodeContainer) { + for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { + if (childNode instanceof ChoiceSchemaNode) { + childChoices.add((ChoiceSchemaNode) childNode); + } else { + final QName childQName = childNode.getQName(); + + if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) { + if (potentialChildNode == null || + childQName.getRevision().after(potentialChildNode.getQName().getRevision())) { + potentialChildNode = childNode; + } + } + } + } + } + if (potentialChildNode != null) { + result.push(potentialChildNode); + return result; + } + + // try to find data schema node in choice (looking for first match) + for (final ChoiceSchemaNode choiceNode : childChoices) { + for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { + final Deque resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName, + namespace); + if (!resultFromRecursion.isEmpty()) { + resultFromRecursion.push(concreteCase); + resultFromRecursion.push(choiceNode); + return resultFromRecursion; + } + } + } + return result; + } +} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RpcAsContainer.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/RpcAsContainer.java similarity index 93% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RpcAsContainer.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/RpcAsContainer.java index b9fddf3ecf..da1c5a8d4d 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/RpcAsContainer.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/RpcAsContainer.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.util.ArrayList; import java.util.Collection; @@ -25,7 +25,7 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; import org.opendaylight.yangtools.yang.model.api.UsesNode; -final class RpcAsContainer implements ContainerSchemaNode { +public final class RpcAsContainer implements ContainerSchemaNode { private final RpcDefinition delegate; @@ -62,7 +62,7 @@ final class RpcAsContainer implements ContainerSchemaNode { return delegate.getOutput(); } - RpcAsContainer(final RpcDefinition parentNode) { + public RpcAsContainer(final RpcDefinition parentNode) { delegate = parentNode; } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java similarity index 69% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java index 26d774cde2..fd0b0e2baa 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/SimpleNodeDataWithSchema.java @@ -1,15 +1,15 @@ /* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema { +public abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema { private Object value; @@ -17,7 +17,7 @@ abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema { super(dataSchemaNode); } - void setValue(final Object value) { + public void setValue(final Object value) { this.value = value; } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java similarity index 89% rename from yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlNodeDataWithSchema.java rename to yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java index d89bcefbc6..a8f89ec974 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/YangModeledAnyXmlNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/YangModeledAnyXmlNodeDataWithSchema.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * Copyright (c) 2016 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.data.codec.gson; +package org.opendaylight.yangtools.yang.data.util; import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; -- 2.36.6