BUG 1440 - json stream to normalized node stream writer 33/10133/9
authorJozef Gloncak <jgloncak@cisco.com>
Thu, 21 Aug 2014 13:00:10 +0000 (15:00 +0200)
committerRobert Varga <rovarga@cisco.com>
Sat, 23 Aug 2014 01:23:30 +0000 (03:23 +0200)
JsonParserStream contains implementation of read method in which there
is gradually constructed structure of NodeDataWithSchema nodes.

NodeDataWithSchema is similar to SchemaNode but contains only reference to
children which really exists in JSON input and concrete values for nodes
of type leaf or leaf list.

It was necessary to firstly load JSON to NodeDataWitchSchema structure
because for some types (composite keys of list, all nodes belonging to one
augment, all nodes belonging to one choice) it isn't possible to do
sequential processing (it means to call concrete opening and closing
methods of NormalizedNodeStreamWriter interface).

JsonParserStream constructor requires schema context as input parameter.
If data behind mount point should be parsed then schema context of mount
point is required.

Change-Id: I28c1d3193792feb875ec2ceda0035ca56bfa5d42
Signed-off-by: Jozef Gloncak <jgloncak@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
31 files changed:
yang/pom.xml
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java [new file with mode: 0644]
yang/yang-data-codec-gson/pom.xml [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang [new file with mode: 0644]

index a7e7cc77ada2232130cc69f2ce53e7cf9858615c..2e12378abbe72c837203188ccc92c6859202a9d2 100644 (file)
@@ -22,6 +22,7 @@
         <module>yang-data-util</module>
         <module>yang-data-impl</module>
         <module>yang-data-operations</module>
+        <module>yang-data-codec-gson</module>
         <module>yang-model-api</module>
         <module>yang-maven-plugin</module>
         <module>yang-maven-plugin-it</module>
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/LoggingNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..83a8c33
--- /dev/null
@@ -0,0 +1,119 @@
+package org.opendaylight.yangtools.yang.data.api.schema.stream;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public class LoggingNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    private static final Logger LOG = LoggerFactory.getLogger(LoggingNormalizedNodeStreamWriter.class);
+    private static final int DEFAULT_INDENT_SIZE = 2;
+    private final int indentSize = DEFAULT_INDENT_SIZE;
+    private int currentIndent = 0;
+
+    private String ind() {
+        return Strings.repeat(" ", currentIndent);
+    }
+
+    private void decIndent() {
+        Preconditions.checkState(currentIndent >= 0, "Unexpected indentation %s", currentIndent);
+        currentIndent -= indentSize;
+    }
+
+    private void incIndent() {
+        currentIndent += indentSize;
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IllegalStateException {
+        LOG.debug("{}{}[](no key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        LOG.debug("{}{}(no key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        LOG.debug("{}{}(key)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IllegalArgumentException {
+        LOG.debug("{}{}[](key)", ind(), identifier);
+        incIndent();
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        LOG.debug("{}{}(leaf-list)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        LOG.debug("{}{}(container)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        LOG.debug("{}{}(choice)", ind(), name);
+        incIndent();
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+        LOG.debug("{}{}(augmentation)", ind(), identifier);
+        incIndent();
+    }
+
+    @Override
+    public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+        LOG.debug("{}{}({}) ", ind(), value, value.getClass().getSimpleName());
+    }
+
+    @Override
+    public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        LOG.debug("{}{}(leaf({}))=", ind(), name, value.getClass().getSimpleName(), value);
+    }
+
+    @Override
+    public void endNode() throws IllegalStateException {
+        decIndent();
+        LOG.debug("{}(end)", ind());
+    }
+
+    @Override
+    public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+        LOG.debug("{}{}(anyxml)=", ind(), name, value);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        LOG.trace("<<FLUSH>>");
+    }
+
+    @Override
+    public void close() throws IOException {
+        LOG.debug("<<END-OF-STREAM>>");
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..a0068c3
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2014 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.api.schema.stream;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+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.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+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.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+/**
+ * This is an experimental
+ */
+@Beta
+public final class NormalizedNodeWriter implements Closeable, Flushable {
+    private final NormalizedNodeStreamWriter writer;
+
+    private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+        this.writer = Preconditions.checkNotNull(writer);
+    }
+
+    public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+        return new NormalizedNodeWriter(writer);
+    }
+
+    public NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+        if (wasProcessedAsCompositeNode(node)) {
+            return this;
+        }
+
+        if (wasProcessAsSimpleNode(node)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof LeafSetEntryNode) {
+            final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
+            writer.leafSetEntryNode(nodeAsLeafList.getValue());
+            return true;
+        } else if (node instanceof LeafNode) {
+            final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+            writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+            return true;
+        } else if (node instanceof AnyXmlNode) {
+            final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+            writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+        boolean hasDataContainerChild = false;
+        if (node instanceof ContainerNode) {
+            writer.startContainerNode(((ContainerNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof MapEntryNode) {
+            writer.startMapEntryNode(((MapEntryNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof UnkeyedListEntryNode) {
+            writer.startUnkeyedListItem(((UnkeyedListEntryNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof ChoiceNode) {
+            writer.startChoiceNode(((ChoiceNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof AugmentationNode) {
+            writer.startAugmentationNode(((AugmentationNode) node).getIdentifier());
+            hasDataContainerChild = true;
+        } else if (node instanceof UnkeyedListNode) {
+            writer.startUnkeyedList(((UnkeyedListNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof OrderedMapNode) {
+            writer.startOrderedMapNode(((OrderedMapNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        } else if (node instanceof MapNode) {
+            writer.startMapNode(((MapNode) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+          //covers also OrderedLeafSetNode for which doesn't exist start* method
+        } else if (node instanceof LeafSetNode) {
+            writer.startLeafSet(((LeafSetNode<?>) node).getIdentifier(), UNKNOWN_SIZE);
+            hasDataContainerChild = true;
+        }
+
+        if (hasDataContainerChild) {
+            for (NormalizedNode<?, ?> childNode : ((NormalizedNode<?, Iterable<NormalizedNode<?, ?>>>) node).getValue()) {
+                write(childNode);
+            }
+
+            writer.endNode();
+            return true;
+        }
+        return false;
+
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.close();
+    }
+}
diff --git a/yang/yang-data-codec-gson/pom.xml b/yang/yang-data-codec-gson/pom.xml
new file mode 100644 (file)
index 0000000..c8e1719
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yangtools-parent</artifactId>
+        <version>0.6.2-SNAPSHOT</version>
+        <relativePath>/../../common/parent/pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yang-data-codec-gson</artifactId>
+    <name>${project.artifactId}</name>
+    <description>${project.artifactId}</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-data-impl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-parser-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..3284df2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractNodeDataWithSchema {
+
+    private final DataSchemaNode schema;
+
+    public AbstractNodeDataWithSchema(final DataSchemaNode schema) {
+        this.schema = schema;
+    }
+
+    public final DataSchemaNode getSchema() {
+        return schema;
+    }
+
+    protected abstract void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException;
+
+    protected NodeIdentifier provideNodeIdentifier() {
+        return new NodeIdentifier(schema.getQName());
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AbstractNodeDataWithSchema other = (AbstractNodeDataWithSchema) obj;
+        if (schema == null) {
+            if (other.schema != null) {
+                return false;
+            }
+        } else if (!schema.equals(other.schema)) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..aaef749
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+    public AnyXmlNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+//      FIXME: should be changed according to format of value
+        nnStreamWriter.anyxmlNode(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-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..e3fdaed
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+
+class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+    public CaseNodeDataWithSchema(final ChoiceCaseNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        super.writeToStream(nnStreamWriter);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..39c1589
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014 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;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ *
+ * childs - empty augment - only one element can be
+ *
+ */
+class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+    private CaseNodeDataWithSchema caseNodeDataWithSchema;
+
+    public ChoiceNodeDataWithSchema(final ChoiceNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public CompositeNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+        CaseNodeDataWithSchema newChild = new CaseNodeDataWithSchema((ChoiceCaseNode) schema);
+        caseNodeDataWithSchema = newChild;
+        addCompositeChild(newChild);
+        return newChild;
+    }
+
+    public CaseNodeDataWithSchema getCase() {
+        return caseNodeDataWithSchema;
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        nnStreamWriter.startChoiceNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+        super.writeToStream(nnStreamWriter);
+        nnStreamWriter.endNode();
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..83d715c
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+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.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.ListSchemaNode;
+
+class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+    /**
+     * nodes which were added to schema via augmentation and are present in data input
+     */
+    protected Map<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationsToChild = new HashMap<>();
+
+    /**
+     * remaining data nodes (which aren't added via augment). Every of them should have the same QName
+     */
+    protected List<AbstractNodeDataWithSchema> childs = new ArrayList<>();
+
+    public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    public AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
+        SimpleNodeDataWithSchema newChild = null;
+        if (schema instanceof LeafSchemaNode) {
+            newChild = new LeafNodeDataWithSchema(schema);
+        } else if (schema instanceof AnyXmlSchemaNode) {
+            newChild = new AnyXmlNodeDataWithSchema(schema);
+        }
+
+        if (newChild != null) {
+
+            AugmentationSchema augSchema = null;
+            if (schema.isAugmenting()) {
+                augSchema = findCorrespondingAugment(getSchema(), schema);
+            }
+            if (augSchema != null) {
+                addChildToAugmentation(augSchema, newChild);
+            } else {
+                addChild(newChild);
+            }
+            return newChild;
+        }
+        return null;
+    }
+
+    private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) {
+        List<AbstractNodeDataWithSchema> childsInAugment = augmentationsToChild.get(augSchema);
+        if (childsInAugment == null) {
+            childsInAugment = new ArrayList<>();
+            augmentationsToChild.put(augSchema, childsInAugment);
+        }
+        childsInAugment.add(newChild);
+    }
+
+    public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
+        if (schemas.size() == 1) {
+            final DataSchemaNode childDataSchemaNode = schemas.pop();
+            return addChild(childDataSchemaNode);
+        } else {
+            DataSchemaNode choiceCandidate = schemas.pop();
+            DataSchemaNode caseCandidate = schemas.pop();
+            ChoiceNode choiceNode = null;
+            ChoiceCaseNode caseNode = null;
+            if (choiceCandidate instanceof ChoiceNode) {
+                choiceNode = (ChoiceNode) choiceCandidate;
+            } else {
+                throw new IllegalArgumentException("Awaited node of type ChoiceNode but was "
+                        + choiceCandidate.getClass().getSimpleName());
+            }
+
+            if (caseCandidate instanceof ChoiceCaseNode) {
+                caseNode = (ChoiceCaseNode) caseCandidate;
+            } else {
+                throw new IllegalArgumentException("Awaited node of type ChoiceCaseNode but was "
+                        + caseCandidate.getClass().getSimpleName());
+            }
+
+            AugmentationSchema augSchema = null;
+            if (choiceCandidate.isAugmenting()) {
+                augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
+            }
+
+            // looking for existing choice
+            List<AbstractNodeDataWithSchema> childNodes = Collections.emptyList();
+            if (augSchema != null) {
+                childNodes = augmentationsToChild.get(augSchema);
+            } else {
+                childNodes = childs;
+            }
+
+            CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
+            if (caseNodeDataWithSchema == null) {
+                ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
+                addChild(choiceNodeDataWithSchema);
+                caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
+            }
+
+            return caseNodeDataWithSchema.addChild(schemas);
+        }
+
+    }
+
+    private CaseNodeDataWithSchema findChoice(final List<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
+            final DataSchemaNode caseCandidate) {
+        if (childNodes == null) {
+            return null;
+        }
+        for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
+            if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
+                    && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
+                CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
+                if (casePrevious.getSchema().getQName() != caseCandidate.getQName()) {
+                    throw new IllegalArgumentException("Data from case " + caseCandidate.getQName()
+                            + " are specified but other data from case " + casePrevious.getSchema().getQName()
+                            + " were specified erlier. Data aren't from the same case.");
+                }
+                return casePrevious;
+            }
+        }
+        return null;
+    }
+
+    public AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+        CompositeNodeDataWithSchema newChild;
+        if (schema instanceof ListSchemaNode) {
+            newChild = new ListNodeDataWithSchema(schema);
+        } else if (schema instanceof LeafListSchemaNode) {
+            newChild = new LeafListNodeDataWithSchema(schema);
+        } else if (schema instanceof ContainerSchemaNode) {
+            newChild = new ContainerNodeDataWithSchema(schema);
+        } else {
+            newChild = new CompositeNodeDataWithSchema(schema);
+        }
+        addCompositeChild(newChild);
+        return newChild;
+    }
+
+    public void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
+        AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
+        if (augSchema != null) {
+            addChildToAugmentation(augSchema, newChild);
+        } else {
+            addChild(newChild);
+        }
+    }
+
+    private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
+        AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
+        return newChild == null ? addCompositeChild(schema) : newChild;
+    }
+
+    public void addChild(final AbstractNodeDataWithSchema newChild) {
+        childs.add(newChild);
+    }
+
+    /**
+     * 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.
+     */
+    protected AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
+        if (parent instanceof AugmentationTarget) {
+            for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+                DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+                if (childInAugmentation != null) {
+                    return augmentation;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        for (AbstractNodeDataWithSchema child : childs) {
+            child.writeToStream(nnStreamWriter);
+        }
+        for (Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.entrySet()) {
+
+            final List<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
+
+            if (!childsFromAgumentation.isEmpty()) {
+                nnStreamWriter.startAugmentationNode(toAugmentationIdentifier(augmentationToChild));
+
+                for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
+                    nodeDataWithSchema.writeToStream(nnStreamWriter);
+                }
+
+                nnStreamWriter.endNode();
+            }
+        }
+    }
+
+    private static AugmentationIdentifier toAugmentationIdentifier(
+            final Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild) {
+        Collection<DataSchemaNode> nodes = augmentationToChild.getKey().getChildNodes();
+        Set<QName> nodesQNames = new HashSet<>();
+        for (DataSchemaNode node : nodes) {
+            nodesQNames.add(node.getQName());
+        }
+
+        return new AugmentationIdentifier(nodesQNames);
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..c49d71b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 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;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+    public ContainerNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        nnStreamWriter.startContainerNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+        super.writeToStream(nnStreamWriter);
+        nnStreamWriter.endNode();
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..16428cd
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2014 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;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This implementation will create JSON output as output stream.
+ *
+ * Values of leaf and leaf-list are NOT translated according to codecs.
+ *
+ * FIXME: rewrite this in terms of {@link JsonWriter}.
+ */
+public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+
+    private static enum NodeType {
+        OBJECT,
+        LIST,
+        OTHER,
+    }
+
+    private static class TypeInfo {
+        private boolean hasAtLeastOneChild = false;
+        private final NodeType type;
+        private final URI uri;
+
+        public TypeInfo(final NodeType type, final URI uri) {
+            this.type = type;
+            this.uri = uri;
+        }
+
+        public void setHasAtLeastOneChild(final boolean hasChildren) {
+            this.hasAtLeastOneChild = hasChildren;
+        }
+
+        public NodeType getType() {
+            return type;
+        }
+
+        public URI getNamespace() {
+            return uri;
+        }
+
+        public boolean hasAtLeastOneChild() {
+            return hasAtLeastOneChild;
+        }
+    }
+
+    private final Deque<TypeInfo> stack = new ArrayDeque<>();
+    private final SchemaContext schemaContext;
+    private final Writer writer;
+    private final String indent;
+
+    private URI currentNamespace = null;
+    private int currentDepth = 0;
+
+    private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
+            final Writer writer, final int indentSize) {
+        this.schemaContext = Preconditions.checkNotNull(schemaContext);
+        this.writer = Preconditions.checkNotNull(writer);
+
+        Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative");
+
+        if (indentSize != 0) {
+            indent = Strings.repeat(" ", indentSize);
+        } else {
+            indent = null;
+        }
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified {@link Writer}.
+     *
+     * @param schemaContext Schema context
+     * @param writer Output writer
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer) {
+        return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0);
+    }
+
+    /**
+     * Create a new stream writer, which writes to the specified output stream.
+     *
+     * @param schemaContext Schema context
+     * @param writer Output writer
+     * @param indentSize indentation size
+     * @return A stream writer instance
+     */
+    public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer, final int indentSize) {
+        return new JSONNormalizedNodeStreamWriter(schemaContext, writer, indentSize);
+    }
+
+    @Override
+    public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+        separateElementFromPreviousElement();
+        writeJsonIdentifier(name);
+        currentNamespace = stack.peek().getNamespace();
+        writeValue(value.toString());
+        separateNextSiblingsWithComma();
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        separateElementFromPreviousElement();
+        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+        writeJsonIdentifier(name);
+        writeStartList();
+        indentRight();
+    }
+
+    @Override
+    public void leafSetEntryNode(final Object value) throws IOException {
+        separateElementFromPreviousElement();
+        writeValue(value.toString());
+        separateNextSiblingsWithComma();
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        separateElementFromPreviousElement();
+        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+        writeJsonIdentifier(name);
+        writeStartObject();
+        indentRight();
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        separateElementFromPreviousElement();
+        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+        writeJsonIdentifier(name);
+        writeStartList();
+        indentRight();
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+        separateElementFromPreviousElement();
+        writeStartObject();
+        indentRight();
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        separateElementFromPreviousElement();
+        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+        writeJsonIdentifier(name);
+        writeStartList();
+        indentRight();
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace()));
+        separateElementFromPreviousElement();
+        writeStartObject();
+        indentRight();
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+        stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+        separateElementFromPreviousElement();
+        writeJsonIdentifier(name);
+        writeStartList();
+        indentRight();
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+        handleInvisibleNode(name.getNodeType().getNamespace());
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+        handleInvisibleNode(currentNamespace);
+    }
+
+    @Override
+    public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+        separateElementFromPreviousElement();
+        writeJsonIdentifier(name);
+        currentNamespace = stack.peek().getNamespace();
+        writeValue(value.toString());
+        separateNextSiblingsWithComma();
+    }
+
+    @Override
+    public void endNode() throws IOException {
+        switch (stack.peek().getType()) {
+        case LIST:
+            indentLeft();
+            newLine();
+            writer.append(']');
+            break;
+        case OBJECT:
+            indentLeft();
+            newLine();
+            writer.append('}');
+            break;
+        default:
+            break;
+        }
+        stack.pop();
+        currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace();
+        separateNextSiblingsWithComma();
+    }
+
+    private void separateElementFromPreviousElement() throws IOException {
+        if (!stack.isEmpty() && stack.peek().hasAtLeastOneChild()) {
+            writer.append(',');
+        }
+        newLine();
+    }
+
+    private void newLine() throws IOException {
+        if (indent != null) {
+            writer.append('\n');
+
+            for (int i = 0; i < currentDepth; i++) {
+                writer.append(indent);
+            }
+        }
+    }
+
+    private void separateNextSiblingsWithComma() {
+        if (!stack.isEmpty()) {
+            stack.peek().setHasAtLeastOneChild(true);
+        }
+    }
+
+    /**
+     * Invisible nodes have to be also pushed to stack because of pairing of start*() and endNode() methods. Information
+     * about child existing (due to printing comma) has to be transfered to invisible node.
+     */
+    private void handleInvisibleNode(final URI uri) {
+        TypeInfo typeInfo = new TypeInfo(NodeType.OTHER, uri);
+        typeInfo.setHasAtLeastOneChild(stack.peek().hasAtLeastOneChild());
+        stack.push(typeInfo);
+    }
+
+    private void writeStartObject() throws IOException {
+        writer.append('{');
+    }
+
+    private void writeStartList() throws IOException {
+        writer.append('[');
+    }
+
+    private void writeModulName(final URI namespace) throws IOException {
+        if (this.currentNamespace == null || namespace != this.currentNamespace) {
+            Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+            writer.append(module.getName());
+            writer.append(':');
+            currentNamespace = namespace;
+        }
+    }
+
+    private void writeValue(final String value) throws IOException {
+        writer.append('"');
+        writer.append(value);
+        writer.append('"');
+    }
+
+    private void writeJsonIdentifier(final NodeIdentifier name) throws IOException {
+        writer.append('"');
+        writeModulName(name.getNodeType().getNamespace());
+        writer.append(name.getNodeType().getLocalName());
+        writer.append("\":");
+    }
+
+    private void indentRight() {
+        currentDepth++;
+    }
+
+    private void indentLeft() {
+        currentDepth--;
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+
+}
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
new file mode 100644 (file)
index 0000000..18232ff
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2014 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;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterators;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.MalformedJsonException;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.Flushable;
+import java.io.IOException;
+import java.net.URI;
+import java.security.InvalidParameterException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil.PrefixMapingFromJson;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.SchemaContextUtils;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+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.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+/**
+ * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the
+ * default GSON JsonParser.
+ */
+@Beta
+public final class JsonParserStream implements Closeable, Flushable {
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
+    private final Deque<URI> namespaces = new ArrayDeque<>();
+    private final NormalizedNodeStreamWriter writer;
+    private final SchemaContextUtils utils;
+    private final RestCodecFactory codecs;
+    private final SchemaContext schema;
+
+    private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+        this.schema = Preconditions.checkNotNull(schemaContext);
+        this.utils = SchemaContextUtils.create(schemaContext);
+        this.writer = Preconditions.checkNotNull(writer);
+        this.codecs = RestCodecFactory.create(utils);
+    }
+
+    public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+        return new JsonParserStream(writer, schemaContext);
+    }
+
+    public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException {
+        // code copied from gson's JsonParser and Stream classes
+
+        boolean lenient = reader.isLenient();
+        reader.setLenient(true);
+        boolean isEmpty = true;
+        try {
+            reader.peek();
+            isEmpty = false;
+            CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(schema);
+            read(reader, compositeNodeDataWithSchema);
+            compositeNodeDataWithSchema.writeToStream(writer);
+
+            return this;
+            // return read(reader);
+        } catch (EOFException e) {
+            if (isEmpty) {
+                return this;
+                // return JsonNull.INSTANCE;
+            }
+            // The stream ended prematurely so it is likely a syntax error.
+            throw new JsonSyntaxException(e);
+        } catch (MalformedJsonException e) {
+            throw new JsonSyntaxException(e);
+        } catch (IOException e) {
+            throw new JsonIOException(e);
+        } catch (NumberFormatException e) {
+            throw new JsonSyntaxException(e);
+        } catch (StackOverflowError | OutOfMemoryError e) {
+            throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+        } finally {
+            reader.setLenient(lenient);
+        }
+    }
+
+    public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException {
+
+        final JsonToken peek = in.peek();
+        Optional<String> value = Optional.absent();
+        switch (peek) {
+        case STRING:
+        case NUMBER:
+            value = Optional.of(in.nextString());
+            break;
+        case BOOLEAN:
+            value = Optional.of(Boolean.toString(in.nextBoolean()));
+            break;
+        case NULL:
+            in.nextNull();
+            value = Optional.of((String) null);
+            break;
+        default:
+            break;
+        }
+        if (value.isPresent()) {
+            final Object translatedValue = translateValueByType(value.get(), parent.getSchema());
+            ((SimpleNodeDataWithSchema) parent).setValue(translatedValue);
+        }
+
+        switch (peek) {
+        case BEGIN_ARRAY:
+            in.beginArray();
+            while (in.hasNext()) {
+                AbstractNodeDataWithSchema newChild = null;
+                if (parent instanceof ListNodeDataWithSchema) {
+                    newChild = new ListEntryNodeDataWithSchema(parent.getSchema());
+                    ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+                } else if (parent instanceof LeafListNodeDataWithSchema) {
+                    newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema());
+                    ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+                }
+                read(in, newChild);
+            }
+            in.endArray();
+            return;
+        case BEGIN_OBJECT:
+            Set<String> namesakes = new HashSet<>();
+            in.beginObject();
+            while (in.hasNext()) {
+                final String jsonElementName = in.nextName();
+                final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName);
+                final String localName = namespaceAndName.getName();
+                addNamespace(namespaceAndName.getUri());
+                if (namesakes.contains(jsonElementName)) {
+                    throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input.");
+                }
+                namesakes.add(jsonElementName);
+                final Deque<DataSchemaNode> childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parent.getSchema(),
+                        localName, getCurrentNamespace());
+                if (childDataSchemaNodes.isEmpty()) {
+                    throw new IllegalStateException("Schema for node with name " + localName + " and namespace "
+                            + getCurrentNamespace() + " doesn't exist.");
+                }
+
+                AbstractNodeDataWithSchema newChild;
+                newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes);
+//                FIXME:anyxml data shouldn't be skipped but should be loaded somehow. will be specified after 17AUG2014
+                if (newChild instanceof AnyXmlNodeDataWithSchema) {
+                    in.skipValue();
+                } else {
+                    read(in, newChild);
+                }
+                removeNamespace();
+            }
+            in.endObject();
+            return;
+        case END_DOCUMENT:
+        case NAME:
+        case END_OBJECT:
+        case END_ARRAY:
+        }
+    }
+
+    private Object translateValueByType(final String value, final DataSchemaNode node) {
+        final TypeDefinition<? extends Object> typeDefinition = typeDefinition(node);
+        if (typeDefinition == null) {
+            return value;
+        }
+
+        final Object inputValue;
+        if (typeDefinition instanceof IdentityrefTypeDefinition) {
+            inputValue = valueAsIdentityRef(value);
+        } else if (typeDefinition instanceof InstanceIdentifierTypeDefinition) {
+            inputValue = valueAsInstanceIdentifier(value);
+        } else {
+            inputValue = value;
+        }
+
+        // FIXME: extract this as a cacheable context?
+        final Codec<Object, Object> codec = codecs.codecFor(typeDefinition);
+        if (codec == null) {
+            return null;
+        }
+        return codec.deserialize(inputValue);
+    }
+
+    private static TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
+        TypeDefinition<?> baseType = null;
+        if (node instanceof LeafListSchemaNode) {
+            baseType = ((LeafListSchemaNode) node).getType();
+        } else if (node instanceof LeafSchemaNode) {
+            baseType = ((LeafSchemaNode) node).getType();
+        } else if (node instanceof AnyXmlSchemaNode) {
+            return null;
+        } else {
+            throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
+        }
+
+        if (baseType != null) {
+            while (baseType.getBaseType() != null) {
+                baseType = baseType.getBaseType();
+            }
+        }
+        return baseType;
+    }
+
+    private static Object valueAsInstanceIdentifier(final String value) {
+        // it could be instance-identifier Built-In Type
+        if (!value.isEmpty() && value.charAt(0) == '/') {
+            IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson());
+            if (resolvedValue != null) {
+                return resolvedValue;
+            }
+        }
+        throw new InvalidParameterException("Value for instance-identifier doesn't have correct format");
+    }
+
+    private static IdentityValuesDTO valueAsIdentityRef(final String value) {
+        // it could be identityref Built-In Type
+        URI namespace = getNamespaceFor(value);
+        if (namespace != null) {
+            return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value);
+        }
+        throw new InvalidParameterException("Value for identityref has to be in format moduleName:localName.");
+    }
+
+    private static URI getNamespaceFor(final String jsonElementName) {
+        final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+        // The string needs to me in form "moduleName:localName"
+        if (it.hasNext()) {
+            final String maybeURI = it.next();
+            if (Iterators.size(it) == 1) {
+                return URI.create(maybeURI);
+            }
+        }
+
+        return null;
+    }
+
+    private static String getLocalNameFor(final String jsonElementName) {
+        final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+        // The string needs to me in form "moduleName:localName"
+        final String ret = Iterators.get(it, 1, null);
+        return ret != null && !it.hasNext() ? ret : jsonElementName;
+    }
+
+    private void removeNamespace() {
+        namespaces.pop();
+    }
+
+    private void addNamespace(final Optional<URI> namespace) {
+        if (!namespace.isPresent()) {
+            if (namespaces.isEmpty()) {
+                throw new IllegalStateException("Namespace has to be specified at top level.");
+            } else {
+                namespaces.push(namespaces.peek());
+            }
+        } else {
+            namespaces.push(namespace.get());
+        }
+    }
+
+    private NamespaceAndName resolveNamespace(final String childName) {
+        int lastIndexOfColon = childName.lastIndexOf(":");
+        String moduleNamePart = null;
+        String nodeNamePart = null;
+        URI namespace = null;
+        if (lastIndexOfColon != -1) {
+            moduleNamePart = childName.substring(0, lastIndexOfColon);
+            nodeNamePart = childName.substring(lastIndexOfColon + 1);
+            namespace = utils.findNamespaceByModuleName(moduleNamePart);
+        } else {
+            nodeNamePart = childName;
+        }
+
+        Optional<URI> namespaceOpt = namespace == null ? Optional.<URI> absent() : Optional.of(namespace);
+        return new NamespaceAndName(nodeNamePart, namespaceOpt);
+    }
+
+    private URI getCurrentNamespace() {
+        return namespaces.peek();
+    }
+
+    /**
+     * Returns stack of schema nodes via which it was necessary to prass 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 dirrect child then stack
+     *         contains only one node. If it is found under choice and case then stack should conains 2*n+1 element
+     *         (where n is number of choices through it was passed)
+     */
+    private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+            final String childName, final URI namespace) {
+        final Deque<DataSchemaNode> result = new ArrayDeque<>();
+        List<ChoiceNode> childChoices = new ArrayList<>();
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                if (childNode instanceof ChoiceNode) {
+                    childChoices.add((ChoiceNode) childNode);
+                } else {
+                    final QName childQName = childNode.getQName();
+                    if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) {
+                        result.push(childNode);
+                        return result;
+                    }
+                }
+            }
+        }
+        // try to find data schema node in choice (looking for first match)
+        for (ChoiceNode choiceNode : childChoices) {
+            for (ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+                Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
+                        namespace);
+                if (!resultFromRecursion.isEmpty()) {
+                    resultFromRecursion.push(concreteCase);
+                    resultFromRecursion.push(choiceNode);
+                    return resultFromRecursion;
+                }
+            }
+        }
+        return result;
+    }
+
+    private static class NamespaceAndName {
+        private final Optional<URI> uri;
+        private final String name;
+
+        public NamespaceAndName(final String name, final Optional<URI> uri) {
+            this.name = name;
+            this.uri = uri;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Optional<URI> getUri() {
+            return uri;
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..e53367c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema {
+    public LeafListEntryNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        nnStreamWriter.leafSetEntryNode(getValue());
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..8b23acd
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 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;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+    public LeafListNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        nnStreamWriter.startLeafSet(provideNodeIdentifier(), UNKNOWN_SIZE);
+        super.writeToStream(nnStreamWriter);
+        nnStreamWriter.endNode();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..c63422a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 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;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+    public LeafNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        nnStreamWriter.leafNode(provideNodeIdentifier(), getValue());
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..b08add8
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 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;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+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 {
+
+    private final Map<QName, SimpleNodeDataWithSchema> qNameToKeys = new HashMap<>();
+
+    public ListEntryNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    public void addChild(final AbstractNodeDataWithSchema newChild) {
+        DataSchemaNode childSchema = newChild.getSchema();
+        if (childSchema instanceof LeafSchemaNode && isPartOfKey((LeafSchemaNode) childSchema)) {
+            qNameToKeys.put(childSchema.getQName(), (SimpleNodeDataWithSchema)newChild);
+        }
+        super.addChild(newChild);
+    }
+
+    private boolean isPartOfKey(final LeafSchemaNode potentialKey) {
+        List<QName> keys = ((ListSchemaNode) getSchema()).getKeyDefinition();
+        for (QName qName : keys) {
+            if (qName.equals(potentialKey.getQName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        int keyCount = ((ListSchemaNode) getSchema()).getKeyDefinition().size();
+        if (keyCount == 0) {
+            nnStreamWriter.startUnkeyedListItem(provideNodeIdentifier(), UNKNOWN_SIZE);
+            super.writeToStream(nnStreamWriter);
+            nnStreamWriter.endNode();
+        } else if (keyCount == qNameToKeys.size()) {
+            nnStreamWriter.startMapEntryNode(provideNodeIdentifierWithPredicates(), UNKNOWN_SIZE);
+            super.writeToStream(nnStreamWriter);
+            nnStreamWriter.endNode();
+        } else {
+            throw new IllegalStateException("Some of keys of " + getSchema().getQName() + " are missing in input.");
+        }
+    }
+
+    private NodeIdentifierWithPredicates provideNodeIdentifierWithPredicates() {
+        Map<QName, Object> qNameToPredicateValues = new HashMap<>();
+
+        for (SimpleNodeDataWithSchema simpleNodeDataWithSchema : qNameToKeys.values()) {
+            qNameToPredicateValues.put(simpleNodeDataWithSchema.getSchema().getQName(), simpleNodeDataWithSchema.getValue());
+        }
+
+        return new NodeIdentifierWithPredicates(getSchema().getQName(), qNameToPredicateValues);
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..612c386
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 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;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+    public ListNodeDataWithSchema(final DataSchemaNode schema) {
+        super(schema);
+    }
+
+    @Override
+    protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+        if (!((ListSchemaNode) getSchema()).getKeyDefinition().isEmpty()) {
+            nnStreamWriter.startMapNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+        } else {
+            nnStreamWriter.startUnkeyedList(provideNodeIdentifier(), UNKNOWN_SIZE);
+        }
+        super.writeToStream(nnStreamWriter);
+        nnStreamWriter.endNode();
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleNodeDataWithSchema.java
new file mode 100644 (file)
index 0000000..26d774c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 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;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+    private Object value;
+
+    public SimpleNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+        super(dataSchemaNode);
+    }
+
+    void setValue(final Object value) {
+        this.value = value;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java
new file mode 100644 (file)
index 0000000..407d866
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractCodecImpl {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecImpl.class);
+    private final SchemaContextUtils schema;
+
+    protected AbstractCodecImpl(final SchemaContextUtils schema) {
+        this.schema = Preconditions.checkNotNull(schema);
+    }
+
+    protected final SchemaContextUtils getSchema() {
+        return schema;
+    }
+
+    protected final Module getModuleByNamespace(final String namespace) {
+        URI validNamespace = resolveValidNamespace(namespace);
+
+        Module module = schema.findModuleByNamespace(validNamespace);
+        if (module == null) {
+            LOG.info("Module for namespace " + validNamespace + " wasn't found.");
+            return null;
+        }
+        return module;
+    }
+
+    protected final URI resolveValidNamespace(final String namespace) {
+        URI validNamespace = schema.findNamespaceByModuleName(namespace);
+        if (validNamespace == null) {
+            validNamespace = URI.create(namespace);
+        }
+
+        return validNamespace;
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java
new file mode 100644 (file)
index 0000000..30ba2a0
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class IdentityValuesDTO {
+
+    private final List<IdentityValue> elementData = new ArrayList<>();
+    private final String originValue;
+
+    public IdentityValuesDTO(final String namespace, final String value, final String prefix, final String originValue) {
+        elementData.add(new IdentityValue(namespace, value, prefix));
+        this.originValue = originValue;
+    }
+
+    public IdentityValuesDTO(final String originValue) {
+        this.originValue = originValue;
+    }
+
+    public IdentityValuesDTO() {
+        originValue = null;
+    }
+
+    public void add(final String namespace, final String value, final String prefix) {
+        elementData.add(new IdentityValue(namespace, value, prefix));
+    }
+
+    public void add(final IdentityValue identityValue) {
+        elementData.add(identityValue);
+    }
+
+    public List<IdentityValue> getValuesWithNamespaces() {
+        return Collections.unmodifiableList(elementData);
+    }
+
+    @Override
+    public String toString() {
+        return elementData.toString();
+    }
+
+    public String getOriginValue() {
+        return originValue;
+    }
+
+    public static final class IdentityValue {
+
+        private final String namespace;
+        private final String value;
+        private final String prefix;
+        private List<Predicate> predicates;
+
+        public IdentityValue(final String namespace, final String value, final String prefix) {
+            this.namespace = namespace;
+            this.value = value;
+            this.prefix = prefix;
+        }
+
+        public String getNamespace() {
+            return namespace;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public String getPrefix() {
+            return prefix;
+        }
+
+        public List<Predicate> getPredicates() {
+            if (predicates == null) {
+                return Collections.emptyList();
+            }
+            return Collections.unmodifiableList(predicates);
+        }
+
+        public void setPredicates(final List<Predicate> predicates) {
+            this.predicates = predicates;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            if (namespace != null) {
+                sb.append(namespace);
+            }
+            if (prefix != null) {
+                sb.append("(").append(prefix).append(")");
+            }
+            if (value != null) {
+                sb.append(" - ").append(value);
+            }
+            if (predicates != null && !predicates.isEmpty()) {
+                for (Predicate predicate : predicates) {
+                    sb.append("[");
+                    predicate.toString();
+                    sb.append("]");
+                }
+            }
+            return sb.toString();
+        }
+
+    }
+
+    public static final class Predicate {
+
+        private final IdentityValue name;
+        private final String value;
+
+        public Predicate(final IdentityValue name, final String value) {
+            super();
+            this.name = name;
+            this.value = value;
+        }
+
+        public IdentityValue getName() {
+            return name;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            if (name != null) {
+                sb.append(name.toString());
+            }
+            if (value != null) {
+                sb.append("=").append(value);
+            }
+            return sb.toString();
+        }
+
+        public boolean isLeafList() {
+            return name == null ? true : false;
+        }
+
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java
new file mode 100644 (file)
index 0000000..a8c8b8d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class IdentityrefCodecImpl extends AbstractCodecImpl implements IdentityrefCodec<IdentityValuesDTO> {
+    private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class);
+
+    IdentityrefCodecImpl(final SchemaContextUtils schema) {
+        super(schema);
+    }
+
+    @Override
+    public IdentityValuesDTO serialize(final QName data) {
+        return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(), null);
+    }
+
+    @Override
+    public QName deserialize(final IdentityValuesDTO data) {
+        IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+        Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+        if (module == null) {
+            LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace());
+            LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace));
+            return null;
+        }
+
+        return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue());
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java
new file mode 100644 (file)
index 0000000..5904859
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InstanceIdentifierCodecImpl extends AbstractCodecImpl implements InstanceIdentifierCodec<IdentityValuesDTO> {
+    private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
+
+    InstanceIdentifierCodecImpl(final SchemaContextUtils schema) {
+        super(schema);
+    }
+
+    @Override
+    public IdentityValuesDTO serialize(final YangInstanceIdentifier data) {
+        IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO();
+        for (PathArgument pathArgument : data.getPathArguments()) {
+            IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType());
+            if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) {
+                List<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
+                        .getKeyValues());
+                identityValue.setPredicates(predicates);
+            } else if (pathArgument instanceof NodeWithValue && identityValue != null) {
+                List<Predicate> predicates = new ArrayList<>();
+                String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
+                predicates.add(new Predicate(null, value));
+                identityValue.setPredicates(predicates);
+            }
+            identityValuesDTO.add(identityValue);
+        }
+        return identityValuesDTO;
+    }
+
+    @Override
+    public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) {
+        List<PathArgument> result = new ArrayList<PathArgument>();
+        IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+        Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+        if (module == null) {
+            LOG.info("Module by namespace '{}' of first node in instance-identiefier was not found.",
+                    valueWithNamespace.getNamespace());
+            LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                    String.valueOf(valueWithNamespace.getValue()));
+            return null;
+        }
+
+        DataNodeContainer parentContainer = module;
+        List<IdentityValue> identities = data.getValuesWithNamespaces();
+        for (int i = 0; i < identities.size(); i++) {
+            IdentityValue identityValue = identities.get(i);
+            URI validNamespace = resolveValidNamespace(identityValue.getNamespace());
+            DataSchemaNode node = getSchema().findInstanceDataChildByNameAndNamespace(
+                    parentContainer, identityValue.getValue(), validNamespace);
+            if (node == null) {
+                LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes());
+                LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                        String.valueOf(identityValue.getValue()));
+                return null;
+            }
+            QName qName = node.getQName();
+            PathArgument pathArgument = null;
+            if (identityValue.getPredicates().isEmpty()) {
+                pathArgument = new NodeIdentifier(qName);
+            } else {
+                if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry
+                    Predicate leafListPredicate = identityValue.getPredicates().get(0);
+                    if (!leafListPredicate.isLeafList()) {
+                        LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\"");
+                        LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                                String.valueOf(identityValue.getValue()));
+                        return null;
+                    }
+                    pathArgument = new NodeWithValue(qName, leafListPredicate.getValue());
+                } else if (node instanceof ListSchemaNode) { // predicates are keys of list
+                    DataNodeContainer listNode = (DataNodeContainer) node;
+                    Map<QName, Object> predicatesMap = new HashMap<>();
+                    for (Predicate predicate : identityValue.getPredicates()) {
+                        validNamespace = resolveValidNamespace(predicate.getName().getNamespace());
+                        DataSchemaNode listKey = getSchema()
+                                .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(),
+                                        validNamespace);
+                        predicatesMap.put(listKey.getQName(), predicate.getValue());
+                    }
+                    pathArgument = new NodeIdentifierWithPredicates(qName, predicatesMap);
+                } else {
+                    LOG.info("Node {} is not List or Leaf-list.", node);
+                    LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                            String.valueOf(identityValue.getValue()));
+                    return null;
+                }
+            }
+            result.add(pathArgument);
+            if (i < identities.size() - 1) { // last element in instance-identifier can be other than
+                // DataNodeContainer
+                if (node instanceof DataNodeContainer) {
+                    parentContainer = (DataNodeContainer) node;
+                } else {
+                    LOG.info("Node {} isn't instance of DataNodeContainer", node);
+                    LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                            String.valueOf(identityValue.getValue()));
+                    return null;
+                }
+            }
+        }
+
+        return result.isEmpty() ? null : YangInstanceIdentifier.create(result);
+    }
+
+    private static List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
+        List<Predicate> result = new ArrayList<>();
+        for (QName qName : keyValues.keySet()) {
+            Object value = keyValues.get(qName);
+            result.add(new Predicate(qNameToIdentityValue(qName), String.valueOf(value)));
+        }
+        return result;
+    }
+
+    private static IdentityValue qNameToIdentityValue(final QName qName) {
+        if (qName != null) {
+            // FIXME: the prefix here is completely arbitrary
+            return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix());
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java
new file mode 100644 (file)
index 0000000..c6d8baf
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
+
+class LeafrefCodecImpl implements LeafrefCodec<String> {
+
+    @Override
+    public String serialize(final Object data) {
+        return String.valueOf(data);
+    }
+
+    @Override
+    public Object deserialize(final String data) {
+        return data;
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java
new file mode 100644 (file)
index 0000000..abe7cd2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+final class ObjectCodec extends AbstractCodecImpl implements Codec<Object, Object> {
+    public static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl();
+    private static final Logger LOG = LoggerFactory.getLogger(RestCodecFactory.class);
+    private final Codec instanceIdentifier;
+    private final Codec identityrefCodec;
+    private final TypeDefinition<?> type;
+
+    ObjectCodec(final SchemaContextUtils schema, final TypeDefinition<?> typeDefinition) {
+        super(schema);
+        type = RestUtil.resolveBaseTypeFrom(typeDefinition);
+        if (type instanceof IdentityrefTypeDefinition) {
+            identityrefCodec = new IdentityrefCodecImpl(schema);
+        } else {
+            identityrefCodec = null;
+        }
+        if (type instanceof InstanceIdentifierTypeDefinition) {
+            instanceIdentifier = new InstanceIdentifierCodecImpl(schema);
+        } else {
+            instanceIdentifier = null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(final Object input) {
+        try {
+            if (type instanceof IdentityrefTypeDefinition) {
+                if (input instanceof IdentityValuesDTO) {
+                    return identityrefCodec.deserialize(input);
+                }
+                LOG.debug("Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of  - {}",
+                        input == null ? "null" : input.getClass(), String.valueOf(input));
+                return null;
+            } else if (type instanceof LeafrefTypeDefinition) {
+                if (input instanceof IdentityValuesDTO) {
+                    return LEAFREF_DEFAULT_CODEC.deserialize(((IdentityValuesDTO) input).getOriginValue());
+                }
+                return LEAFREF_DEFAULT_CODEC.deserialize(input);
+            } else if (type instanceof InstanceIdentifierTypeDefinition) {
+                if (input instanceof IdentityValuesDTO) {
+                    return instanceIdentifier.deserialize(input);
+                }
+                LOG.info(
+                        "Value is not instance of InstanceIdentifierTypeDefinition but is {}. Therefore NULL is used as translation of  - {}",
+                        input == null ? "null" : input.getClass(), String.valueOf(input));
+                return null;
+            } else {
+                TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+                        .from(type);
+                if (typeAwarecodec != null) {
+                    if (input instanceof IdentityValuesDTO) {
+                        return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue());
+                    }
+                    return typeAwarecodec.deserialize(String.valueOf(input));
+                } else {
+                    LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+                            + "\" is not implemented yet.");
+                    return null;
+                }
+            }
+        } catch (ClassCastException e) {
+            // TODO remove this catch when everyone use codecs
+            LOG.error("ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+                    e);
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object serialize(final Object input) {
+        try {
+            if (type instanceof IdentityrefTypeDefinition) {
+                return identityrefCodec.serialize(input);
+            } else if (type instanceof LeafrefTypeDefinition) {
+                return LEAFREF_DEFAULT_CODEC.serialize(input);
+            } else if (type instanceof InstanceIdentifierTypeDefinition) {
+                return instanceIdentifier.serialize(input);
+            } else {
+                TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+                        .from(type);
+                if (typeAwarecodec != null) {
+                    return typeAwarecodec.serialize(input);
+                } else {
+                    LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+                            + "\" is not implemented yet.");
+                    return null;
+                }
+            }
+        } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs
+            LOG.error(
+                    "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+                    e);
+            return input;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java
new file mode 100644 (file)
index 0000000..94bba92
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestCodecFactory {
+    private final SchemaContextUtils utils;
+
+    private RestCodecFactory(final SchemaContextUtils utils) {
+        this.utils = Preconditions.checkNotNull(utils);
+    }
+
+    public static RestCodecFactory create(final SchemaContextUtils utils) {
+        return new RestCodecFactory(utils);
+    }
+
+    public final Codec<Object, Object> codecFor(final TypeDefinition<?> typeDefinition) {
+        // FIXME: implement loadingcache
+        return new ObjectCodec(utils, typeDefinition);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java
new file mode 100644 (file)
index 0000000..c3d002c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.stream.events.StartElement;
+
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestUtil {
+
+    // FIXME: BUG-1275: this is code duplicates data.impl.codec
+
+    public static final String SQUOTE = "'";
+    public static final String DQUOTE = "\"";
+    private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+
+    public final static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
+        TypeDefinition<?> superType = type;
+        while (superType.getBaseType() != null) {
+            superType = superType.getBaseType();
+        }
+        return superType;
+    }
+
+    public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) {
+        String valueTrimmed = value.trim();
+        if (!valueTrimmed.startsWith("/")) {
+            return null;
+        }
+        String[] xPathParts = valueTrimmed.split("/");
+        if (xPathParts.length < 2) { // must be at least "/pr:node"
+            return null;
+        }
+        IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value);
+        for (int i = 1; i < xPathParts.length; i++) {
+            String xPathPartTrimmed = xPathParts[i].trim();
+
+            String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed);
+            IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap);
+            if (identityValue == null) {
+                return null;
+            }
+
+            List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
+            if (predicates == null) {
+                return null;
+            }
+            identityValue.setPredicates(predicates);
+
+            identityValuesDTO.add(identityValue);
+        }
+        return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
+    }
+
+    private static String getIdAndPrefixAsStr(final String pathPart) {
+        int predicateStartIndex = pathPart.indexOf("[");
+        return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+    }
+
+    private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) {
+        String xPathPartTrimmed = xPathPart.trim();
+        if (xPathPartTrimmed.isEmpty()) {
+            return null;
+        }
+        String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
+        // it is not "prefix:value"
+        if (prefixAndIdentifier.length != 2) {
+            return null;
+        }
+        String prefix = prefixAndIdentifier[0].trim();
+        String identifier = prefixAndIdentifier[1].trim();
+        if (prefix.isEmpty() || identifier.isEmpty()) {
+            return null;
+        }
+        String namespace = prefixMap.getNamespace(prefix);
+        return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix);
+    }
+
+    private static List<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
+        List<Predicate> result = new ArrayList<>();
+        List<String> predicates = new ArrayList<>();
+        Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
+        while (matcher.find()) {
+            predicates.add(matcher.group(1).trim());
+        }
+        for (String predicate : predicates) {
+            int indexOfEqualityMark = predicate.indexOf("=");
+            if (indexOfEqualityMark != -1) {
+                String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1));
+                if (predicate.startsWith(".")) { // it is leaf-list
+                    if (predicateValue == null) {
+                        return null;
+                    }
+                    result.add(new Predicate(null, predicateValue));
+                } else {
+                    IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap);
+                    if (identityValue == null || predicateValue == null) {
+                        return null;
+                    }
+                    result.add(new Predicate(identityValue, predicateValue));
+                }
+            }
+        }
+        return result;
+    }
+
+    private static String toPredicateValue(final String predicatedValue) {
+        String predicatedValueTrimmed = predicatedValue.trim();
+        if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
+                && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
+            return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
+        }
+        return null;
+    }
+
+    public interface PrefixesMaping {
+        public String getNamespace(String prefix);
+    }
+
+    public static class PrefixMapingFromXml implements PrefixesMaping {
+        StartElement startElement = null;
+
+        public PrefixMapingFromXml(final StartElement startElement) {
+            this.startElement = startElement;
+        }
+
+        @Override
+        public String getNamespace(final String prefix) {
+            return startElement.getNamespaceContext().getNamespaceURI(prefix);
+        }
+    }
+
+    public static class PrefixMapingFromJson implements PrefixesMaping {
+
+        @Override
+        public String getNamespace(final String prefix) {
+            return prefix;
+        }
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java
new file mode 100644 (file)
index 0000000..5e8f6f1
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014 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.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class SchemaContextUtils {
+    private final SchemaContext schemaContext;
+
+    private SchemaContextUtils(final SchemaContext schemaContext) {
+        this.schemaContext = Preconditions.checkNotNull(schemaContext);
+    }
+
+    public static SchemaContextUtils create(final SchemaContext schemaContext) {
+        return new SchemaContextUtils(schemaContext);
+    }
+
+    public URI findNamespaceByModuleName(final String moduleName) {
+        final Module module = this.findModuleByName(moduleName);
+        return module == null ? null : module.getNamespace();
+    }
+
+
+    public Module findModuleByName(final String moduleName) {
+        checkPreconditions();
+        Preconditions.checkArgument(moduleName != null && !moduleName.isEmpty());
+        return schemaContext.findModuleByName(moduleName, null);
+    }
+
+    public Module findModuleByNamespace(final URI namespace) {
+        this.checkPreconditions();
+        Preconditions.checkArgument(namespace != null);
+        return schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+    }
+
+    private void checkPreconditions() {
+        if (schemaContext == null) {
+            throw new IllegalStateException("Schema context isn't set.");
+        }
+    }
+
+    public DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name,
+            final URI namespace) {
+        Preconditions.<URI> checkNotNull(namespace);
+
+        final List<DataSchemaNode> potentialSchemaNodes = findInstanceDataChildrenByName(container, name);
+
+        Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+            @Override
+            public boolean apply(final DataSchemaNode node) {
+                return Objects.equal(node.getQName().getNamespace(), namespace);
+            }
+        };
+
+        Iterable<DataSchemaNode> result = Iterables.filter(potentialSchemaNodes, filter);
+        return Iterables.getFirst(result, null);
+    }
+
+    public List<DataSchemaNode> findInstanceDataChildrenByName(final DataNodeContainer container, final String name) {
+        Preconditions.<DataNodeContainer> checkNotNull(container);
+        Preconditions.<String> checkNotNull(name);
+
+        List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<DataSchemaNode>();
+        collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name);
+        return instantiatedDataNodeContainers;
+    }
+
+    private void collectInstanceDataNodeContainers(final List<DataSchemaNode> potentialSchemaNodes,
+            final DataNodeContainer container, final String name) {
+
+        Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+            @Override
+            public boolean apply(final DataSchemaNode node) {
+                return Objects.equal(node.getQName().getLocalName(), name);
+            }
+        };
+
+        Iterable<DataSchemaNode> nodes = Iterables.filter(container.getChildNodes(), filter);
+
+        // Can't combine this loop with the filter above because the filter is
+        // lazily-applied by Iterables.filter.
+        for (final DataSchemaNode potentialNode : nodes) {
+            if (isInstantiatedDataSchema(potentialNode)) {
+                potentialSchemaNodes.add(potentialNode);
+            }
+        }
+
+        Iterable<ChoiceNode> choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceNode.class);
+        Iterable<Set<ChoiceCaseNode>> map = Iterables.transform(choiceNodes, CHOICE_FUNCTION);
+
+        final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat(map);
+        for (final ChoiceCaseNode caze : allCases) {
+            collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name);
+        }
+    }
+
+    public boolean isInstantiatedDataSchema(final DataSchemaNode node) {
+        return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode
+                || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode
+                || node instanceof AnyXmlSchemaNode;
+    }
+
+    private final Function<ChoiceNode, Set<ChoiceCaseNode>> CHOICE_FUNCTION = new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
+        @Override
+        public Set<ChoiceCaseNode> apply(final ChoiceNode node) {
+            return node.getCases();
+        }
+    };
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..1010989
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2014 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;
+
+import com.google.gson.stream.JsonReader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.LoggingNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StreamToNormalizedNodeTest {
+    private static final Logger LOG = LoggerFactory.getLogger(StreamToNormalizedNodeTest.class);
+    private static SchemaContext schemaContext;
+    private static String streamAsString;
+
+    @BeforeClass
+    public static void initialization() throws IOException {
+        schemaContext = loadModules("/complexjson/yang");
+        streamAsString = loadTextFile(StreamToNormalizedNodeTest.class.getResource("/complexjson/complex-json.json")
+                .getPath());
+    }
+
+    /**
+     * Demonstrates how to log events produced by a {@link JsonReader}.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void ownStreamWriterImplementationDemonstration() throws IOException {
+        // GSON's JsonReader reading from the loaded string (our event source)
+        final JsonReader reader = new JsonReader(new StringReader(streamAsString));
+
+        // StreamWriter which outputs SLF4J events
+        final LoggingNormalizedNodeStreamWriter logWriter = new LoggingNormalizedNodeStreamWriter();
+
+        // JSON -> StreamWriter parser
+        try (final JsonParserStream jsonHandler = JsonParserStream.create(logWriter, schemaContext)) {
+            // Process multiple readers, flush()/close() as needed
+            jsonHandler.parse(reader);
+        }
+    }
+
+    /**
+     * Demonstrates how to create an immutable NormalizedNode tree from a {@link JsonReader} and
+     * then writes the data back into string representation.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException {
+        /*
+         * This is the parsing part
+         */
+        // This is where we will output the nodes
+        final NormalizedNodeContainerBuilder<NodeIdentifier, ?, ?, ? extends NormalizedNode<?, ?>> parent =
+                Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create("dummy", "2014-12-31", "dummy")));
+
+        // StreamWriter which attaches NormalizedNode under parent
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(parent);
+
+        // JSON -> StreamWriter parser
+        try (JsonParserStream handler = JsonParserStream.create(streamWriter, schemaContext)) {
+            handler.parse(new JsonReader(new StringReader(streamAsString)));
+        }
+
+        // Finally build the node
+        final NormalizedNode<?, ?> parsedData = parent.build();
+        LOG.debug("Parsed NormalizedNodes: {}", parsedData);
+
+        /*
+         * This is the serialization part.
+         */
+        // We want to write the first child out
+        final DataContainerChild<? extends PathArgument, ?> firstChild = ((ContainerNode) parsedData).getValue().iterator().next();
+        LOG.debug("Serializing first child: {}", firstChild);
+
+        // String holder
+        final StringWriter writer = new StringWriter();
+
+        // StreamWriter which outputs JSON strings
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.create(schemaContext, writer, 2);
+
+        // NormalizedNode -> StreamWriter
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+
+        // Write multiple NormalizedNodes fluently, flush()/close() as needed
+        nodeWriter.write(firstChild).close();
+
+        // Just to put it somewhere
+        LOG.debug("Serialized JSON: {}", writer.toString());
+    }
+
+    private static SchemaContext loadModules(final String resourceDirectory) throws IOException {
+        YangContextParser parser = new YangParserImpl();
+        String path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).getPath();
+        final File testDir = new File(path);
+        final String[] fileList = testDir.list();
+        final List<File> testFiles = new ArrayList<File>();
+        if (fileList == null) {
+            throw new FileNotFoundException(resourceDirectory);
+        }
+        for (String fileName : fileList) {
+            if (new File(testDir, fileName).isDirectory() == false) {
+                testFiles.add(new File(testDir, fileName));
+            }
+        }
+        return parser.parseFiles(testFiles);
+    }
+
+    private static String loadTextFile(final String filePath) throws IOException {
+        FileReader fileReader = new FileReader(filePath);
+        BufferedReader bufReader = new BufferedReader(fileReader);
+
+        String line = null;
+        StringBuilder result = new StringBuilder();
+        while ((line = bufReader.readLine()) != null) {
+            result.append(line);
+        }
+        bufReader.close();
+        return result.toString();
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json
new file mode 100644 (file)
index 0000000..8ecaa37
--- /dev/null
@@ -0,0 +1,41 @@
+{
+    "complexjson:cont1": {
+        "lf12-any":[
+            {
+                "anyxml-in-data":"foo"
+            }
+        ],
+        
+        "lf13-any":{
+            "anyxml-in-data":"foo"
+        },
+        
+        "lf14-any":"anyxml data",
+       
+        "lflst11":["lflst11 value1","lflst11 value2"],
+        
+        "lst11":[
+            {            
+                "key111":"key111 value",
+                "lf112":"/complexjson:cont1/complexjson:lflst11",
+                "lf113":"lf113 value",
+                "lf111":"lf111 value"
+            }
+        ],        
+        "lf11" : "453",
+        "lf12_1" : "lf12 value",
+        "lf13" : "lf13 value",        
+        "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+        "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+        "lf15_11" : "one two",
+        "lf15_12" : "complexjson:lf11",
+        "lf15_21" : "lf15_21 value",
+        "lf17" : "lf17 value",        
+        
+        "lst12":[
+            {
+                "lf121":"lf121 value"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang
new file mode 100644 (file)
index 0000000..d4ea623
--- /dev/null
@@ -0,0 +1,37 @@
+module complexjson-augmentation {
+    namespace "ns:complex:json:augmentation";
+    prefix cjaug;
+    
+  import complexjson {
+    prefix cj;
+  }    
+
+    revision "2014-08-14" {        
+    }
+    
+    augment "/cj:cont1/cj:choc11/cj:c11A" {
+        leaf lf15_11  {
+                    type string;
+                }
+        leaf lf15_12  {
+                    type string;
+                }
+                
+    }    
+    
+    augment "/cj:cont1" {
+        leaf lf12_1aug {
+                    type string;
+                }
+        leaf lf12_2aug {
+            type string;
+        }
+    }    
+    
+    augment "/cj:cont1/cj:choc11/cj:c11A" {
+        leaf lf15_21aug {
+                    type string;
+                }
+    }    
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang
new file mode 100644 (file)
index 0000000..6c07d65
--- /dev/null
@@ -0,0 +1,140 @@
+module complexjson {
+    namespace "ns:complex:json";
+    prefix cj;
+
+    revision "2014-08-11" {        
+    }
+    
+    
+    identity ident;
+    
+    container cont1 {
+    
+        anyxml lf12-any;
+        anyxml lf13-any;
+        anyxml lf14-any;
+        
+        leaf lf11 {
+            type int32;
+        }
+        
+        leaf-list lflst11 {
+            type string;
+        }
+        
+        list lst11 {
+            key "key111 lf111";
+            leaf key111 {
+                type string;
+            }
+            leaf lf111 {
+                type string;
+            }
+            leaf lf112 {
+                type instance-identifier;
+            }
+            leaf lf113 {
+                type string;
+            }
+        }
+        
+        list lst12 {
+            leaf lf121 {
+                type string;
+            }
+            leaf lf122 {
+                type string;
+            }
+        }
+        
+    
+        choice choc11 {
+            case c11A {
+                leaf lf13 {
+                    type string;
+                }
+            }
+            leaf lf16 {
+                type string;
+            }
+        }
+
+        choice choc12 {
+            case c12A {
+            }
+        }
+    }
+    
+    
+    augment "/cont1/choc12" {
+        case c12B {
+            leaf lf17 {
+                type string;
+            }
+        }
+    }    
+    
+    
+    augment "/cont1" {
+        container cont11 {
+            leaf lf111 {
+                type string;
+            }
+        }        
+    }
+    
+    augment "/cont1" {
+        leaf lf12_1 {
+                    type string;
+                }
+        leaf lf12_2 {
+            type string;
+        }
+    }
+    
+    augment "/cont1" {
+        leaf lf12_3 {
+                    type string;
+                }
+    }
+    
+    
+    augment "/cont1/choc11" {
+        case c11B {
+            leaf lf14_1  {
+                    type string;
+                }
+        }
+    }
+    
+    augment "/cont1/choc11" {
+        case c11C {
+            leaf lf14_2  {
+                    type string;
+                }
+        }
+    }
+    
+    augment "/cont1/choc11/c11A" {
+        leaf lf15_11  {
+                    type bits {
+                        bit one;
+                        bit two;
+                        bit three;                        
+                    }
+                }
+        leaf lf15_12  {
+                    type identityref {
+                        base ident;
+                    }
+                }
+                
+    }
+    
+    augment "/cont1/choc11/c11A" {
+        leaf lf15_21 {
+                    type string;
+                }
+    }
+
+}