Fixed bug in Data store where multiple readers could overwrite
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / yangtools / yang / util / YangDataOperations.xtend
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/yangtools/yang/util/YangDataOperations.xtend b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/yangtools/yang/util/YangDataOperations.xtend
new file mode 100644 (file)
index 0000000..f69e00a
--- /dev/null
@@ -0,0 +1,116 @@
+package org.opendaylight.yangtools.yang.util
+
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
+import static com.google.common.base.Preconditions.*;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import java.util.ArrayList
+
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
+import org.opendaylight.yangtools.yang.data.api.Node
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
+import java.util.List
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
+import java.util.Collections
+import static extension org.opendaylight.yangtools.yang.util.YangDataUtils.*;
+import java.util.HashSet
+import org.opendaylight.yangtools.yang.common.QName
+
+class YangDataOperations {
+
+    static def CompositeNode merge(DataSchemaNode schema, CompositeNode stored, CompositeNode modified, boolean config) {
+        checkConfigurational(schema, config);
+        if (stored === null) {
+            return modified;
+        }
+
+        if (schema instanceof ListSchemaNode || schema instanceof ContainerSchemaNode) {
+            return mergeContainer(schema as DataNodeContainer, stored, modified, config);
+        }
+        throw new IllegalArgumentException("Supplied node is not data node container.");
+    }
+
+    private def static checkConfigurational(DataSchemaNode node, boolean config) {
+        if (config) {
+            checkArgument(node.configuration, "Supplied composite node is not configurational.");
+        }
+    }
+
+    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(LeafSchemaNode node, List<Node<?>> original,
+        List<Node<?>> modified, boolean configurational) {
+        checkArgument(original.size === 1);
+        checkArgument(modified.size === 1);
+        checkConfigurational(node, configurational);
+        return modified;
+    }
+
+    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(LeafListSchemaNode node,
+        List<Node<?>> original, List<Node<?>> modified, boolean configurational) {
+        return modified;
+    }
+
+    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(ContainerSchemaNode node,
+        List<Node<?>> original, List<Node<?>> modified, boolean configurational) {
+        checkArgument(original.size === 1);
+        checkArgument(modified.size === 1);
+        return Collections.singletonList(
+            merge(node, original.get(0) as CompositeNode, modified.get(0) as CompositeNode, configurational));
+    }
+
+    private static dispatch def Iterable<? extends Node<?>> mergeMultiple(ListSchemaNode node, List<Node<?>> original,
+        List<Node<?>> modified, boolean configurational) {
+        checkConfigurational(node,configurational);
+        if(node.keyDefinition === null || node.keyDefinition.empty) {
+            return modified;
+        } 
+        val originalMap = (original as List).toIndexMap(node.keyDefinition);
+        val modifiedMap = (modified as List).toIndexMap(node.keyDefinition);
+        
+        val List<Node<?>> mergedNodes = new ArrayList(original.size + modified.size);
+        for(entry : modifiedMap.entrySet) {
+            val originalEntry = originalMap.get(entry.key);
+            if(originalEntry != null) {
+                originalMap.remove(entry.key);
+                mergedNodes.add(merge(node,originalEntry,entry.value,configurational));
+                
+            }
+        }
+        mergedNodes.addAll(originalMap.values);
+        return mergedNodes;
+    }
+
+    static private def CompositeNode mergeContainer(DataNodeContainer schema, CompositeNode stored,
+        CompositeNode modified, boolean config) {
+        if (stored == null) {
+            return modified;
+        }
+        checkNotNull(stored)
+        checkNotNull(modified)
+        checkArgument(stored.nodeType == modified.nodeType);
+
+        val mergedChildNodes = new ArrayList<Node<?>>(stored.children.size + modified.children.size);
+        
+        val toProcess = new HashSet<QName>(stored.keySet);
+        toProcess.addAll(modified.keySet);
+        
+        for (qname : toProcess) {
+            val schemaChild = schema.getDataChildByName(qname);
+            val storedChildren = stored.get(qname);
+            val modifiedChildren = modified.get(qname);
+
+            if (modifiedChildren !== null && !modifiedChildren.empty) {
+                if (storedChildren === null || storedChildren.empty || schemaChild === null) {
+                    mergedChildNodes.addAll(modifiedChildren);
+                } else {
+                    mergedChildNodes.addAll(mergeMultiple(schemaChild, storedChildren, modifiedChildren, config));
+                }
+            } else if (storedChildren !== null && !storedChildren.empty) {
+                mergedChildNodes.addAll(storedChildren);
+            }
+        }
+        return new CompositeNodeTOImpl(stored.nodeType, null, mergedChildNodes);
+    }
+
+}