Fixed serialization issues with Augments and java types 12/3412/2
authorTony Tkacik <ttkacik@cisco.com>
Tue, 3 Dec 2013 19:00:20 +0000 (20:00 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Tue, 3 Dec 2013 19:39:27 +0000 (20:39 +0100)
Change-Id: I55a522423c4bb6ac809527c175d1689897c35fb2
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
opendaylight/md-sal/sal-rest-connector/pom.xml
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/CompositeNodeWrapper.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/SimpleNodeWrapper.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java

index 3e13a58..5e798b6 100644 (file)
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-impl</artifactId>
       <version>${yang.version}</version>
+    </dependency>
+        <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-model-util</artifactId>
+      <version>${yang.version}</version>
     </dependency>
     <dependency>
       <groupId>com.google.code.gson</groupId>
index a2ae1c9..83e2d20 100644 (file)
@@ -7,6 +7,7 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
 
 import com.google.common.collect.Lists;
@@ -69,7 +70,7 @@ class JsonReader {
             }
         } else if (childType.isJsonArray()) {
             if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
-                parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), null));
+                parent.addValue(new EmptyNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName)));
 
             } else {
                 for (JsonElement childOfChildType : childType.getAsJsonArray()) {
index bf7ff7d..a532814 100644 (file)
@@ -14,8 +14,10 @@ import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
 
 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.yangtools.yang.data.api.Node;
 
 public class XmlReader {
 
@@ -121,7 +123,7 @@ public class XmlReader {
         return false;
     }
 
-    private SimpleNodeWrapper resolveSimpleNodeFromStartElement(final StartElement startElement)
+    private NodeWrapper<? extends Node<?>> resolveSimpleNodeFromStartElement(final StartElement startElement)
             throws XMLStreamException {
         checkArgument(startElement != null, "Start Element cannot be NULL!");
         String data = null;
@@ -141,7 +143,9 @@ public class XmlReader {
                 }
             }
         }
-
+        if(data == null) {
+            return new EmptyNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement));
+        }
         return new SimpleNodeWrapper(getNamespaceFrom(startElement), getLocalNameFrom(startElement), data);
     }
 
index 470b473..602e8b9 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition
 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil
 
 import static com.google.common.base.Preconditions.*
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec
 
 class ControllerContext implements SchemaServiceListener {
 
@@ -288,9 +289,10 @@ class ControllerContext implements SchemaServiceListener {
     private def void addKeyValue(HashMap<QName, Object> map, DataSchemaNode node, String uriValue) {
         checkNotNull(uriValue);
         checkArgument(node instanceof LeafSchemaNode);
-        val decoded = URLDecoder.decode(uriValue);
+        val urlDecoded = URLDecoder.decode(uriValue);
+        val typedef = (node as LeafSchemaNode).type;
+        val decoded = TypeDefinitionAwareCodec.from(typedef)?.deserialize(urlDecoded)
         map.put(node.QName, decoded);
-
     }
 
     private def String toModuleName(String str) {
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/EmptyNodeWrapper.java
new file mode 100644 (file)
index 0000000..c146954
--- /dev/null
@@ -0,0 +1,111 @@
+package org.opendaylight.controller.sal.restconf.impl;
+
+import java.net.URI;
+import java.util.Collections;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+
+import com.google.common.base.Preconditions;
+
+public final class EmptyNodeWrapper implements NodeWrapper<Node<?>>, Node<Void> {
+    
+    private Node<?> unwrapped;
+    
+    private String localName;
+    private URI namespace;
+    private QName name;
+
+    private boolean composite;
+
+    public boolean isComposite() {
+        return composite;
+    }
+    
+    public void setComposite(boolean composite) {
+        this.composite = composite;
+    }
+    
+    public EmptyNodeWrapper(URI namespace, String localName) {
+        this.localName = Preconditions.checkNotNull(localName);
+        this.namespace = namespace;
+    }
+    
+    @Override
+    public void setQname(QName name) {
+        Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies.");
+        this.name = name;
+    }
+    
+    @Override
+    public String getLocalName() {
+        if (unwrapped != null) {
+            return unwrapped.getNodeType().getLocalName();
+        }
+        return localName;
+    }
+    
+    @Override
+    public URI getNamespace() {
+        if (unwrapped != null) {
+            return unwrapped.getNodeType().getNamespace();
+        }
+        return namespace;
+    }
+
+    @Override
+    public void setNamespace(URI namespace) {
+        Preconditions.checkState(unwrapped == null, "Cannot change the object, due to data inconsistencies.");
+        this.namespace = namespace;
+    }
+
+    @Override
+    public Node<?> unwrap() {
+        if (unwrapped == null) {
+            if (name == null) {
+                Preconditions.checkNotNull(namespace);
+                name = new QName(namespace, localName);
+            }
+            if(composite) {
+                unwrapped = NodeFactory.createImmutableCompositeNode(name, null, Collections.<Node<?>>emptyList(),null);
+            } else {
+                unwrapped = NodeFactory.createImmutableSimpleNode(name, null, null);
+            }
+            namespace = null;
+            localName = null;
+            name = null;
+        }
+        return unwrapped;
+    }
+
+    @Override
+    public QName getNodeType() {
+        return unwrap().getNodeType();
+    }
+
+    @Override
+    public CompositeNode getParent() {
+        return unwrap().getParent();
+    }
+
+    @Override
+    public Void getValue() {
+        return null;
+    }
+
+    @Override
+    public QName getKey() {
+        return unwrap().getKey();
+    }
+
+    @Override
+    public Void setValue(Void value) {
+        return null;
+    }
+
+}
index a6a71fe..35352e0 100644 (file)
@@ -12,6 +12,12 @@ import org.opendaylight.yangtools.yang.data.impl.NodeFactory
 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.data.impl.codec.TypeDefinitionAwareCodec
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
 
 class RestconfImpl implements RestconfService {
     
@@ -54,7 +60,7 @@ class RestconfImpl implements RestconfService {
 
     override createConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
-        val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
+        val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
         val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
         switch status.result {
             case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
@@ -64,7 +70,7 @@ class RestconfImpl implements RestconfService {
 
     override updateConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
-        val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
+        val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
         val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
         switch status.result {
             case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
@@ -77,7 +83,7 @@ class RestconfImpl implements RestconfService {
         if (rpc === null) {
             throw new ResponseException(Response.Status.NOT_FOUND, "RPC does not exist.");
         }
-        val value = resolveNodeNamespaceBySchema(payload, rpc.input)
+        val value = normalizeNode(payload, rpc.input)
         val List<Node<?>> input = new ArrayList
         input.add(value)
         val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
@@ -113,38 +119,69 @@ class RestconfImpl implements RestconfService {
         return identifierWithSchemaNode
     }
     
-    private def CompositeNode resolveNodeNamespaceBySchema(CompositeNode node, DataSchemaNode schema) {
+    private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema) {
         if (node instanceof CompositeNodeWrapper) {
-            addNamespaceToNodeFromSchemaRecursively(node as CompositeNodeWrapper, schema)
+            normalizeNode(node as CompositeNodeWrapper, schema,null)
             return (node as CompositeNodeWrapper).unwrap()
         }
         return node
     }
 
-    private def void addNamespaceToNodeFromSchemaRecursively(NodeWrapper<?> nodeBuilder, DataSchemaNode schema) {
+    private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema,QName previousAugment) {
         if (schema === null) {
             throw new ResponseException(Response.Status.BAD_REQUEST,
                 "Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema.");
         }
-        val moduleName = controllerContext.findModuleByNamespace(schema.QName.namespace);
-        if (nodeBuilder.namespace === null || nodeBuilder.namespace == schema.QName.namespace ||
+        var validQName = schema.QName
+        var currentAugment = previousAugment;
+        if(schema.augmenting) {
+            currentAugment = schema.QName
+        } else if(previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
+            validQName = QName.create(currentAugment,schema.QName.localName);
+        }
+        val moduleName = controllerContext.findModuleByNamespace(validQName.namespace);
+        if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
             nodeBuilder.namespace.path == moduleName) {
-            nodeBuilder.qname = schema.QName
+            nodeBuilder.qname = validQName
         } else {
             throw new ResponseException(Response.Status.BAD_REQUEST,
                 "Data has bad format\nIf data is in XML format then namespace for " + nodeBuilder.localName +
                     " should be " + schema.QName.namespace + ".\n If data is in Json format then module name for " +
                     nodeBuilder.localName + " should be " + moduleName + ".");
         }
+        
         if (nodeBuilder instanceof CompositeNodeWrapper) {
             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
             for (child : children) {
-                addNamespaceToNodeFromSchemaRecursively(child,
-                    findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes))
+                normalizeNode(child,
+                    findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),currentAugment)
+            }
+        } else if (nodeBuilder instanceof SimpleNodeWrapper) {
+            val simpleNode = (nodeBuilder as SimpleNodeWrapper)
+            val stringValue = simpleNode.value as String;
+            
+            val objectValue = TypeDefinitionAwareCodec.from(schema.typeDefinition)?.deserialize(stringValue);
+            simpleNode.setValue(objectValue)
+        } else if (nodeBuilder instanceof EmptyNodeWrapper) {
+            val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
+            if(schema instanceof LeafSchemaNode) {
+                emptyNodeBuilder.setComposite(false);
+            } else if(schema instanceof ContainerSchemaNode) {
+                // FIXME: Add presence check
+                emptyNodeBuilder.setComposite(true);
             }
         }
     }
     
+    private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
+        node.type
+    }
+    
+    private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
+        node.type
+    }
+    
+    
     private def DataSchemaNode findFirstSchemaByLocalName(String localName, Set<DataSchemaNode> schemas) {
         for (schema : schemas) {
             if (schema instanceof ChoiceNode) {
@@ -155,7 +192,11 @@ class RestconfImpl implements RestconfService {
                     }
                 }
             } else {
-                return schemas.findFirst[n|n.QName.localName.equals(localName)]
+                val result = schemas.findFirst[n|n.QName.localName.equals(localName)]
+                if(result !== null) {
+                    return result;
+                
+                }
             }
         }
         return null
index c157689..9f3c6e5 100644 (file)
@@ -11,16 +11,16 @@ import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
 
 import com.google.common.base.Preconditions;
 
-public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, SimpleNode<String> {
+public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, SimpleNode<Object> {
     
-    private SimpleNode<String> simpleNode;
+    private SimpleNode<?> simpleNode;
     
     private String localName;
-    private String value;
+    private Object value;
     private URI namespace;
     private QName name;
 
-    public SimpleNodeWrapper(String localName, String value) {
+    public SimpleNodeWrapper(String localName, Object value) {
         this.localName = Preconditions.checkNotNull(localName);
         this.value = value;
     }
@@ -59,7 +59,7 @@ public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, Simp
     }
 
     @Override
-    public SimpleNode<String> unwrap() {
+    public SimpleNode<Object> unwrap() {
         if (simpleNode == null) {
             if (name == null) {
                 Preconditions.checkNotNull(namespace);
@@ -72,7 +72,7 @@ public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, Simp
             localName = null;
             name = null;
         }
-        return simpleNode;
+        return (SimpleNode<Object>) simpleNode;
     }
 
     @Override
@@ -86,7 +86,7 @@ public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, Simp
     }
 
     @Override
-    public String getValue() {
+    public Object getValue() {
         return unwrap().getValue();
     }
 
@@ -96,7 +96,7 @@ public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, Simp
     }
 
     @Override
-    public MutableSimpleNode<String> asMutable() {
+    public MutableSimpleNode<Object> asMutable() {
         return unwrap().asMutable();
     }
 
@@ -106,9 +106,10 @@ public final class SimpleNodeWrapper implements NodeWrapper<SimpleNode<?>>, Simp
     }
 
     @Override
-    public String setValue(String value) {
+    public Object setValue(Object value) {
         return unwrap().setValue(value);
     }
+    
 
 
 }
index 5aa6d03..a72bd60 100644 (file)
@@ -138,7 +138,7 @@ public class FromXmlToCompositeNodeTest {
         SimpleNode<?> cont11_lf111 = (SimpleNode<?>) cont11.getChildren().get(0);
         assertEquals(nameSpaceCont, cont11_lf111.getNodeType().getNamespace().toString());
         assertEquals("lf111", cont11_lf111.getNodeType().getLocalName());
-        assertEquals("100", cont11_lf111.getValue());
+        assertEquals((short) 100, cont11_lf111.getValue());
         // :lst1_2
 
     }
@@ -251,7 +251,7 @@ public class FromXmlToCompositeNodeTest {
         SimpleNode<?> cont1_lf11 = (SimpleNode<?>) cont1suf.getChildren().get(0);
         assertEquals(nameSpace, cont1_lf11.getNodeType().getNamespace().toString());
         assertEquals("lf11" + suf, cont1_lf11.getNodeType().getLocalName());
-        assertEquals("100", cont1_lf11.getValue());
+        assertEquals((short) 100, cont1_lf11.getValue());
     }
 
     private CompositeNode compositeContainerFromXml(String xmlPath, boolean dummyNamespaces) {