Merge changes I50218f21,I6bc2631b
authorEd Warnicke <eaw@cisco.com>
Tue, 26 Nov 2013 14:03:22 +0000 (14:03 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 26 Nov 2013 14:03:22 +0000 (14:03 +0000)
* changes:
  Serialization to Json improvement
  Union type processing added to JsonMapper

opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ToJsonBasicDataTypesTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/simple-data-types.yang
opendaylight/md-sal/sal-rest-connector/src/test/resources/yang-to-json-conversion/simple-data-types/xml/data.xml

index 5a1b42fd8009d074613407088c94359bac5dc6d9..073b24e033914a1fe2e227d6e78c351b1c75b8a0 100644 (file)
@@ -3,70 +3,58 @@ package org.opendaylight.controller.sal.rest.impl;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 import javax.activation.UnsupportedDataTypeException;
 
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-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.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.*;
+import org.opendaylight.yangtools.yang.model.api.type.*;
 
 import com.google.common.base.Preconditions;
 import com.google.gson.stream.JsonWriter;
 
 class JsonMapper {
-    
+
     private final Set<LeafListSchemaNode> foundLeafLists = new HashSet<>();
     private final Set<ListSchemaNode> foundLists = new HashSet<>();
-    
+
     public void write(JsonWriter writer, CompositeNode data, DataNodeContainer schema) throws IOException {
         Preconditions.checkNotNull(writer);
         Preconditions.checkNotNull(data);
         Preconditions.checkNotNull(schema);
 
         writer.beginObject();
-        
+
         if (schema instanceof ContainerSchemaNode) {
             writeContainer(writer, data, (ContainerSchemaNode) schema);
         } else if (schema instanceof ListSchemaNode) {
-            writeList(writer, data, (ListSchemaNode) schema);
+            writeList(writer, null, data, (ListSchemaNode) schema);
         } else {
             throw new UnsupportedDataTypeException(
                     "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
         }
-        
+
         writer.endObject();
-        
+
         foundLeafLists.clear();
         foundLists.clear();
     }
 
-    private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema) throws IOException {
+    private void writeChildrenOfParent(JsonWriter writer, CompositeNode parent, DataNodeContainer parentSchema)
+            throws IOException {
         checkNotNull(parent);
         checkNotNull(parentSchema);
-        
+
         for (Node<?> child : parent.getChildren()) {
             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
             if (childSchema == null) {
                 throw new UnsupportedDataTypeException("Probably the data node \"" + child.getNodeType().getLocalName()
                         + "\" is not conform to schema");
             }
-            
+
             if (childSchema instanceof ContainerSchemaNode) {
                 Preconditions.checkState(child instanceof CompositeNode,
                         "Data representation of Container should be CompositeNode - " + child.getNodeType());
@@ -76,14 +64,14 @@ class JsonMapper {
                     Preconditions.checkState(child instanceof CompositeNode,
                             "Data representation of List should be CompositeNode - " + child.getNodeType());
                     foundLists.add((ListSchemaNode) childSchema);
-                    writeList(writer, (CompositeNode) child, (ListSchemaNode) childSchema);
+                    writeList(writer, parent, (CompositeNode) child, (ListSchemaNode) childSchema);
                 }
             } else if (childSchema instanceof LeafListSchemaNode) {
                 if (!foundLeafLists.contains(childSchema)) {
                     Preconditions.checkState(child instanceof SimpleNode<?>,
                             "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
                     foundLeafLists.add((LeafListSchemaNode) childSchema);
-                    writeLeafList(writer, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
+                    writeLeafList(writer, parent, (SimpleNode<?>) child, (LeafListSchemaNode) childSchema);
                 }
             } else if (childSchema instanceof LeafSchemaNode) {
                 Preconditions.checkState(child instanceof SimpleNode<?>,
@@ -94,7 +82,7 @@ class JsonMapper {
                         + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
             }
         }
-        
+
         for (Node<?> child : parent.getChildren()) {
             DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
             if (childSchema instanceof LeafListSchemaNode) {
@@ -104,7 +92,7 @@ class JsonMapper {
             }
         }
     }
-    
+
     private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
         for (DataSchemaNode dsn : dataSchemaNode) {
             if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
@@ -113,84 +101,154 @@ class JsonMapper {
         }
         return null;
     }
-    
+
     private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException {
         writer.name(node.getNodeType().getLocalName());
         writer.beginObject();
         writeChildrenOfParent(writer, node, schema);
         writer.endObject();
     }
-    
-    private void writeList(JsonWriter writer, CompositeNode node, ListSchemaNode schema) throws IOException {
-            writer.name(node.getNodeType().getLocalName());
-            writer.beginArray();
-            
-            if (node.getParent() != null) {
-                CompositeNode parent = node.getParent();
-                List<CompositeNode> nodeLists = parent.getCompositesByName(node.getNodeType());
-                for (CompositeNode nodeList : nodeLists) {
-                    writer.beginObject();
-                    writeChildrenOfParent(writer, nodeList, schema);
-                    writer.endObject();
-                }
-            } else {
+
+    private void writeList(JsonWriter writer, CompositeNode nodeParent, CompositeNode node, ListSchemaNode schema) throws IOException {
+        writer.name(node.getNodeType().getLocalName());
+        writer.beginArray();
+
+        if (nodeParent != null) {
+            List<CompositeNode> nodeLists = nodeParent.getCompositesByName(node.getNodeType());
+            for (CompositeNode nodeList : nodeLists) {
                 writer.beginObject();
-                writeChildrenOfParent(writer, node, schema);
+                writeChildrenOfParent(writer, nodeList, schema);
                 writer.endObject();
             }
-            
-            writer.endArray();
-    }
-    
-    private void writeLeafList(JsonWriter writer, SimpleNode<?> node, LeafListSchemaNode schema) throws IOException {
-            writer.name(node.getNodeType().getLocalName());
-            writer.beginArray();
-            
-            CompositeNode parent = node.getParent();
-            List<SimpleNode<?>> nodeLeafLists = parent.getSimpleNodesByName(node.getNodeType());
-            for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
-                writeValueOfNodeByType(writer, nodeLeafList, schema.getType());
-            }
-            
-            writer.endArray();
+        } else {
+            writer.beginObject();
+            writeChildrenOfParent(writer, node, schema);
+            writer.endObject();
+        }
+
+        writer.endArray();
     }
-    
+
+    private void writeLeafList(JsonWriter writer, CompositeNode nodeParent, SimpleNode<?> node, LeafListSchemaNode schema) throws IOException {
+        writer.name(node.getNodeType().getLocalName());
+        writer.beginArray();
+
+        List<SimpleNode<?>> nodeLeafLists = nodeParent.getSimpleNodesByName(node.getNodeType());
+        for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
+            writeValueOfNodeByType(writer, nodeLeafList, schema.getType());
+        }
+
+        writer.endArray();
+    }
+
     private void writeLeaf(JsonWriter writer, SimpleNode<?> node, LeafSchemaNode schema) throws IOException {
         writer.name(node.getNodeType().getLocalName());
         writeValueOfNodeByType(writer, node, schema.getType());
     }
-    
-    private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type) throws IOException {
+
+    private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type)
+            throws IOException {
         if (!(node.getValue() instanceof String)) {
             throw new IllegalStateException("Value in SimpleNode should be type String");
         }
-        
+
         String value = (String) node.getValue();
-        // TODO check Leafref, InstanceIdentifierTypeDefinition, IdentityrefTypeDefinition, UnionTypeDefinition
-        if (type.getBaseType() != null) {
-            writeValueOfNodeByType(writer, node, type.getBaseType());
-        } else if (type instanceof InstanceIdentifierTypeDefinition) {
-            writer.value(((InstanceIdentifierTypeDefinition) type).getPathStatement().toString());
-        } else if (type instanceof DecimalTypeDefinition 
-                || type instanceof IntegerTypeDefinition
-                || type instanceof UnsignedIntegerTypeDefinition) {
+        // TODO check Leafref, InstanceIdentifierTypeDefinition,
+        // IdentityrefTypeDefinition, UnionTypeDefinition
+        TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
+        if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            writer.value(((InstanceIdentifierTypeDefinition) baseType).getPathStatement().toString());
+        } else if (baseType instanceof UnionTypeDefinition) {
+            processTypeIsUnionType(writer, (UnionTypeDefinition) baseType, value);
+        } else if (baseType instanceof DecimalTypeDefinition || baseType instanceof IntegerTypeDefinition
+                || baseType instanceof UnsignedIntegerTypeDefinition) {
             writer.value(new NumberForJsonWriter(value));
-        } else if (type instanceof BooleanTypeDefinition) {
+        } else if (baseType instanceof BooleanTypeDefinition) {
             writer.value(Boolean.parseBoolean(value));
-        } else if (type instanceof EmptyTypeDefinition) {
-            writer.beginArray();
-            writer.nullValue();
-            writer.endArray();
+        } else if (baseType instanceof EmptyTypeDefinition) {
+            writeEmptyDataTypeToJson(writer);
         } else {
             writer.value(value != null ? value : "");
         }
     }
-    
+
+    private void processTypeIsUnionType(JsonWriter writer, UnionTypeDefinition unionType, String value)
+            throws IOException {
+        if (value == null) {
+            writeEmptyDataTypeToJson(writer);
+        } else if ((isNumber(value))
+                && containsType(unionType, UnsignedIntegerTypeDefinition.class, IntegerTypeDefinition.class,
+                        DecimalTypeDefinition.class)) {
+            writer.value(new NumberForJsonWriter(value));
+        } else if (isBoolean(value) && containsType(unionType, BooleanTypeDefinition.class)) {
+            writer.value(Boolean.parseBoolean(value));
+        } else {
+            writer.value(value);
+        }
+    }
+
+    private boolean isBoolean(String value) {
+        if (value.equals("true") || value.equals("false")) {
+            return true;
+        }
+        return false;
+    }
+
+    private void writeEmptyDataTypeToJson(JsonWriter writer) throws IOException {
+        writer.beginArray();
+        writer.nullValue();
+        writer.endArray();
+    }
+
+    private boolean isNumber(String value) {
+        try {
+            Double.valueOf(value);
+        } catch (NumberFormatException e) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean containsType(UnionTypeDefinition unionType, Class<?>... searchedTypes) {
+        List<TypeDefinition<?>> allUnionSubtypes = resolveAllUnionSubtypesFrom(unionType);
+
+        for (TypeDefinition<?> unionSubtype : allUnionSubtypes) {
+            for (Class<?> searchedType : searchedTypes) {
+                if (searchedType.isInstance(unionSubtype)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private List<TypeDefinition<?>> resolveAllUnionSubtypesFrom(UnionTypeDefinition inputType) {
+        List<TypeDefinition<?>> result = new ArrayList<>();
+        for (TypeDefinition<?> subtype : inputType.getTypes()) {
+            TypeDefinition<?> resolvedSubtype = subtype;
+
+            resolvedSubtype = resolveBaseTypeFrom(subtype);
+
+            if (resolvedSubtype instanceof UnionTypeDefinition) {
+                List<TypeDefinition<?>> subtypesFromRecursion = resolveAllUnionSubtypesFrom((UnionTypeDefinition) resolvedSubtype);
+                result.addAll(subtypesFromRecursion);
+            } else {
+                result.add(resolvedSubtype);
+            }
+        }
+
+        return result;
+    }
+
+    private TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) {
+        return type.getBaseType() != null ? resolveBaseTypeFrom(type.getBaseType()) : type;
+    }
+
     private static final class NumberForJsonWriter extends Number {
-        
+
         private static final long serialVersionUID = -3147729419814417666L;
         private final String value;
-        
+
         public NumberForJsonWriter(String value) {
             this.value = value;
         }
@@ -219,7 +277,7 @@ class JsonMapper {
         public String toString() {
             return value;
         }
-        
+
     }
 
 }
index ede225c709ea89f53b411ecf14fc366dda27522b..2e8b07151914b12c9b0c2a360b1c9b075c0a880a 100644 (file)
@@ -90,7 +90,7 @@ public class FromJsonToCompositeNodeTest {
     }
 
     @Test
-    public void nullArrayToCompositeNodeWithNullValueTest() {
+    public void nullArrayToSimpleNodeWithNullValueTest() {
         CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/array-with-null.json", true);
         assertNotNull(compositeNode);
         assertEquals("cont", compositeNode.getNodeType().getLocalName());
index 1d8d7495f9dfabae21bdddb737401032af34724b..c43de5d9e8525091eb535703381cb222ac42b9ca 100644 (file)
@@ -320,15 +320,15 @@ final class TestUtils {
         if (modules.size() < 1) {
             return null;
         }
-        
-        Module moduleRes = null;        
+
+        Module moduleRes = null;
         if (modules.size() > 1) {
             if (moduleName == null) {
                 return null;
             } else {
-                for (Module module: modules) {
+                for (Module module : modules) {
                     if (module.getName().equals(moduleName)) {
-                        moduleRes = module; 
+                        moduleRes = module;
                     }
                 }
                 if (moduleRes == null) {
@@ -338,7 +338,7 @@ final class TestUtils {
         } else {
             moduleRes = modules.iterator().next();
         }
-        
+
         if (moduleRes.getChildNodes() == null) {
             return null;
         }
index f88a335f18c9ac37927f34e973ab469ebaa0315c..69de9f86c1defa491003d13db1a4825183deb521 100644 (file)
@@ -18,10 +18,6 @@ public class ToJsonBasicDataTypesTest {
     @Test
     public void simpleYangDataTest() {
         String jsonOutput;
-        // jsonOutput =
-        // TestUtils.readJsonFromFile("/yang-to-json-conversion/simple-yang-types/xml/awaited_output.json",
-        // false);
-
         jsonOutput = TestUtils.convertCompositeNodeDataAndYangToJson(
                 TestUtils.loadCompositeNode("/yang-to-json-conversion/simple-data-types/xml/data.xml"),
                 "/yang-to-json-conversion/simple-data-types", "/yang-to-json-conversion/simple-data-types/xml");
@@ -73,7 +69,7 @@ public class ToJsonBasicDataTypesTest {
         // boolean lfref1Checked = false;
         boolean lfemptyChecked = false;
         boolean lfstr1Checked = false;
-        
+
         while (jReader.hasNext()) {
             String keyName = jReader.nextName();
             JsonToken peek = null;
@@ -148,10 +144,8 @@ public class ToJsonBasicDataTypesTest {
                 jReader.nextNull();
                 jReader.endArray();
                 lfemptyChecked = true;
-                // TODO: test will be implemented when functionality will be
-                // implemented
-            } else if (keyName.equals("lflstunion")) {
-                jReader.skipValue();
+            } else if (keyName.startsWith("lfunion")) {
+                checkLfUnion(jReader, keyName, peek);
             } else {
                 assertTrue("Key " + keyName + " doesn't exists in yang file.", false);
             }
@@ -180,4 +174,44 @@ public class ToJsonBasicDataTypesTest {
         jReader.endObject();
 
     }
+
+    private void checkLfUnion(JsonReader jReader, String keyName, JsonToken peek) throws IOException {
+        if (keyName.equals("lfunion1")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion2")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion3")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextInt();
+        } else if (keyName.equals("lfunion4")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
+            jReader.nextBoolean();
+        } else if (keyName.equals("lfunion5")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion6")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion7")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion8")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion9")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion10")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.STRING, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion11")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.NUMBER, peek);
+            jReader.nextString();
+        } else if (keyName.equals("lfunion12")) {
+            assertEquals("Key " + keyName + " has incorrect type", JsonToken.BOOLEAN, peek);
+            jReader.nextBoolean();
+        }
+    }
 }
index 010d3b1c2b374c1a9002877700bac6787ea6b676..759b3ecf71cd564a8702bb694f7b9177748b8c30 100644 (file)
@@ -5,6 +5,45 @@ module simple-data-types {
   revision 2013-11-12 {    
   }
   
+  typedef tpdfempty {
+       type empty;
+  }
+  
+  typedef tpdfbit {
+       type bits {
+               bit b1;
+               bit b2;
+               bit b3;
+       }
+  }
+  
+  typedef tpdfun4 {
+       type boolean;
+  }
+  
+  typedef tpdfun3 {
+       type union {
+               type tpdfbit;
+               type tpdfempty;
+       }
+  }  
+  
+  typedef tpdfun2 {
+       type union {
+               type tpdfun3;
+               type tpdfun4;
+       }
+  }
+  
+  typedef tpdfun1 {
+       type union {
+               type uint8;
+               type decimal64 {
+                       fraction-digits 2;
+               }
+       }
+  }
+  
   container cont {
          leaf lfnint8Min {
                type int8; 
@@ -125,12 +164,87 @@ module simple-data-types {
        type empty;
     }
     
-    leaf-list lflstunion {
+    leaf lfunion1 {
        type union {
                type uint16;
                type string;
        }
     }
+    leaf lfunion2 {
+       type union {
+               type decimal64 {
+                               fraction-digits 2;              
+               }
+               type string;
+       }
+    }
+    
+    leaf lfunion3 {
+       type union {
+               type empty;
+               type string;
+       }
+    }
+    
+    leaf lfunion4 {
+       type union {
+               type boolean;
+               type string;
+       }
+    }
+    
+    leaf lfunion5 {
+       type union {
+               type uint16;
+               type string;
+       }
+    }
+    
+    leaf lfunion6 {
+       type union {
+               type uint16;
+               type empty;
+       }
+    }
+    
+    leaf lfunion7 {
+               type tpdfun3;
+    }
+    
+    leaf lfunion8 {
+       type union {
+               type uint16;
+               type string;
+       }
+    }
+    
+    leaf lfunion9 {
+       type union {
+               type uint16;
+               type boolean;                   
+       }
+    }
+    
+    leaf lfunion10 {
+       type union {
+               type bits {
+                       bit bt1;
+                       bit bt2;
+               }
+               type boolean;                   
+       }
+    }
+    
+    leaf lfunion11 {
+               type union {
+                       type tpdfun1;
+                       type tpdfun2;
+               }
+    }
+    
+    leaf lfunion12 {
+               type tpdfun2;
+    }
          
          
   }
index df00ca917e7d16f1e5f8a65bdaec9f10671cecc8..0d31e9037a763b3732274da5eddc2512a8c8ecb4 100644 (file)
        <lfbits>bit3</lfbits>   
        <lfbinary>AAaacdabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%%-#^</lfbinary>
        <lfempty></lfempty>
-       <lflstunion>324</lflstunion>
-       <lflstunion>33.3</lflstunion>
-       <lflstunion>lfunion</lflstunion>
-       <lflstunion>true</lflstunion>
+       <lfunion1>324</lfunion1>
+       <lfunion2>33.3</lfunion2>
+       <lfunion3>55</lfunion3>
+       <lfunion4>true</lfunion4>
+       <lfunion5>true</lfunion5>
+       <lfunion6>false</lfunion6>
+       <lfunion7></lfunion7>
+       <lfunion8></lfunion8>
+       <lfunion9></lfunion9>
+       <lfunion10>bt1</lfunion10>
+       <lfunion11>33</lfunion11>
+       <lfunion12>false</lfunion12>
 </cont>
\ No newline at end of file