Merge "Leafref and identityref types to Json"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / rest / impl / JsonMapper.java
index 55751e5ac7c33a583ea8d4ea36fdf2a4dbe2ce14..a42c468c2af27ea5f2cb84887757ed8057a4466f 100644 (file)
@@ -3,67 +3,89 @@ 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 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 java.util.*;
 
+import javax.activation.UnsupportedDataTypeException;
+
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.*;
+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();
-        writeChildrenOfParent(writer, data, schema);
+
+        if (schema instanceof ContainerSchemaNode) {
+            writeContainer(writer, data, (ContainerSchemaNode) schema);
+        } else if (schema instanceof ListSchemaNode) {
+            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 = findSchemaForNode(child, parentSchema.getChildNodes());
+            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());
                 writeContainer(writer, (CompositeNode) child, (ContainerSchemaNode) childSchema);
             } else if (childSchema instanceof ListSchemaNode) {
                 if (!foundLists.contains(childSchema)) {
+                    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<?>,
+                        "Data representation of LeafList should be SimpleNode - " + child.getNodeType());
                 writeLeaf(writer, (SimpleNode<?>) child, (LeafSchemaNode) childSchema);
+            } else {
+                throw new UnsupportedDataTypeException("Schema can be ContainerSchemaNode, ListSchemaNode, "
+                        + "LeafListSchemaNode, or LeafSchemaNode. Other types are not supported yet.");
             }
         }
-        
+
         for (Node<?> child : parent.getChildren()) {
-            DataSchemaNode childSchema = findSchemaForNode(child, parentSchema.getChildNodes());
+            DataSchemaNode childSchema = findFirstSchemaForNode(child, parentSchema.getChildNodes());
             if (childSchema instanceof LeafListSchemaNode) {
                 foundLeafLists.remove((LeafListSchemaNode) childSchema);
             } else if (childSchema instanceof ListSchemaNode) {
@@ -71,91 +93,200 @@ class JsonMapper {
             }
         }
     }
-    
-    private DataSchemaNode findSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
+
+    private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
         for (DataSchemaNode dsn : dataSchemaNode) {
             if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
                 return dsn;
+            } else if (dsn instanceof ChoiceNode) {
+                for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
+                    DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
+                    if (foundDsn != null) {
+                        return foundDsn;
+                    }
+                }
             }
         }
         return null;
     }
-    
+
     private void writeContainer(JsonWriter writer, CompositeNode node, ContainerSchemaNode schema) throws IOException {
-        writer.name(node.getNodeType().getLocalName());
+        writeName(node, schema, writer);
         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 {
+        writeName(node, schema, writer);
+        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 {
+        writeName(node, schema, writer);
+        writer.beginArray();
+
+        List<SimpleNode<?>> nodeLeafLists = nodeParent.getSimpleNodesByName(node.getNodeType());
+        for (SimpleNode<?> nodeLeafList : nodeLeafLists) {
+            writeValueOfNodeByType(writer, nodeLeafList, schema.getType(), schema);
+        }
+        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 {
-        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) {
+        writeName(node, schema, writer);
+        writeValueOfNodeByType(writer, node, schema.getType(), schema);
+    }
+
+    private void writeValueOfNodeByType(JsonWriter writer, SimpleNode<?> node, TypeDefinition<?> type,
+            DataSchemaNode schema) throws IOException {
+
+        String value = String.valueOf(node.getValue());
+        TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
+
+        // TODO check InstanceIdentifierTypeDefinition,
+        // IdentityrefTypeDefinition
+        if (baseType instanceof IdentityrefTypeDefinition) {
+            if (node.getValue() instanceof QName) {
+                QName qName = (QName) node.getValue();
+
+                ControllerContext contContext = ControllerContext.getInstance();
+                String moduleName = contContext.findModuleByNamespace(qName.getNamespace());
+
+                writer.value(moduleName + ":" + qName.getLocalName());
+            }
+
+        } else if (baseType instanceof LeafrefTypeDefinition) {
+            ControllerContext contContext = ControllerContext.getInstance();
+            LeafSchemaNode lfSchemaNode = contContext.resolveTypeFromLeafref((LeafrefTypeDefinition) baseType, schema);
+            if (lfSchemaNode != null) {
+                writeValueOfNodeByType(writer, node, lfSchemaNode.getType(), lfSchemaNode);
+            } else {
+                writer.value(value);
+            }
+        } else 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 (baseType instanceof BooleanTypeDefinition) {
+            writer.value(Boolean.parseBoolean(value));
+        } else if (baseType instanceof EmptyTypeDefinition) {
+            writeEmptyDataTypeToJson(writer);
+        } else {
+            writer.value(value.equals("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 (type instanceof BooleanTypeDefinition) {
+        } else if (isBoolean(value) && containsType(unionType, BooleanTypeDefinition.class)) {
             writer.value(Boolean.parseBoolean(value));
-        } else if (type instanceof EmptyTypeDefinition) {
-            writer.value("[null]");
         } 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 void writeName(Node<?> node, DataSchemaNode schema, JsonWriter writer) throws IOException {
+        String nameForOutput = node.getNodeType().getLocalName();
+        if (schema.isAugmenting()) {
+            ControllerContext contContext = ControllerContext.getInstance();
+            CharSequence moduleName;
+            moduleName = contContext.toRestconfIdentifier(schema.getQName());
+            if (moduleName != null) {
+                nameForOutput = moduleName.toString();
+            }
+        }
+        writer.name(nameForOutput);
+    }
+
     private static final class NumberForJsonWriter extends Number {
-        
+
         private static final long serialVersionUID = -3147729419814417666L;
         private final String value;
-        
+
         public NumberForJsonWriter(String value) {
             this.value = value;
         }
@@ -184,7 +315,7 @@ class JsonMapper {
         public String toString() {
             return value;
         }
-        
+
     }
 
 }