Improve NodeModification 74/107674/1
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 3 Sep 2023 18:42:24 +0000 (20:42 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 3 Sep 2023 18:42:24 +0000 (20:42 +0200)
This is a faux-interface to split access. Seal it an allow it to be
implemented only by ModifiedNode.

Introduce NodeModification.isEmpty(), which short-circuits the access to
children.value(). Convert users to prefer this method over accessing
getChildren() -- i.e. moving the potential allocation further down.

Change-Id: Ibce631b729fd496374580be94902c2b18e55268f
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractNodeContainerModificationStrategy.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractReadyIterator.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AutomaticLifecycleMixin.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeModification.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ListModificationStrategy.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ModifiedNode.java
data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/NodeModification.java

index d1438ed70f90a242c2fe6343b0fcdbde964a2bbf..24743f88f6fa3fd01a11235344e563a91cabc648 100644 (file)
@@ -165,9 +165,8 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
     @Override
     protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue,
             final Optional<? extends TreeNode> currentMeta, final Version version) {
-        final TreeNode newValueMeta = TreeNode.of(newValue, version);
-
-        if (modification.getChildren().isEmpty()) {
+        final var newValueMeta = TreeNode.of(newValue, version);
+        if (modification.isEmpty()) {
             return newValueMeta;
         }
 
@@ -185,12 +184,11 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
          *        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();
+        final var mutable = newValueMeta.mutable();
         mutable.setSubtreeVersion(version);
 
-        @SuppressWarnings("rawtypes")
-        final NormalizedNodeContainerBuilder dataBuilder = support.createBuilder(newValue);
-        final TreeNode result = mutateChildren(mutable, dataBuilder, version, modification.getChildren());
+        final var result = mutateChildren(mutable, support.createBuilder(newValue), version,
+            modification.getChildren());
 
         // We are good to go except one detail: this is a single logical write, but
         // we have a result TreeNode which has been forced to materialized, e.g. it
@@ -260,7 +258,7 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
     @Override
     final void mergeIntoModifiedNode(final ModifiedNode modification, final NormalizedNode value,
             final Version version) {
-        final Collection<? extends NormalizedNode> children = ((DistinctNodeContainer<?, ?>)value).body();
+        final var valueChildren = ((DistinctNodeContainer<?, ?>) value).body();
         switch (modification.getOperation()) {
             case NONE:
                 // Fresh node, just record a MERGE with a value
@@ -269,7 +267,7 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
                 return;
             case TOUCH:
 
-                mergeChildrenIntoModification(modification, children, version);
+                mergeChildrenIntoModification(modification, valueChildren, version);
                 // We record empty merge value, since real children merges are already expanded. This is needed to
                 // satisfy non-null for merge original merge value can not be used since it mean different order of
                 // operation - parent changes are always resolved before children ones, and having node in TOUCH means
@@ -279,7 +277,7 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
             case MERGE:
                 // Merging into an existing node. Merge data children modifications (maybe recursively) and mark
                 // as MERGE, invalidating cached snapshot
-                mergeChildrenIntoModification(modification, children, version);
+                mergeChildrenIntoModification(modification, valueChildren, version);
                 modification.updateOperationType(LogicalOperation.MERGE);
                 return;
             case DELETE:
@@ -287,13 +285,12 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
                 // we are really performing a write. One thing that ruins that are any child modifications. If there
                 // are any, we will perform a read() to get the current state of affairs, turn this into into a WRITE
                 // and then append any child entries.
-                if (!modification.getChildren().isEmpty()) {
+                if (!modification.isEmpty()) {
                     // Version does not matter here as we'll throw it out
-                    final Optional<? extends TreeNode> current = apply(modification, modification.getOriginal(),
-                        Version.initial());
+                    final var current = apply(modification, modification.getOriginal(), Version.initial());
                     if (current.isPresent()) {
                         modification.updateValue(LogicalOperation.WRITE, current.orElseThrow().getData());
-                        mergeChildrenIntoModification(modification, children, version);
+                        mergeChildrenIntoModification(modification, valueChildren, version);
                         return;
                     }
                 }
@@ -303,7 +300,7 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
             case WRITE:
                 // We are augmenting a previous write. We'll just walk value's children, get the corresponding
                 // ModifiedNode and run recursively on it
-                mergeChildrenIntoModification(modification, children, version);
+                mergeChildrenIntoModification(modification, valueChildren, version);
                 modification.updateOperationType(LogicalOperation.WRITE);
                 return;
             default:
@@ -320,13 +317,12 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
          * - do not collect useless garbage.
          * It also means the ModificationType is UNMODIFIED.
          */
-        final Collection<ModifiedNode> children = modification.getChildren();
-        if (!children.isEmpty()) {
-            @SuppressWarnings("rawtypes")
-            final NormalizedNodeContainerBuilder dataBuilder = support.createBuilder(currentMeta.getData());
-            final MutableTreeNode newMeta = currentMeta.mutable();
+        if (!modification.isEmpty()) {
+            final var dataBuilder = support.createBuilder(currentMeta.getData());
+            final var newMeta = currentMeta.mutable();
             newMeta.setSubtreeVersion(version);
-            final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
+            final var children = modification.getChildren();
+            final var ret = mutateChildren(newMeta, dataBuilder, version, children);
 
             /*
              * It is possible that the only modifications under this node were empty merges, which were turned into
@@ -337,7 +333,7 @@ abstract class AbstractNodeContainerModificationStrategy<T extends DataSchemaNod
              *
              * Let's do precisely that, stopping as soon we find a different result.
              */
-            for (final ModifiedNode child : children) {
+            for (var child : children) {
                 if (child.getModificationType() != ModificationType.UNMODIFIED) {
                     modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
                     return ret;
index ab7ce96af458c070b1a9384aa0ab2a24e70e4883..f4f44a4da19a73f1ad0d6a952f150ba2e382ace6 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.yangtools.yang.data.tree.impl;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
-import java.util.Collection;
 import java.util.Iterator;
 import org.opendaylight.yangtools.yang.data.tree.impl.node.Version;
 
@@ -23,7 +22,7 @@ abstract class AbstractReadyIterator {
             final ModificationApplyOperation operation) {
         this.children = requireNonNull(children);
         this.node = requireNonNull(node);
-        this.op = requireNonNull(operation);
+        op = requireNonNull(operation);
     }
 
     static AbstractReadyIterator create(final ModifiedNode root, final ModificationApplyOperation operation) {
@@ -35,19 +34,19 @@ abstract class AbstractReadyIterator {
         // been modified. If a child has children, we need to iterate
         // through it via re-entering this method on the child iterator.
         while (children.hasNext()) {
-            final ModifiedNode child = children.next();
-            final ModificationApplyOperation childOp = op.childByArg(child.getIdentifier());
-            checkState(childOp != null, "Schema for child %s is not present.", child.getIdentifier());
-            final Collection<ModifiedNode> grandChildren = child.getChildren();
+            final var child = children.next();
+            final var childId = child.getIdentifier();
+            final var childOp = op.childByArg(childId);
+            checkState(childOp != null, "Schema for child %s is not present.", childId);
 
-            if (grandChildren.isEmpty()) {
+            if (child.isEmpty()) {
                 // The child is empty, seal it
                 child.seal(childOp, version);
                 if (child.getOperation() == LogicalOperation.NONE) {
                     children.remove();
                 }
             } else {
-                return new NestedReadyIterator(this, child, grandChildren.iterator(), childOp);
+                return new NestedReadyIterator(this, child, child.getChildren().iterator(), childOp);
             }
         }
 
index 177f5cf1f26e2aa6f0fc3088e0c7bee61b4b413d..1eb0c1f4be54ee49dee4d4d664562bd9b8cba76f 100644 (file)
@@ -49,7 +49,7 @@ final class AutomaticLifecycleMixin {
             final Optional<? extends TreeNode> storeMeta, final Version version) {
         final Optional<? extends TreeNode> ret;
         if (modification.getOperation() == LogicalOperation.DELETE) {
-            if (modification.getChildren().isEmpty()) {
+            if (modification.isEmpty()) {
                 return delegate.apply(modification, storeMeta, version);
             }
             // Delete with children, implies it really is an empty write
index f3cd50a2f6d93855ae79824e501498f1adacb9d3..c7833d019973d08b74e4d965c3038d8c24ea5346 100644 (file)
@@ -13,7 +13,6 @@ import static java.util.Objects.requireNonNull;
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
-import java.util.Collection;
 import java.util.Map.Entry;
 import java.util.Optional;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -233,10 +232,9 @@ final class InMemoryDataTreeModification extends AbstractCursorAware implements
     }
 
     private static void applyChildren(final DataTreeModificationCursor cursor, final ModifiedNode node) {
-        final Collection<ModifiedNode> children = node.getChildren();
-        if (!children.isEmpty()) {
+        if (!node.isEmpty()) {
             cursor.enter(node.getIdentifier());
-            for (final ModifiedNode child : children) {
+            for (var child : node.getChildren()) {
                 applyNode(cursor, child);
             }
             cursor.exit();
@@ -272,7 +270,7 @@ final class InMemoryDataTreeModification extends AbstractCursorAware implements
 
     @Override
     public void applyToCursor(final DataTreeModificationCursor cursor) {
-        for (final ModifiedNode child : rootNode.getChildren()) {
+        for (var child : rootNode.getChildren()) {
             applyNode(cursor, child);
         }
     }
index 8770991ba2c303a49cb359f1b03567a24283216b..51b04fbdc8d3ffcbbd58baa9cd4144b102b91775 100644 (file)
@@ -69,8 +69,8 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation<ListSchem
     @Override
     protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue,
             final Optional<? extends TreeNode> currentMeta, final Version version) {
-        final TreeNode newValueMeta = TreeNode.of(newValue, version);
-        if (modification.getChildren().isEmpty()) {
+        final var newValueMeta = TreeNode.of(newValue, version);
+        if (modification.isEmpty()) {
             return newValueMeta;
         }
 
@@ -83,7 +83,7 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation<ListSchem
          * 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.
          */
-        final MutableTreeNode mutable = newValueMeta.mutable();
+        final var mutable = newValueMeta.mutable();
         mutable.setSubtreeVersion(version);
 
         return mutateChildren(mutable, ImmutableUnkeyedListNodeBuilder.create((UnkeyedListNode) newValue), version,
index a041fb1c425ffd53ceffa61a76146d3dc2cfb147..8d8315d0c09431c9efd0f745544cd3244248ee61 100644 (file)
@@ -194,6 +194,11 @@ final class ModifiedNode extends NodeModification implements StoreTreeNode<Modif
         return children.values();
     }
 
+    @Override
+    boolean isEmpty() {
+        return children.isEmpty();
+    }
+
     /**
      * Records a delete for associated node.
      */
index 21a4bb3ec8c3946b242a8e652f22b9f0174f7b08..4b4005db6bde4c2a4f4852b5d010f481dcf1fb69 100644 (file)
@@ -14,11 +14,10 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.tree.impl.node.TreeNode;
 
 /**
- * Internal interface representing a modification action of a particular node.
- * It is used by the validation code to allow for a read-only view of the
- * modification tree as we should never modify that during validation.
+ * Internal interface representing a modification action of a particular node. It is used by the validation code to
+ * allow for a read-only view of the modification tree as we should never modify that during validation.
  */
-abstract class NodeModification implements Identifiable<PathArgument> {
+abstract sealed class NodeModification implements Identifiable<PathArgument> permits ModifiedNode {
     /**
      * Get the type of modification.
      *
@@ -40,4 +39,11 @@ abstract class NodeModification implements Identifiable<PathArgument> {
      * @return Collection of all children nodes.
      */
     abstract Collection<? extends NodeModification> getChildren();
+
+    /**
+     * A shortcut to {@code getChildren().isEmpty()}.
+     *
+     * @return {@code} if {@link #getChildren()} is empty.
+     */
+    abstract boolean isEmpty();
 }