BUG-509: break up SchemaAwareApplyOperation
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / tree / data / NormalizedNodeContainerModificationStrategy.java
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/NormalizedNodeContainerModificationStrategy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/tree/data/NormalizedNodeContainerModificationStrategy.java
new file mode 100644 (file)
index 0000000..7ab840e
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree.data;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+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.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.primitives.UnsignedLong;
+
+abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
+
+    private final Class<? extends NormalizedNode<?, ?>> nodeClass;
+
+    protected NormalizedNodeContainerModificationStrategy(final Class<? extends NormalizedNode<?, ?>> nodeClass) {
+        this.nodeClass = nodeClass;
+    }
+
+    @Override
+    public void verifyStructure(final NodeModification modification) throws IllegalArgumentException {
+        if (modification.getModificationType() == ModificationType.WRITE) {
+
+        }
+        for (NodeModification childModification : modification.getModifications()) {
+            resolveChildOperation(childModification.getIdentifier()).verifyStructure(childModification);
+        }
+    }
+
+    @Override
+    protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
+            final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
+        // 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(writtenValue instanceof NormalizedNodeContainer);
+
+        NormalizedNodeContainer container = (NormalizedNodeContainer) writtenValue;
+        for (Object child : container.getValue()) {
+            checkArgument(child instanceof NormalizedNode);
+
+            /*
+             * FIXME: fail-fast semantics:
+             *
+             * We can validate the data structure here, aborting the commit
+             * before it ever progresses to being committed.
+             */
+        }
+    }
+
+    @Override
+    protected StoreMetadataNode applyWrite(final NodeModification modification,
+            final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+
+        NormalizedNode<?, ?> newValue = modification.getWrittenValue();
+
+        final UnsignedLong nodeVersion;
+        if (currentMeta.isPresent()) {
+            nodeVersion = StoreUtils.increase(currentMeta.get().getNodeVersion());
+        } else {
+            nodeVersion = subtreeVersion;
+        }
+
+        final StoreMetadataNode newValueMeta = StoreMetadataNode.createRecursively(newValue, nodeVersion);
+        if (!modification.hasAdditionalModifications()) {
+            return newValueMeta;
+        }
+
+        @SuppressWarnings("rawtypes")
+        NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
+        StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.create(nodeVersion, dataBuilder) //
+                .setSubtreeVersion(subtreeVersion);
+
+        return mutateChildren(modification.getModifications(), newValueMeta, builder, nodeVersion);
+    }
+
+    @Override
+    protected StoreMetadataNode applyMerge(final NodeModification modification, final StoreMetadataNode currentMeta,
+            final UnsignedLong subtreeVersion) {
+        // For Node Containers - merge is same as subtree change - we only replace children.
+        return applySubtreeChange(modification, currentMeta, subtreeVersion);
+    }
+
+    @Override
+    public StoreMetadataNode applySubtreeChange(final NodeModification modification,
+            final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+        // Bump subtree version to its new target
+        final UnsignedLong updatedSubtreeVersion = StoreUtils.increase(currentMeta.getSubtreeVersion());
+
+        @SuppressWarnings("rawtypes")
+        NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
+        StoreNodeCompositeBuilder builder = StoreNodeCompositeBuilder.create(dataBuilder, currentMeta)
+                .setIdentifier(modification.getIdentifier())
+                .setSubtreeVersion(updatedSubtreeVersion);
+
+        return mutateChildren(modification.getModifications(), currentMeta, builder, updatedSubtreeVersion);
+    }
+
+    private StoreMetadataNode mutateChildren(final Iterable<NodeModification> modifications, final StoreMetadataNode meta,
+            final StoreNodeCompositeBuilder builder, final UnsignedLong nodeVersion) {
+
+        for (NodeModification mod : modifications) {
+            final PathArgument id = mod.getIdentifier();
+            final Optional<StoreMetadataNode> cm = meta.getChild(id);
+
+            Optional<StoreMetadataNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
+            if (result.isPresent()) {
+                builder.add(result.get());
+            } else {
+                builder.remove(id);
+            }
+        }
+
+        return builder.build();
+    }
+
+    @Override
+    protected void checkSubtreeModificationApplicable(final InstanceIdentifier path,final NodeModification modification,
+            final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
+        checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
+        checkChildPreconditions(path,modification,current);
+
+    }
+
+    private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
+        StoreMetadataNode currentMeta = current.get();
+        for (NodeModification childMod : modification.getModifications()) {
+            PathArgument childId = childMod.getIdentifier();
+            Optional<StoreMetadataNode> childMeta = currentMeta.getChild(childId);
+            InstanceIdentifier childPath = StoreUtils.append(path, childId);
+            resolveChildOperation(childId).checkApplicable(childPath,childMod, childMeta);
+        }
+    }
+
+    @Override
+    protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
+            final Optional<StoreMetadataNode> current) throws DataPreconditionFailedException {
+        if(current.isPresent()) {
+            checkChildPreconditions(path,modification,current);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    protected abstract NormalizedNodeContainerBuilder createBuilder(NormalizedNode<?, ?> original);
+
+    public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Map<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();
+
+            for (ChoiceCaseNode caze : schemaNode.getCases()) {
+                for (DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    SchemaAwareApplyOperation childNode = from(cazeChild);
+                    child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
+                }
+            }
+            childNodes = child.build();
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final 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);
+        }
+    }
+
+    public static class OrderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected OrderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
+            super((Class) LeafSetNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
+            checkArgument(original instanceof OrderedLeafSetNode<?>);
+            return ImmutableOrderedLeafSetNodeBuilder.create((OrderedLeafSetNode<?>) original);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeWithValue) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+    }
+
+    public static class OrderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        protected OrderedMapModificationStrategy(final ListSchemaNode schema) {
+            super(OrderedMapNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
+            checkArgument(original instanceof OrderedMapNode);
+            return ImmutableOrderedMapNodeBuilder.create((OrderedMapNode) original);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeIdentifierWithPredicates) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+        @Override
+        public String toString() {
+            return "OrderedMapModificationStrategy [entry=" + entryStrategy + "]";
+        }
+    }
+
+    public static class UnorderedLeafSetModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        protected UnorderedLeafSetModificationStrategy(final LeafListSchemaNode schema) {
+            super((Class) LeafSetNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new LeafSetEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
+            checkArgument(original instanceof LeafSetNode<?>);
+            return ImmutableLeafSetNodeBuilder.create((LeafSetNode<?>) original);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeWithValue) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+    }
+
+    public static class UnorderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        protected UnorderedMapModificationStrategy(final ListSchemaNode schema) {
+            super(MapNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode<?, ?> original) {
+            checkArgument(original instanceof MapNode);
+            return ImmutableMapNodeBuilder.create((MapNode) original);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeIdentifierWithPredicates) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+        @Override
+        public String toString() {
+            return "UnorderedMapModificationStrategy [entry=" + entryStrategy + "]";
+        }
+    }
+}
\ No newline at end of file