Revert "Revert "BUG 2282 - JSON top level element without module name"" 16/12716/1
authorThanh Ha <thanh.ha@linuxfoundation.org>
Tue, 11 Nov 2014 03:35:26 +0000 (22:35 -0500)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Tue, 11 Nov 2014 03:35:34 +0000 (22:35 -0500)
To undo revert commit b1135e4f2e6e6856322d012eb732a01d6f7b68ed for
Helium SR1.

Change-Id: I58fc5c4ed5dd02aed8355155e86bff3a61326c9a
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonStreamToNormalizedNodeTest.java
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/TestingNormalizedNodeStructuresCreator.java
yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/unkeyed-node-in-container.json
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation.yang

index 93946567dcb9080e98d131c9922afc3acdd220a5..724c85cff1dcbda40d3fbec38f5392edbc1db8de 100644 (file)
@@ -8,7 +8,6 @@
 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.gson.JsonIOException;
 import com.google.gson.JsonParseException;
@@ -24,6 +23,7 @@ import java.net.URI;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.List;
@@ -146,7 +146,7 @@ public final class JsonParserStream implements Closeable, Flushable {
             in.beginObject();
             while (in.hasNext()) {
                 final String jsonElementName = in.nextName();
-                final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName);
+                final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName, parent.getSchema());
                 final String localName = namespaceAndName.getName();
                 addNamespace(namespaceAndName.getUri());
                 if (namesakes.contains(jsonElementName)) {
@@ -213,19 +213,11 @@ public final class JsonParserStream implements Closeable, Flushable {
         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 void addNamespace(final URI namespace) {
+        namespaces.push(namespace);
     }
 
-    private NamespaceAndName resolveNamespace(final String childName) {
+    private NamespaceAndName resolveNamespace(final String childName, final DataSchemaNode dataSchemaNode) {
         int lastIndexOfColon = childName.lastIndexOf(':');
         String moduleNamePart = null;
         String nodeNamePart = null;
@@ -240,8 +232,52 @@ public final class JsonParserStream implements Closeable, Flushable {
             nodeNamePart = childName;
         }
 
-        Optional<URI> namespaceOpt = namespace == null ? Optional.<URI> absent() : Optional.of(namespace);
-        return new NamespaceAndName(nodeNamePart, namespaceOpt);
+        if (namespace == null) {
+            Set<URI> potentialUris = Collections.emptySet();
+            potentialUris = resolveAllPotentialNamespaces(nodeNamePart, dataSchemaNode);
+            if (potentialUris.contains(getCurrentNamespace())) {
+                namespace = getCurrentNamespace();
+            } else if (potentialUris.size() == 1) {
+                namespace = potentialUris.iterator().next();
+            } else if (potentialUris.size() > 1) {
+                throw new IllegalStateException("Choose suitable module name for element "+nodeNamePart+":"+toModuleNames(potentialUris));
+            } else if (potentialUris.isEmpty()) {
+                throw new IllegalStateException("Schema node with name "+nodeNamePart+" wasn't found.");
+            }
+        }
+
+        return new NamespaceAndName(nodeNamePart, namespace);
+    }
+
+    private String toModuleNames(Set<URI> potentialUris) {
+        final StringBuilder builder = new StringBuilder();
+        for (URI potentialUri : potentialUris) {
+            builder.append("\n");
+            //FIXME how to get information about revision from JSON input? currently first available is used.
+            builder.append(schema.findModuleByNamespace(potentialUri).iterator().next().getName());
+        }
+        return builder.toString();
+    }
+
+    private Set<URI> resolveAllPotentialNamespaces(final String elementName, final DataSchemaNode dataSchemaNode) {
+        final Set<URI> potentialUris = new HashSet<>();
+        final Set<ChoiceNode> choices = new HashSet<>();
+        if (dataSchemaNode instanceof DataNodeContainer) {
+            for (DataSchemaNode childSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+                if (childSchemaNode instanceof ChoiceNode) {
+                    choices.add((ChoiceNode)childSchemaNode);
+                } else if (childSchemaNode.getQName().getLocalName().equals(elementName)) {
+                    potentialUris.add(childSchemaNode.getQName().getNamespace());
+                }
+            }
+
+            for (ChoiceNode choiceNode : choices) {
+                for (ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+                    potentialUris.addAll(resolveAllPotentialNamespaces(elementName, concreteCase));
+                }
+            }
+        }
+        return potentialUris;
     }
 
     private URI getCurrentNamespace() {
@@ -292,10 +328,10 @@ public final class JsonParserStream implements Closeable, Flushable {
     }
 
     private static class NamespaceAndName {
-        private final Optional<URI> uri;
+        private final URI uri;
         private final String name;
 
-        public NamespaceAndName(final String name, final Optional<URI> uri) {
+        public NamespaceAndName(final String name, final URI uri) {
             this.name = name;
             this.uri = uri;
         }
@@ -304,7 +340,7 @@ public final class JsonParserStream implements Closeable, Flushable {
             return name;
         }
 
-        public Optional<URI> getUri() {
+        public URI getUri() {
             return uri;
         }
     }
index 56d12104bd8b86e27888762767bca7f4b5b88d81..1a71142be5553ecee451b42edb5fce7d02bf2612 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.data.codec.gson;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
 import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
 
@@ -133,6 +134,57 @@ public class JsonStreamToNormalizedNodeTest {
         verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
     }
 
+    /**
+     * Top level JSON element contains no information about module name.
+     *
+     * It should be possible to find out potential module name from available schema context.
+     *
+     */
+    @Test
+    public void missingModuleInfoInTopLevelElement() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/missing-module-in-top-level.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.topLevelContainer());
+    }
+
+    /**
+     *
+     * Exception expected.
+     *
+     * It tests case when several elements with the same name and various namespaces exists and are in JSON specified
+     * without module name prefix.
+     */
+    @Test
+    public void leafNamesakes() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/namesakes.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+        } catch (IllegalStateException e) {
+            final String errorMessage = e.getMessage();
+            assertTrue(errorMessage.contains("Choose suitable module name for element lf11-namesake:"));
+            assertTrue(errorMessage.contains("complexjson-augmentation"));
+            assertTrue(errorMessage.contains("complexjson-augmentation-namesake"));
+        }
+    }
+
+    /**
+     *
+     * Exception expected.
+     *
+     * Json input contains element which doesn't exist in YANG schema
+     */
+    @Test
+    public void parsingNotExistingElement() throws IOException, URISyntaxException {
+        String inputJson = loadTextFile("/complexjson/not-existing-element.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Schema node with name dummy-element wasn't found."));
+        }
+    }
+
+
     private void verifyTransformationToNormalizedNode(final String inputJson,
             final NormalizedNode<?, ?> awaitedStructure) {
         NormalizedNodeResult result = new NormalizedNodeResult();
index 1038ce883db6d8389f6ed15a77cc243b05011e0a..8898cee98158d17df767e405a14d43ef1ea7818a 100644 (file)
@@ -314,4 +314,7 @@ public class TestingNormalizedNodeStructuresCreator {
         return cont1Node(lst12Node());
     }
 
+    public static NormalizedNode<?, ?> topLevelContainer() {
+        return cont1Node();
+    }
 }
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/missing-module-in-top-level.json
new file mode 100644 (file)
index 0000000..1070c36
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "cont1": {
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/namesakes.json
new file mode 100644 (file)
index 0000000..9c0fab4
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "cont1": {
+        "lf11-namesake":"value lf11"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/not-existing-element.json
new file mode 100644 (file)
index 0000000..ebc44bb
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "cont1": {
+        "dummy-element":"value lf11"
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson-augmentation-namesake.yang
new file mode 100644 (file)
index 0000000..e94acb2
--- /dev/null
@@ -0,0 +1,18 @@
+module complexjson-augmentation-namesake {
+    namespace "ns:complex:json:augmentation:namesake";
+    prefix cjaugnmsk;
+
+  import complexjson {
+    prefix cj;
+  }
+
+    revision "2014-08-14" {
+    }
+
+    augment "/cj:cont1" {
+        leaf lf11-namesake {
+            type string;
+        }
+    }
+
+}
index d4ea623b19098c9d073b2d00012839ad99f16113..a4cf5adeb52738569597e4aa6614ea2ff8e5a12b 100644 (file)
@@ -1,24 +1,30 @@
 module complexjson-augmentation {
     namespace "ns:complex:json:augmentation";
     prefix cjaug;
-    
+
   import complexjson {
     prefix cj;
-  }    
+  }
 
-    revision "2014-08-14" {        
+    revision "2014-08-14" {
     }
-    
+
+    augment "/cj:cont1" {
+        leaf lf11-namesake {
+            type string;
+        }
+    }
+
     augment "/cj:cont1/cj:choc11/cj:c11A" {
-        leaf lf15_11  {
+        leaf lf15_11 {
                     type string;
                 }
-        leaf lf15_12  {
+        leaf lf15_12 {
                     type string;
                 }
-                
-    }    
-    
+
+    }
+
     augment "/cj:cont1" {
         leaf lf12_1aug {
                     type string;
@@ -26,12 +32,12 @@ module complexjson-augmentation {
         leaf lf12_2aug {
             type string;
         }
-    }    
-    
+    }
+
     augment "/cj:cont1/cj:choc11/cj:c11A" {
         leaf lf15_21aug {
                     type string;
                 }
-    }    
+    }
 
 }