BUG-2876: determine ModificationType while applying changes
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / NormalizedNodeContainerModificationStrategy.java
index 6a7d67e3ad15e41b668808663cd12b10d50a0a0c..e5c4d09033b966d5f8e11a149461a4c6aa6cd768 100644 (file)
@@ -8,14 +8,12 @@
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
 import static com.google.common.base.Preconditions.checkArgument;
-
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
 import java.util.Map;
-
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -37,15 +35,11 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMa
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedLeafSetNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-
 abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
 
     private final Class<? extends NormalizedNode<?, ?>> nodeClass;
@@ -56,26 +50,15 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
 
     @Override
     public void verifyStructure(final ModifiedNode modification) throws IllegalArgumentException {
-        if (modification.getType() == ModificationType.WRITE) {
-
-        }
         for (ModifiedNode childModification : modification.getChildren()) {
             resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
         }
     }
 
-    @Override
-    protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification,
-            final Optional<TreeNode> current) throws DataValidationFailedException {
-        // FIXME: Implement proper write check for replacement of node container
-        //        prerequisite is to have transaction chain available for clients
-        //        otherwise this will break chained writes to same node.
-    }
-
     @SuppressWarnings("rawtypes")
     @Override
     protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
-        checkArgument(nodeClass.isInstance(writtenValue), "Node should must be of type %s", nodeClass);
+        checkArgument(nodeClass.isInstance(writtenValue), "Node %s is not of type %s", writtenValue, nodeClass);
         checkArgument(writtenValue instanceof NormalizedNodeContainer);
 
         NormalizedNodeContainer container = (NormalizedNodeContainer) writtenValue;
@@ -97,7 +80,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
         final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
 
-        if (Iterables.isEmpty(modification.getChildren())) {
+        if (modification.getChildren().isEmpty()) {
             return newValueMeta;
         }
 
@@ -111,6 +94,11 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
          * As it turns out, once we materialize the written data, we can share the
          * code path with the subtree change. So let's create an unsealed TreeNode
          * and run the common parts on it -- which end with the node being sealed.
+         *
+         * FIXME: this code needs to be moved out from the prepare() path and into
+         *        the read() and seal() paths. Merging of writes needs to be charged
+         *        to the code which originated this, not to the code which is
+         *        attempting to make it visible.
          */
         final MutableTreeNode mutable = newValueMeta.mutable();
         mutable.setSubtreeVersion(version);
@@ -121,12 +109,22 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         return mutateChildren(mutable, dataBuilder, version, modification.getChildren());
     }
 
+    /**
+     * Applies write/remove diff operation for each modification child in modification subtree.
+     * Operation also sets the Data tree references for each Tree Node (Index Node) in meta (MutableTreeNode) structure.
+     *
+     * @param meta MutableTreeNode (IndexTreeNode)
+     * @param data DataBuilder
+     * @param nodeVersion Version of TreeNode
+     * @param modifications modification operations to apply
+     * @return Sealed immutable copy of TreeNode structure with all Data Node references set.
+     */
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data,
             final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
 
         for (ModifiedNode mod : modifications) {
-            final PathArgument id = mod.getIdentifier();
+            final YangInstanceIdentifier.PathArgument id = mod.getIdentifier();
             final Optional<TreeNode> cm = meta.getChild(id);
 
             Optional<TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
@@ -148,6 +146,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
     protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
             final Version version) {
         // For Node Containers - merge is same as subtree change - we only replace children.
+        modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
         return applySubtreeChange(modification, currentMeta, version);
     }
 
@@ -157,10 +156,20 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         final MutableTreeNode newMeta = currentMeta.mutable();
         newMeta.setSubtreeVersion(version);
 
+        /*
+         * The user has issued an empty merge operation. In this case we do not perform
+         * a data tree mutation, do not pass GO, and do not collect useless garbage.
+         */
+        final Collection<ModifiedNode> children = modification.getChildren();
+        if (children.isEmpty()) {
+            newMeta.setData(currentMeta.getData());
+            return newMeta.seal();
+        }
+
         @SuppressWarnings("rawtypes")
         NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
 
-        return mutateChildren(newMeta, dataBuilder, version, modification.getChildren());
+        return mutateChildren(newMeta, dataBuilder, version, children);
     }
 
     @Override
@@ -177,7 +186,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
     private void checkChildPreconditions(final YangInstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
         final TreeNode currentMeta = current.get();
         for (NodeModification childMod : modification.getChildren()) {
-            final PathArgument childId = childMod.getIdentifier();
+            final YangInstanceIdentifier.PathArgument childId = childMod.getIdentifier();
             final Optional<TreeNode> childMeta = currentMeta.getChild(childId);
 
             YangInstanceIdentifier childPath = path.node(childId);
@@ -198,31 +207,31 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
 
     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
 
-        private final Map<PathArgument, ModificationApplyOperation> childNodes;
+        private final Map<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> childNodes;
 
-        public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
-            super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
-            ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
+        public ChoiceModificationStrategy(final ChoiceSchemaNode schemaNode) {
+            super(ChoiceNode.class);
+            ImmutableMap.Builder<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
 
             for (ChoiceCaseNode caze : schemaNode.getCases()) {
                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
                     SchemaAwareApplyOperation childNode = SchemaAwareApplyOperation.from(cazeChild);
-                    child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
+                    child.put(new YangInstanceIdentifier.NodeIdentifier(cazeChild.getQName()), childNode);
                 }
             }
             childNodes = child.build();
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument child) {
             return Optional.fromNullable(childNodes.get(child));
         }
 
         @Override
         @SuppressWarnings("rawtypes")
         protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
-            checkArgument(original instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
-            return ImmutableChoiceNodeBuilder.create((org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode) original);
+            checkArgument(original instanceof ChoiceNode);
+            return ImmutableChoiceNodeBuilder.create((ChoiceNode) original);
         }
     }
 
@@ -236,6 +245,11 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
             entryStrategy = Optional.<ModificationApplyOperation> of(new ValueNodeModificationStrategy.LeafSetEntryModificationStrategy(schema));
         }
 
+        @Override
+        boolean isOrdered() {
+            return true;
+        }
+
         @SuppressWarnings("rawtypes")
         @Override
         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
@@ -244,8 +258,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeWithValue) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeWithValue) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -261,6 +275,11 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
             entryStrategy = Optional.<ModificationApplyOperation> of(new DataNodeContainerModificationStrategy.ListEntryModificationStrategy(schema));
         }
 
+        @Override
+        boolean isOrdered() {
+            return true;
+        }
+
         @SuppressWarnings("rawtypes")
         @Override
         protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
@@ -269,8 +288,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeIdentifierWithPredicates) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -300,8 +319,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeWithValue) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeWithValue) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -325,8 +344,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeIdentifierWithPredicates) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
                 return entryStrategy;
             }
             return Optional.absent();