Clean up TreeNode API
[yangtools.git] / data / yang-data-tree-ri / src / main / java / org / opendaylight / yangtools / yang / data / tree / impl / ContainerModificationStrategy.java
index 845e9287906c19464c45796ed6fe21d4f56f6a62..da1b626c3d28907b8a36d6d634fbdb9959132f14 100644 (file)
@@ -9,11 +9,12 @@ package org.opendaylight.yangtools.yang.data.tree.impl;
 
 import static java.util.Objects.requireNonNull;
 
-import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.spi.node.MandatoryLeafEnforcer;
 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
 import org.opendaylight.yangtools.yang.data.tree.impl.node.TreeNode;
 import org.opendaylight.yangtools.yang.data.tree.impl.node.Version;
@@ -25,8 +26,8 @@ import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
  * with mandatory nodes, as it needs to tap into {@link SchemaAwareApplyOperation}'s operations, or subclassed
  * for the purposes of {@link StructuralContainerModificationStrategy}'s automatic lifecycle.
  */
-class ContainerModificationStrategy extends DataNodeContainerModificationStrategy<ContainerLike> {
-    private static final class EnforcingMandatory extends ContainerModificationStrategy {
+sealed class ContainerModificationStrategy extends DataNodeContainerModificationStrategy<ContainerLike> {
+    static final class EnforcingMandatory extends ContainerModificationStrategy {
         private final MandatoryLeafEnforcer enforcer;
 
         EnforcingMandatory(final ContainerSchemaNode schemaNode, final DataTreeConfiguration treeConfig,
@@ -36,50 +37,78 @@ class ContainerModificationStrategy extends DataNodeContainerModificationStrateg
         }
 
         @Override
-        void mandatoryVerifyValueChildren(final NormalizedNode writtenValue) {
+        void mandatoryVerifyValueChildren(final DistinctNodeContainer<?, ?> writtenValue) {
             enforcer.enforceOnData(writtenValue);
         }
 
         @Override
         protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
                 final Version version) {
-            final TreeNode ret = super.applyMerge(modification, currentMeta, version);
-            enforcer.enforceOnTreeNode(ret);
-            return ret;
+            return enforce(super.applyMerge(modification, currentMeta, version));
         }
 
         @Override
         protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue,
-                final Optional<? extends TreeNode> currentMeta, final Version version) {
-            final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version);
-            enforcer.enforceOnTreeNode(ret);
-            return ret;
+                final TreeNode currentMeta, final Version version) {
+            return enforce(super.applyWrite(modification, newValue, currentMeta, version));
         }
 
         @Override
         protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta,
                 final Version version) {
-            final TreeNode ret = super.applyTouch(modification, currentMeta, version);
-            enforcer.enforceOnTreeNode(ret);
-            return ret;
+            return enforce(super.applyTouch(modification, currentMeta, version));
+        }
+
+        private @NonNull TreeNode enforce(final TreeNode treeNode) {
+            enforcer.enforceOnData(treeNode.data());
+            return treeNode;
+        }
+    }
+
+    /**
+     * Structural containers are special in that they appear when implied by child nodes and disappear whenever they are
+     * empty. We could implement this as a subclass of {@link SchemaAwareApplyOperation}, but the automatic semantic
+     * is quite different from all the other strategies. We create a {@link ContainerModificationStrategy} to tap into
+     * that logic, but wrap it so we only call out into it. We do not use {@link PresenceContainerModificationStrategy}
+     * because it enforces presence of mandatory leaves, which is not something we want here, as structural containers
+     * are not root anchors for that validation.
+     */
+    static final class Structural extends ContainerModificationStrategy {
+        private final ContainerNode emptyNode;
+
+        Structural(final ContainerLike schema, final DataTreeConfiguration treeConfig) {
+            super(schema, treeConfig);
+            emptyNode = BUILDER_FACTORY.newContainerBuilder()
+                .withNodeIdentifier(NodeIdentifier.create(schema.getQName()))
+                .build();
+        }
+
+        @Override
+        TreeNode apply(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+            return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, currentMeta,
+                version);
+        }
+
+        @Override
+        TreeNode defaultTreeNode() {
+            return defaultTreeNode(emptyNode);
         }
     }
 
     private static final NormalizedNodeContainerSupport<NodeIdentifier, ContainerNode> SUPPORT =
-            new NormalizedNodeContainerSupport<>(ContainerNode.class, ImmutableContainerNodeBuilder::create,
-                    ImmutableContainerNodeBuilder::create);
+            new NormalizedNodeContainerSupport<>(ContainerNode.class, BUILDER_FACTORY::newContainerBuilder,
+                BUILDER_FACTORY::newContainerBuilder);
 
     ContainerModificationStrategy(final ContainerLike schemaNode, final DataTreeConfiguration treeConfig) {
         super(SUPPORT, schemaNode, treeConfig);
     }
 
-    static ModificationApplyOperation of(final ContainerSchemaNode schema, final DataTreeConfiguration treeConfig) {
+    static ContainerModificationStrategy of(final ContainerSchemaNode schema, final DataTreeConfiguration treeConfig) {
         if (schema.isPresenceContainer()) {
-            final var enforcer = MandatoryLeafEnforcer.forContainer(schema, treeConfig);
-            return enforcer.isPresent() ? new EnforcingMandatory(schema, treeConfig, enforcer.orElseThrow())
-                    : new ContainerModificationStrategy(schema, treeConfig);
+            final var enforcer = enforcerFor(schema, treeConfig);
+            return enforcer != null ? new EnforcingMandatory(schema, treeConfig, enforcer)
+                : new ContainerModificationStrategy(schema, treeConfig);
         }
-
-        return new StructuralContainerModificationStrategy(schema, treeConfig);
+        return new Structural(schema, treeConfig);
     }
 }