From c73686880bb5000f1995bc527efa28d80520bc1c Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 4 Sep 2023 20:50:25 +0200 Subject: [PATCH] Refactor ModificationApplyOperation Use currentMeta as a @Nullable TreeNode, replacing checks as appropriate. JIRA: YANGTOOLS-1538 Change-Id: I7dfeea43515fe0dd3099ed2dd48086fd8f297ea8 Signed-off-by: Robert Varga --- .../data/tree/impl/AbstractDataTreeTip.java | 11 ++- ...ractNodeContainerModificationStrategy.java | 48 ++++++------- .../data/tree/impl/AbstractValidation.java | 20 +++--- .../tree/impl/AutomaticLifecycleMixin.java | 43 +++++------ .../tree/impl/ChoiceModificationStrategy.java | 12 ++-- .../impl/ContainerModificationStrategy.java | 14 ++-- .../tree/impl/InMemoryDataTreeCandidate.java | 11 +-- .../impl/InMemoryDataTreeModification.java | 13 ++-- .../tree/impl/InMemoryDataTreeSnapshot.java | 2 +- .../tree/impl/ListModificationStrategy.java | 17 +++-- .../impl/MapEntryModificationStrategy.java | 9 ++- .../tree/impl/MapModificationStrategy.java | 4 +- .../tree/impl/ModificationApplyOperation.java | 29 ++++---- .../yang/data/tree/impl/ModifiedNode.java | 16 ++--- .../tree/impl/OperationWithModification.java | 7 +- .../tree/impl/SchemaAwareApplyOperation.java | 71 ++++++++++--------- .../impl/ValueNodeModificationStrategy.java | 5 +- 17 files changed, 164 insertions(+), 168 deletions(-) diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractDataTreeTip.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractDataTreeTip.java index 494b5ba483..5d54527db4 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractDataTreeTip.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractDataTreeTip.java @@ -7,7 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.tree.impl; -import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateTip; @@ -29,8 +28,8 @@ abstract class AbstractDataTreeTip implements DataTreeTip { @Override public final void validate(final DataTreeModification modification) throws DataValidationFailedException { final var m = accessMod(modification, "validate"); - m.getStrategy().checkApplicable(new ModificationPath(getRootPath()), m.getRootModification(), - Optional.of(getTipRoot()), m.getVersion()); + m.getStrategy().checkApplicable(new ModificationPath(getRootPath()), m.getRootModification(), getTipRoot(), + m.getVersion()); } @Override @@ -43,9 +42,9 @@ abstract class AbstractDataTreeTip implements DataTreeTip { return new NoopDataTreeCandidate(YangInstanceIdentifier.of(), root, currentRoot); } - final var newRoot = m.getStrategy().apply(m.getRootModification(), Optional.of(currentRoot), m.getVersion()) - .orElseThrow(() -> new IllegalStateException("Apply strategy failed to produce root node for modification " - + modification)); + final var newRoot = m.getStrategy().apply(m.getRootModification(), currentRoot, m.getVersion()) + .orElseThrow(() -> new IllegalStateException( + "Apply strategy failed to produce root node for modification " + modification)); return new InMemoryDataTreeCandidate(YangInstanceIdentifier.of(), root, currentRoot, newRoot); } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractNodeContainerModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractNodeContainerModificationStrategy.java index 30240ce098..b47df761f2 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractNodeContainerModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractNodeContainerModificationStrategy.java @@ -11,12 +11,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.base.MoreObjects.ToStringHelper; -import com.google.common.base.Verify; +import com.google.common.base.VerifyException; import java.util.Collection; import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; @@ -166,7 +165,7 @@ abstract sealed class AbstractNodeContainerModificationStrategy currentMeta, final Version version) { + final TreeNode currentMeta, final Version version) { final var newValueMeta = TreeNode.of(newValue, version); if (modification.isEmpty()) { return newValueMeta; @@ -211,14 +210,11 @@ abstract sealed class AbstractNodeContainerModificationStrategy modifications) { - - for (final ModifiedNode mod : modifications) { - final PathArgument id = mod.getIdentifier(); - final Optional cm = meta.findChildByArg(id); - - final Optional result = resolveChildOperation(id).apply(mod, cm, nodeVersion); + for (var mod : modifications) { + final var id = mod.getIdentifier(); + final var result = resolveChildOperation(id).apply(mod, meta.childByArg(id), nodeVersion); if (result.isPresent()) { - final TreeNode tn = result.orElseThrow(); + final var tn = result.orElseThrow(); meta.putChild(tn); data.addChild(tn.getData()); } else { @@ -238,10 +234,12 @@ abstract sealed class AbstractNodeContainerModificationStrategy containerValue)) { + throw new VerifyException("Attempted to merge non-container " + value); + } - Verify.verify(value instanceof DistinctNodeContainer, "Attempted to merge non-container %s", value); - for (var c : ((DistinctNodeContainer) value).body()) { + for (var c : containerValue.body()) { final var id = c.name(); modification.modifyChild(id, resolveChildOperation(id), version); } @@ -289,7 +287,7 @@ abstract sealed class AbstractNodeContainerModificationStrategy current, final Version version) throws DataValidationFailedException { + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { final TreeNode currentNode; - if (current.isEmpty()) { + if (currentMeta == null) { currentNode = defaultTreeNode(); if (currentNode == null) { if (modification.original() == null) { @@ -366,7 +364,7 @@ abstract sealed class AbstractNodeContainerModificationStrategy current, final Version version) throws DataValidationFailedException { - if (current.isPresent()) { - checkChildPreconditions(path, modification, current.orElseThrow(), version); + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { + if (currentMeta != null) { + checkChildPreconditions(path, modification, currentMeta, version); } } @@ -400,13 +398,13 @@ abstract sealed class AbstractNodeContainerModificationStrategy childMeta = current.findChildByArg(childId); + final @NonNull TreeNode currentMeta, final Version version) throws DataValidationFailedException { + for (var childMod : modification.getChildren()) { + final var childId = childMod.getIdentifier(); + final var childMeta = currentMeta.childByArg(childId); path.push(childId); try { diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractValidation.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractValidation.java index 38907df628..e69b959a7d 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractValidation.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AbstractValidation.java @@ -61,31 +61,31 @@ abstract sealed class AbstractValidation extends ModificationApplyOperation } @Override - final Optional apply(final ModifiedNode modification, - final Optional storeMeta, final Version version) { - var validated = modification.validatedNode(this, storeMeta); + final Optional apply(final ModifiedNode modification, final TreeNode currentMeta, + final Version version) { + var validated = modification.validatedNode(this, currentMeta); if (validated != null) { return validated.toOptional(); } // This might also mean the delegate is maintaining validation if (delegate instanceof AbstractValidation) { - validated = modification.validatedNode(delegate, storeMeta); + validated = modification.validatedNode(delegate, currentMeta); if (validated != null) { return validated.toOptional(); } } // Deal with the result moving on us - final var ret = delegate.apply(modification, storeMeta, version); + final var ret = delegate.apply(modification, currentMeta, version); ret.ifPresent(meta -> enforceOnData(meta.getData())); return ret; } @Override final void checkApplicable(final ModificationPath path, final NodeModification modification, - final Optional current, final Version version) throws DataValidationFailedException { - delegate.checkApplicable(path, modification, current, version); + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { + delegate.checkApplicable(path, modification, currentMeta, version); if (!(modification instanceof ModifiedNode modified)) { // FIXME: 7.0.0: turn this into a verify? LOG.debug("Could not validate {}, does not implement expected class {}", modification, ModifiedNode.class); @@ -93,13 +93,13 @@ abstract sealed class AbstractValidation extends ModificationApplyOperation } if (delegate instanceof AbstractValidation) { - checkApplicable(path, verifyNotNull(modified.validatedNode(delegate, current)).toOptional()); + checkApplicable(path, verifyNotNull(modified.validatedNode(delegate, currentMeta)).toOptional()); return; } // We need to actually perform the operation to deal with merge in a sane manner. We know the modification // is immutable, so the result of validation will probably not change. Note we should not be checking number - final Optional applied = delegate.apply(modified, current, version); + final var applied = delegate.apply(modified, currentMeta, version); checkApplicable(path, applied); // Everything passed. We now have a snapshot of the result node, it would be too bad if we just threw it out. @@ -108,7 +108,7 @@ abstract sealed class AbstractValidation extends ModificationApplyOperation // - the effective model context (therefore, the fact this object is associated with the modification) // // So let's stash the result. We will pick it up during apply operation. - modified.setValidatedNode(this, current, applied); + modified.setValidatedNode(this, currentMeta, applied); } private void checkApplicable(final ModificationPath path, final Optional applied) diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AutomaticLifecycleMixin.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AutomaticLifecycleMixin.java index 89467acdbb..fa16bbd8e9 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AutomaticLifecycleMixin.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/AutomaticLifecycleMixin.java @@ -10,6 +10,8 @@ package org.opendaylight.yangtools.yang.data.tree.impl; import static com.google.common.base.Preconditions.checkState; import java.util.Optional; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; import org.opendaylight.yangtools.yang.data.tree.api.ModificationType; @@ -22,52 +24,51 @@ import org.opendaylight.yangtools.yang.data.tree.impl.node.Version; */ final class AutomaticLifecycleMixin { /** - * This is a capture of {@link ModificationApplyOperation#apply(ModifiedNode, Optional, Version)}. + * This is a capture of {@link ModificationApplyOperation#apply(ModifiedNode, TreeNode, Version)}. */ @FunctionalInterface interface Apply { - Optional apply(ModifiedNode modification, Optional storeMeta, - Version version); + Optional apply(ModifiedNode modification, @Nullable TreeNode currentMeta, Version version); } /** * This is a capture of - * {@link SchemaAwareApplyOperation#applyWrite(ModifiedNode, NormalizedNode, Optional, Version)}. + * {@link SchemaAwareApplyOperation#applyWrite(ModifiedNode, NormalizedNode, TreeNode, Version)}. */ @FunctionalInterface interface ApplyWrite { - TreeNode applyWrite(ModifiedNode modification, NormalizedNode newValue, - Optional storeMeta, Version version); + TreeNode applyWrite(ModifiedNode modification, NormalizedNode newValue, @Nullable TreeNode currentMeta, + Version version); } private AutomaticLifecycleMixin() { - + // Hidden on purpose } static Optional apply(final Apply delegate, final ApplyWrite writeDelegate, - final NormalizedNode emptyNode, final ModifiedNode modification, - final Optional storeMeta, final Version version) { + final NormalizedNode emptyNode, final ModifiedNode modification, final @Nullable TreeNode currentMeta, + final Version version) { final Optional ret; if (modification.getOperation() == LogicalOperation.DELETE) { if (modification.isEmpty()) { - return delegate.apply(modification, storeMeta, version); + return delegate.apply(modification, currentMeta, version); } // Delete with children, implies it really is an empty write - ret = Optional.of(writeDelegate.applyWrite(modification, emptyNode, storeMeta, version)); - } else if (modification.getOperation() == LogicalOperation.TOUCH && storeMeta.isEmpty()) { - ret = applyTouch(delegate, emptyNode, modification, storeMeta, version); + ret = Optional.of(writeDelegate.applyWrite(modification, emptyNode, currentMeta, version)); + } else if (modification.getOperation() == LogicalOperation.TOUCH && currentMeta == null) { + ret = applyTouch(delegate, emptyNode, modification, null, version); } else { // No special handling required here, run normal apply operation - ret = delegate.apply(modification, storeMeta, version); + ret = delegate.apply(modification, currentMeta, version); } - return ret.isPresent() ? disappearResult(modification, ret.orElseThrow(), storeMeta) : ret; + return ret.isPresent() ? disappearResult(modification, ret.orElseThrow(), currentMeta) : ret; } private static Optional applyTouch(final Apply delegate, final NormalizedNode emptyNode, - final ModifiedNode modification, final Optional storeMeta, final Version version) { + final ModifiedNode modification, final @Nullable TreeNode currentMeta, final Version version) { // Container is not present, let's take care of the 'magically appear' part of our job - final Optional ret = delegate.apply(modification, fakeMeta(emptyNode, version), version); + final var ret = delegate.apply(modification, fakeMeta(emptyNode, version), version); // If the delegate indicated SUBTREE_MODIFIED, account for the fake and report APPEARED if (modification.getModificationType() == ModificationType.SUBTREE_MODIFIED) { @@ -77,7 +78,7 @@ final class AutomaticLifecycleMixin { } private static Optional disappearResult(final ModifiedNode modification, final TreeNode result, - final Optional storeMeta) { + final @Nullable TreeNode currentMeta) { // Check if the result is in fact empty before pulling any tricks if (!isEmpty(result)) { return Optional.of(result); @@ -85,7 +86,7 @@ final class AutomaticLifecycleMixin { // We are pulling the 'disappear' trick, but what we report can be three different things final ModificationType finalType; - if (storeMeta.isEmpty()) { + if (currentMeta == null) { // ... there was nothing in the datastore, no change finalType = ModificationType.UNMODIFIED; } else if (modification.getModificationType() == ModificationType.WRITE) { @@ -99,8 +100,8 @@ final class AutomaticLifecycleMixin { return Optional.empty(); } - private static Optional fakeMeta(final NormalizedNode emptyNode, final Version version) { - return Optional.of(TreeNode.of(emptyNode, version)); + private static @NonNull TreeNode fakeMeta(final NormalizedNode emptyNode, final Version version) { + return TreeNode.of(emptyNode, version); } private static boolean isEmpty(final TreeNode treeNode) { diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ChoiceModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ChoiceModificationStrategy.java index 8d842c66e0..9420a65270 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ChoiceModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ChoiceModificationStrategy.java @@ -76,9 +76,9 @@ final class ChoiceModificationStrategy extends Visible { } @Override - Optional apply(final ModifiedNode modification, final Optional storeMeta, + Optional apply(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta, + return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, currentMeta, version); } @@ -127,22 +127,22 @@ final class ChoiceModificationStrategy extends Visible { @Override protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyMerge(modification, currentMeta, version); + final var ret = super.applyMerge(modification, currentMeta, version); enforceCases(ret); return ret; } @Override protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue, - final Optional currentMeta, final Version version) { - final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version); + final TreeNode currentMeta, final Version version) { + final var ret = super.applyWrite(modification, newValue, currentMeta, version); enforceCases(ret); return ret; } @Override protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyTouch(modification, currentMeta, version); + final var ret = super.applyTouch(modification, currentMeta, version); enforceCases(ret); return ret; } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ContainerModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ContainerModificationStrategy.java index 906b9eed5b..b6bc51b70b 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ContainerModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ContainerModificationStrategy.java @@ -45,15 +45,15 @@ sealed class ContainerModificationStrategy extends DataNodeContainerModification @Override protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyMerge(modification, currentMeta, version); + final var ret = super.applyMerge(modification, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } @Override protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue, - final Optional currentMeta, final Version version) { - final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version); + final TreeNode currentMeta, final Version version) { + final var ret = super.applyWrite(modification, newValue, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } @@ -61,7 +61,7 @@ sealed class ContainerModificationStrategy extends DataNodeContainerModification @Override protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyTouch(modification, currentMeta, version); + final var ret = super.applyTouch(modification, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } @@ -84,9 +84,9 @@ sealed class ContainerModificationStrategy extends DataNodeContainerModification } @Override - Optional apply(final ModifiedNode modification, - final Optional storeMeta, final Version version) { - return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta, + Optional apply(final ModifiedNode modification, final TreeNode currentMeta, + final Version version) { + return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, currentMeta, version); } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeCandidate.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeCandidate.java index c0ff4518c8..02961a8ad8 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeCandidate.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeCandidate.java @@ -7,7 +7,10 @@ */ package org.opendaylight.yangtools.yang.data.tree.impl; +import static java.util.Objects.requireNonNull; + import com.google.common.base.MoreObjects; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode; @@ -25,12 +28,12 @@ final class InMemoryDataTreeCandidate extends AbstractDataTreeCandidate { } } - private final RootNode root; + private final @NonNull RootNode root; InMemoryDataTreeCandidate(final YangInstanceIdentifier rootPath, final ModifiedNode modificationRoot, final TreeNode beforeRoot, final TreeNode afterRoot) { super(rootPath); - root = new RootNode(modificationRoot, beforeRoot, afterRoot); + root = new RootNode(modificationRoot, requireNonNull(beforeRoot), requireNonNull(afterRoot)); } @Override @@ -49,7 +52,7 @@ final class InMemoryDataTreeCandidate extends AbstractDataTreeCandidate { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("rootPath", getRootPath()) - .add("rootNode", getRootNode()).toString(); + return MoreObjects.toStringHelper(this).add("rootPath", getRootPath()).add("rootNode", getRootNode()) + .toString(); } } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeModification.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeModification.java index df3da508a0..56f426fd4a 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeModification.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeModification.java @@ -143,7 +143,7 @@ final class InMemoryDataTreeModification extends AbstractCursorAware implements } try { - return resolveModificationStrategy(path).apply(modification, modification.getOriginal(), version); + return resolveModificationStrategy(path).apply(modification, modification.original(), version); } catch (Exception e) { LOG.error("Could not create snapshot for {}:{}", path, modification, e); throw e; @@ -213,12 +213,11 @@ final class InMemoryDataTreeModification extends AbstractCursorAware implements * have same version each time this method is called. */ final var originalSnapshotRoot = snapshot.getRootNode(); - final var tempRoot = getStrategy().apply(rootNode, Optional.of(originalSnapshotRoot), version); - checkState(tempRoot.isPresent(), "Data tree root is not present, possibly removed by previous modification"); - - final var tempTree = new InMemoryDataTreeSnapshot(snapshot.getEffectiveModelContext(), tempRoot.orElseThrow(), - strategyTree); - return tempTree.newModification(); + return new InMemoryDataTreeSnapshot(snapshot.getEffectiveModelContext(), + getStrategy().apply(rootNode, originalSnapshotRoot, version) + .orElseThrow(() -> new IllegalStateException( + "Data tree root is not present, possibly removed by previous modification")), strategyTree) + .newModification(); } Version getVersion() { diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeSnapshot.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeSnapshot.java index 1d3d643c80..f058d6317d 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeSnapshot.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/InMemoryDataTreeSnapshot.java @@ -35,7 +35,7 @@ final class InMemoryDataTreeSnapshot extends AbstractCursorAware implements Curs this.applyOper = requireNonNull(applyOper); } - TreeNode getRootNode() { + @NonNull TreeNode getRootNode() { return rootNode; } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ListModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ListModificationStrategy.java index 51b04fbdc8..8b3563e116 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ListModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ListModificationStrategy.java @@ -44,9 +44,9 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation apply(final ModifiedNode modification, final Optional storeMeta, + Optional apply(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta, + return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, currentMeta, version); } @@ -68,7 +68,7 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation currentMeta, final Version version) { + final TreeNode currentMeta, final Version version) { final var newValueMeta = TreeNode.of(newValue, version); if (modification.isEmpty()) { return newValueMeta; @@ -103,12 +103,11 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation modifications) { + for (var mod : modifications) { + final var id = mod.getIdentifier(); + final var cm = meta.childByArg(id); - for (final ModifiedNode mod : modifications) { - final PathArgument id = mod.getIdentifier(); - final Optional cm = meta.findChildByArg(id); - - final Optional result = resolveChildOperation(id).apply(mod, cm, nodeVersion); + final var result = resolveChildOperation(id).apply(mod, cm, nodeVersion); if (result.isPresent()) { final TreeNode tn = result.orElseThrow(); meta.putChild(tn); @@ -140,7 +139,7 @@ final class ListModificationStrategy extends SchemaAwareApplyOperation current, final Version version) throws IncorrectDataStructureException { + final TreeNode currentMeta, final Version version) throws IncorrectDataStructureException { throw new IncorrectDataStructureException(path.toInstanceIdentifier(), "Subtree modification is not allowed."); } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapEntryModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapEntryModificationStrategy.java index 0a35aeec91..4e0fa476af 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapEntryModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapEntryModificationStrategy.java @@ -9,7 +9,6 @@ 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.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer; @@ -39,15 +38,15 @@ sealed class MapEntryModificationStrategy extends DataNodeContainerModificationS @Override protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyMerge(modification, currentMeta, version); + final var ret = super.applyMerge(modification, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } @Override protected TreeNode applyWrite(final ModifiedNode modification, final NormalizedNode newValue, - final Optional currentMeta, final Version version) { - final TreeNode ret = super.applyWrite(modification, newValue, currentMeta, version); + final TreeNode currentMeta, final Version version) { + final var ret = super.applyWrite(modification, newValue, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } @@ -55,7 +54,7 @@ sealed class MapEntryModificationStrategy extends DataNodeContainerModificationS @Override protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - final TreeNode ret = super.applyTouch(modification, currentMeta, version); + final var ret = super.applyTouch(modification, currentMeta, version); enforcer.enforceOnTreeNode(ret); return ret; } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapModificationStrategy.java index bb02d258a7..c894bd2814 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/MapModificationStrategy.java @@ -61,9 +61,9 @@ final class MapModificationStrategy extends Invisible { } @Override - Optional apply(final ModifiedNode modification, final Optional storeMeta, + Optional apply(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { - return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, storeMeta, + return AutomaticLifecycleMixin.apply(super::apply, this::applyWrite, emptyNode, modification, currentMeta, version); } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ModificationApplyOperation.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ModificationApplyOperation.java index 1688d66f78..3fcdccd1ae 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ModificationApplyOperation.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ModificationApplyOperation.java @@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.data.tree.impl; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import java.util.Optional; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode; @@ -26,8 +27,8 @@ import org.opendaylight.yangtools.yang.data.tree.impl.node.Version; * Implementation notes *
    *
  • Implementations MUST expose all nested suboperations which operates on child nodes expose via - * {@link #findChildByArg(PathArgument)} method.
  • - *
  • Same suboperations SHOULD be used when invoked via {@link #apply(ModifiedNode, Optional, Version)}, + * {@link #childByArg(PathArgument)} method.
  • + *
  • Same suboperations SHOULD be used when invoked via {@link #apply(ModifiedNode, TreeNode, Version)}, * if applicable.
  • *
  • There are exactly two base implementations: *
      @@ -47,20 +48,14 @@ abstract sealed class ModificationApplyOperation implements StoreTreeNode apply(ModifiedNode modification, Optional storeMeta, + abstract Optional apply(ModifiedNode modification, @Nullable TreeNode currentMeta, Version version); /** @@ -68,12 +63,12 @@ abstract sealed class ModificationApplyOperation implements StoreTreeNode current, Version version) throws DataValidationFailedException; + @Nullable TreeNode currentMeta, Version version) throws DataValidationFailedException; /** * Performs a quick structural verification of NodeModification, such as written values / types uses right @@ -105,7 +100,7 @@ abstract sealed class ModificationApplyOperation implements StoreTreeNode validatedCurrent; + private @Nullable TreeNode validatedCurrent; private ValidatedTreeNode validatedNode; private ModifiedNode(final PathArgument identifier, final @Nullable TreeNode original, @@ -250,7 +251,7 @@ final class ModifiedNode extends NodeModification implements StoreTreeNode { // A WRITE can collapse all of its children if (!children.isEmpty()) { - value = schema.apply(this, getOriginal(), version).map(TreeNode::getData).orElse(null); + value = schema.apply(this, original(), version).map(TreeNode::getData).orElse(null); children.clear(); } @@ -330,10 +331,10 @@ final class ModifiedNode extends NodeModification implements StoreTreeNode current, + void setValidatedNode(final ModificationApplyOperation op, final @Nullable TreeNode currentMeta, final Optional node) { validatedOp = requireNonNull(op); - validatedCurrent = requireNonNull(current); + validatedCurrent = currentMeta; validatedNode = new ValidatedTreeNode(node); } @@ -342,12 +343,11 @@ final class ModifiedNode extends NodeModification implements StoreTreeNode current) { - return op.equals(validatedOp) && current.equals(validatedCurrent) ? validatedNode : null; + @Nullable ValidatedTreeNode validatedNode(final ModificationApplyOperation op, final @Nullable TreeNode storeMeta) { + return op.equals(validatedOp) && Objects.equals(storeMeta, validatedCurrent) ? validatedNode : null; } } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/OperationWithModification.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/OperationWithModification.java index 52a8072b2d..64b2a891d8 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/OperationWithModification.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/OperationWithModification.java @@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.data.tree.impl; import static java.util.Objects.requireNonNull; import java.util.Optional; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.tree.impl.node.TreeNode; @@ -61,7 +62,7 @@ final class OperationWithModification { Optional snapshot = childNode.getSnapshot(); if (snapshot == null) { // Snapshot is not present, force instantiation - snapshot = applyOperation.getChildByArg(child).apply(childNode, childNode.getOriginal(), version); + snapshot = applyOperation.getChildByArg(child).apply(childNode, childNode.original(), version); } return snapshot.map(TreeNode::getData); @@ -69,7 +70,7 @@ final class OperationWithModification { Optional snapshot = modification.getSnapshot(); if (snapshot == null) { - snapshot = apply(modification.getOriginal(), version); + snapshot = apply(modification.original(), version); } if (snapshot.isPresent()) { @@ -87,7 +88,7 @@ final class OperationWithModification { return applyOperation; } - public Optional apply(final Optional data, final Version version) { + public Optional apply(final @Nullable TreeNode data, final Version version) { return applyOperation.apply(modification, data, version); } diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/SchemaAwareApplyOperation.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/SchemaAwareApplyOperation.java index 8cd04e29ab..79ae731088 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/SchemaAwareApplyOperation.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/SchemaAwareApplyOperation.java @@ -13,6 +13,7 @@ import static com.google.common.base.Verify.verifyNotNull; import java.util.List; import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode; @@ -88,8 +89,8 @@ abstract sealed class SchemaAwareApplyOperation extend return UniqueValidation.of(schemaNode, treeConfig, MinMaxElementsValidation.from(op)); } - protected static void checkNotConflicting(final ModificationPath path, final TreeNode original, - final TreeNode current) throws ConflictingModificationAppliedException { + protected static final void checkNotConflicting(final ModificationPath path, final @NonNull TreeNode original, + final @NonNull TreeNode current) throws ConflictingModificationAppliedException { checkConflicting(path, original.getVersion().equals(current.getVersion()), "Node was replaced by other transaction."); checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()), @@ -104,12 +105,12 @@ abstract sealed class SchemaAwareApplyOperation extend @Override final void checkApplicable(final ModificationPath path, final NodeModification modification, - final Optional current, final Version version) throws DataValidationFailedException { + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { switch (modification.getOperation()) { - case DELETE -> checkDeleteApplicable(modification, current); - case TOUCH -> checkTouchApplicable(path, modification, current, version); - case WRITE -> checkWriteApplicable(path, modification, current, version); - case MERGE -> checkMergeApplicable(path, modification, current, version); + case DELETE -> checkDeleteApplicable(modification, currentMeta); + case TOUCH -> checkTouchApplicable(path, modification, currentMeta, version); + case WRITE -> checkWriteApplicable(path, modification, currentMeta, version); + case MERGE -> checkMergeApplicable(path, modification, currentMeta, version); case NONE -> { // No-op } @@ -147,17 +148,16 @@ abstract sealed class SchemaAwareApplyOperation extend } protected void checkMergeApplicable(final ModificationPath path, final NodeModification modification, - final Optional current, final Version version) throws DataValidationFailedException { + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { final var orig = modification.original(); - if (orig != null && current.isPresent()) { + if (orig != null && currentMeta != null) { /* * We need to do conflict detection only and only if the value of leaf changed before two transactions. If * value of leaf is unchanged between two transactions it should not cause transaction to fail, since result * of this merge leads to same data. */ - final var cur = current.orElseThrow(); - if (!orig.getData().equals(cur.getData())) { - checkNotConflicting(path, orig, cur); + if (!orig.getData().equals(currentMeta.getData())) { + checkNotConflicting(path, orig, currentMeta); } } } @@ -169,55 +169,56 @@ abstract sealed class SchemaAwareApplyOperation extend * * @param path Path from current node in TreeNode * @param modification modification to apply - * @param current current node in TreeNode for modification to apply + * @param currentMeta current node in TreeNode for modification to apply * @throws DataValidationFailedException when a data dependency conflict is detected */ private static void checkWriteApplicable(final ModificationPath path, final NodeModification modification, - final Optional current, final Version version) throws DataValidationFailedException { + final TreeNode currentMeta, final Version version) throws DataValidationFailedException { final var original = modification.original(); - if (original != null && current.isPresent()) { - checkNotConflicting(path, original, current.orElseThrow()); + if (original != null && currentMeta != null) { + checkNotConflicting(path, original, currentMeta); } else { checkConflicting(path, original == null, "Node was deleted by other transaction."); - checkConflicting(path, current.isEmpty(), "Node was created by other transaction."); + checkConflicting(path, currentMeta == null, "Node was created by other transaction."); } } private static void checkDeleteApplicable(final NodeModification modification, - final Optional current) { + final @Nullable TreeNode currentMeta) { // Delete is always applicable, we do not expose it to subclasses - if (current.isEmpty()) { + if (currentMeta == null) { LOG.trace("Delete operation turned to no-op on missing node {}", modification); } } @Override - Optional apply(final ModifiedNode modification, final Optional currentMeta, + Optional apply(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { return switch (modification.getOperation()) { case DELETE -> { // Deletion of a non-existing node is a no-op, report it as such - modification.resolveModificationType(currentMeta.isPresent() ? ModificationType.DELETE + modification.resolveModificationType(currentMeta != null ? ModificationType.DELETE : ModificationType.UNMODIFIED); yield modification.setSnapshot(Optional.empty()); } case TOUCH -> { - checkArgument(currentMeta.isPresent(), "Metadata not available for modification %s", modification); - yield modification.setSnapshot(Optional.of(applyTouch(modification, currentMeta.orElseThrow(), - version))); + if (currentMeta == null) { + throw new IllegalArgumentException("Metadata not available for modification " + modification); + } + yield modification.setSnapshot(Optional.of(applyTouch(modification, currentMeta, version))); } case MERGE -> { final TreeNode result; - if (currentMeta.isEmpty()) { + if (currentMeta == null) { // This is a slight optimization: a merge on a non-existing node equals to a write. Written data // structure is usually verified when the transaction is sealed. To preserve correctness, we have // to run that validation here. modification.resolveModificationType(ModificationType.WRITE); - result = applyWrite(modification, modification.getWrittenValue(), currentMeta, version); + result = applyWrite(modification, modification.getWrittenValue(), null, version); fullVerifyStructure(result.getData()); } else { - result = applyMerge(modification, currentMeta.orElseThrow(), version); + result = applyMerge(modification, currentMeta, version); } yield modification.setSnapshot(Optional.of(result)); @@ -229,7 +230,7 @@ abstract sealed class SchemaAwareApplyOperation extend } case NONE -> { modification.resolveModificationType(ModificationType.UNMODIFIED); - yield currentMeta; + yield Optional.ofNullable(currentMeta); } }; } @@ -244,10 +245,11 @@ abstract sealed class SchemaAwareApplyOperation extend * @param version New subtree version of parent node * @return A sealed TreeNode representing applied operation. */ - protected abstract TreeNode applyMerge(ModifiedNode modification, TreeNode currentMeta, Version version); + protected abstract @NonNull TreeNode applyMerge(ModifiedNode modification, @NonNull TreeNode currentMeta, + Version version); - protected abstract TreeNode applyWrite(ModifiedNode modification, NormalizedNode newValue, - Optional currentMeta, Version version); + protected abstract @NonNull TreeNode applyWrite(ModifiedNode modification, NormalizedNode newValue, + @Nullable TreeNode currentMeta, Version version); /** * Apply a nested operation. Since there may not actually be a nested operation @@ -259,20 +261,21 @@ abstract sealed class SchemaAwareApplyOperation extend * @param version New subtree version of parent node * @return A sealed TreeNode representing applied operation. */ - protected abstract TreeNode applyTouch(ModifiedNode modification, TreeNode currentMeta, Version version); + protected abstract @NonNull TreeNode applyTouch(ModifiedNode modification, @NonNull TreeNode currentMeta, + Version version); /** * Checks is supplied {@link NodeModification} is applicable for Subtree Modification. * * @param path Path to current node * @param modification Node modification which should be applied. - * @param current Current state of data tree + * @param currentMeta Current state of data tree * @throws ConflictingModificationAppliedException If subtree was changed in conflicting way * @throws org.opendaylight.yangtools.yang.data.tree.api.IncorrectDataStructureException If subtree * modification is not applicable (e.g. leaf node). */ protected abstract void checkTouchApplicable(ModificationPath path, NodeModification modification, - Optional current, Version version) throws DataValidationFailedException; + @Nullable TreeNode currentMeta, Version version) throws DataValidationFailedException; /** * Return the {@link DataSchemaNode}-subclass schema associated with this operation. diff --git a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ValueNodeModificationStrategy.java b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ValueNodeModificationStrategy.java index 7ec50eb084..93ccfc10b6 100644 --- a/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ValueNodeModificationStrategy.java +++ b/data/yang-data-tree-ri/src/main/java/org/opendaylight/yangtools/yang/data/tree/impl/ValueNodeModificationStrategy.java @@ -11,7 +11,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.base.MoreObjects.ToStringHelper; -import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -65,13 +64,13 @@ final class ValueNodeModificationStrategy currentMeta, final Version version) { + final TreeNode currentMeta, final Version version) { return TreeNode.of(newValue, version); } @Override protected void checkTouchApplicable(final ModificationPath path, final NodeModification modification, - final Optional current, final Version version) throws IncorrectDataStructureException { + final TreeNode currentMeta, final Version version) throws IncorrectDataStructureException { throw new IncorrectDataStructureException(path.toInstanceIdentifier(), "Subtree modification is not allowed."); } -- 2.36.6