X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fimpl%2Fschema%2Ftree%2FModifiedNode.java;h=540eb9a632d57b47913c86d63cd5c43d435a681c;hb=7ae69e3edccd348635fdba5b2c8560e353da8b7c;hp=27b42cc8fe55116e8a979f8602cf87cd0acc84cb;hpb=08426cfd9d28adb465cf65194f93c58e477eceb4;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java index 27b42cc8fe..540eb9a632 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java @@ -8,19 +8,20 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree; import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import java.util.HashMap; -import org.opendaylight.yangtools.concepts.Identifiable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; 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.ModificationType; import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode; -import javax.annotation.concurrent.GuardedBy; -import java.util.LinkedHashMap; -import java.util.Map; - /** * Node Modification Node and Tree * @@ -28,13 +29,14 @@ import java.util.Map; * to the data store tree. * * This tree is lazily created and populated via {@link #modifyChild(PathArgument)} - * and {@link StoreMetadataNode} which represents original state {@link #getOriginal()}. + * and {@link TreeNode} which represents original state as tracked by {@link #getOriginal()}. */ -final class ModifiedNode implements StoreTreeNode, Identifiable, NodeModification { - - public static final Predicate IS_TERMINAL_PREDICATE = new Predicate() { +@NotThreadSafe +final class ModifiedNode extends NodeModification implements StoreTreeNode { + static final Predicate IS_TERMINAL_PREDICATE = new Predicate() { @Override - public boolean apply(final ModifiedNode input) { + public boolean apply(final @Nonnull ModifiedNode input) { + Preconditions.checkNotNull(input); switch (input.getType()) { case DELETE: case MERGE: @@ -56,7 +58,7 @@ final class ModifiedNode implements StoreTreeNode, Identifiable snapshotCache; private NormalizedNode value; - private ModifiedNode(final PathArgument identifier, final Optional original, boolean isOrdered) { + private ModifiedNode(final PathArgument identifier, final Optional original, final boolean isOrdered) { this.identifier = identifier; this.original = original; @@ -68,9 +70,9 @@ final class ModifiedNode implements StoreTreeNode, Identifiable getWrittenValue() { return value; @@ -87,7 +89,7 @@ final class ModifiedNode implements StoreTreeNode, Identifiable getOriginal() { + Optional getOriginal() { return original; } @@ -97,7 +99,7 @@ final class ModifiedNode implements StoreTreeNode, Identifiable, Identifiable, Identifiable getChildren() { + Iterable getChildren() { return children.values(); } /** - * * Records a delete for associated node. - * */ - public void delete() { + void delete() { + final ModificationType newType; + + switch (modificationType) { + case DELETE: + case UNMODIFIED: + // We need to record this delete. + newType = ModificationType.DELETE; + break; + case MERGE: + case SUBTREE_MODIFIED: + case WRITE: + /* + * We are canceling a previous modification. This is a bit tricky, + * as the original write may have just introduced the data, or it + * may have modified it. + * + * As documented in BUG-2470, a delete of data introduced in this + * transaction needs to be turned into a no-op. + */ + newType = original.isPresent() ? ModificationType.DELETE : ModificationType.UNMODIFIED; + break; + default: + throw new IllegalStateException("Unhandled deletion of node with " + modificationType); + } + clearSnapshot(); - updateModificationType(ModificationType.DELETE); children.clear(); this.value = null; + updateModificationType(newType); } /** - * * Records a write for associated node. * * @param value */ - public void write(final NormalizedNode value) { + void write(final NormalizedNode value) { clearSnapshot(); updateModificationType(ModificationType.WRITE); children.clear(); this.value = value; } - public void merge(final NormalizedNode data) { + void merge(final NormalizedNode value) { clearSnapshot(); updateModificationType(ModificationType.MERGE); - // FIXME: Probably merge with previous value. - this.value = data; + + /* + * Blind overwrite of any previous data is okay, no matter whether the node + * is simple or complex type. + * + * If this is a simple or complex type with unkeyed children, this merge will + * be turned into a write operation, overwriting whatever was there before. + * + * If this is a container with keyed children, there are two possibilities: + * - if it existed before, this value will never be consulted and the children + * will get explicitly merged onto the original data. + * - if it did not exist before, this value will be used as a seed write and + * children will be merged into it. + * In either case we rely on OperationWithModification to manipulate the children + * before calling this method, so unlike a write we do not want to clear them. + */ + this.value = value; } + /** + * Seal the modification node and prune any children which has not been + * modified. + */ void seal() { clearSnapshot(); - for (ModifiedNode child : children.values()) { + + // Walk all child nodes and remove any children which have not + // been modified. + final Iterator it = children.values().iterator(); + while (it.hasNext()) { + final ModifiedNode child = it.next(); child.seal(); + + if (child.modificationType == ModificationType.UNMODIFIED) { + it.remove(); + } + } + + // A SUBTREE_MODIFIED node without any children is a no-op + if (modificationType == ModificationType.SUBTREE_MODIFIED && children.isEmpty()) { + updateModificationType(ModificationType.UNMODIFIED); } } @@ -203,16 +259,15 @@ final class ModifiedNode implements StoreTreeNode, Identifiable storeSnapshot(final Optional snapshot) { - snapshotCache = snapshot; - return snapshot; + Optional getSnapshot() { + return snapshotCache; } - public Optional> getSnapshotCache() { - return Optional.fromNullable(snapshotCache); + Optional setSnapshot(final Optional snapshot) { + snapshotCache = Preconditions.checkNotNull(snapshot); + return snapshot; } - @GuardedBy("this") private void updateModificationType(final ModificationType type) { modificationType = type; clearSnapshot(); @@ -224,7 +279,20 @@ final class ModifiedNode implements StoreTreeNode, Identifiable value) { + final ModifiedNode ret = new ModifiedNode(getIdentifier(), Optional.absent(), false); + ret.write(value); + return ret; + } + + public static ModifiedNode createUnmodified(final TreeNode metadataTree, final boolean isOrdered) { return new ModifiedNode(metadataTree.getIdentifier(), Optional.of(metadataTree), isOrdered); } }