Merge "BUG 1440 - json stream to normalized node stream writer"
authorTony Tkacik <ttkacik@cisco.com>
Mon, 25 Aug 2014 07:59:36 +0000 (07:59 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 25 Aug 2014 07:59:36 +0000 (07:59 +0000)
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;
+                }
+    }
+
+}