*/
package org.opendaylight.yangtools.yang.data.api.schema.tree.spi;
-
import org.opendaylight.yangtools.concepts.Identifiable;
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;
/**
- * A very basic data tree node. It has a version (when it was last modified),
- * a subtree version (when any of its children were modified) and some read-only
- * data.
+ * A very basic data tree node. It has a version (when it was last modified), a subtree version (when any of its
+ * children were modified) and some read-only data.
+ *
+ * Semantic difference between these two is important when dealing with modifications involving parent/child
+ * relationships and what operations can be execute concurrently without creating a data dependency conflict.
+ *
+ * A replace/delete operation cannot be applied to this node if the subtree version does not match. This mismatch
+ * still allows modifications to its descendants.
+ *
+ * A mismatch in node version indicates a replacement, preventing a modification of descendants or itself.
*/
+// FIXME: BUG-2399: clarify that versioning rules are not enforced for non-presence containers, as they are not
+// considered to be data nodes.
public interface TreeNode extends Identifiable<PathArgument>, StoreTreeNode<TreeNode> {
/**
- * Get the data node version.
+ * Get the data node version. This version is updated whenever the data representation of this particular node
+ * changes as a result of a direct write to this node or to its parent nodes -- thus indicating that this node
+ * was logically replaced.
*
* @return Current data node version.
*/
Version getVersion();
/**
- * Get the subtree version.
+ * Get the subtree version. This version is updated whenever the data representation of this particular node
+ * changes as the result of a direct or indirect child node being created, replaced or removed.
*
* @return Current subtree version.
*/
package org.opendaylight.yangtools.yang.data.impl.schema.tree;
import static com.google.common.base.Preconditions.checkArgument;
-
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.util.Collection;
@SuppressWarnings("rawtypes")
final NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
+ final TreeNode result = mutateChildren(mutable, dataBuilder, version, modification.getChildren());
- return mutateChildren(mutable, dataBuilder, 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
+ // is larger than it needs to be. Create a new TreeNode to host the data.
+ return TreeNodeFactory.createTreeNode(result.getData(), version);
}
/**
}
@Override
- public TreeNode applyTouch(final ModifiedNode modification,
- final TreeNode currentMeta, final Version version) {
- final MutableTreeNode newMeta = currentMeta.mutable();
- newMeta.setSubtreeVersion(version);
-
+ public TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
/*
- * The user has issued an empty merge operation. In this case we do not perform
+ * The user may have issued an empty merge operation. In this case we do not perform
* a data tree mutation, do not pass GO, and do not collect useless garbage. It
* also means the ModificationType is UNMODIFIED.
*/
final Collection<ModifiedNode> children = modification.getChildren();
- if (children.isEmpty()) {
- modification.resolveModificationType(ModificationType.UNMODIFIED);
- newMeta.setData(currentMeta.getData());
- return newMeta.seal();
- }
-
- @SuppressWarnings("rawtypes")
- final NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
- final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
-
- /*
- * It is possible that the only modifications under this node were empty merges,
- * which were turned into UNMODIFIED. If that is the case, we can turn this operation
- * into UNMODIFIED, too, potentially cascading it up to root. This has the benefit
- * of speeding up any users, who can skip processing child nodes.
- *
- * In order to do that, though, we have to check all child operations are UNMODIFIED.
- * Let's do precisely that, stopping as soon we find a different result.
- */
- for (final ModifiedNode child : children) {
- if (child.getModificationType() != ModificationType.UNMODIFIED) {
- modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
- return ret;
+ if (!children.isEmpty()) {
+ @SuppressWarnings("rawtypes")
+ final NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
+ final MutableTreeNode newMeta = currentMeta.mutable();
+ newMeta.setSubtreeVersion(version);
+ final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
+
+ /*
+ * It is possible that the only modifications under this node were empty merges,
+ * which were turned into UNMODIFIED. If that is the case, we can turn this operation
+ * into UNMODIFIED, too, potentially cascading it up to root. This has the benefit
+ * of speeding up any users, who can skip processing child nodes.
+ *
+ * In order to do that, though, we have to check all child operations are UNMODIFIED.
+ * Let's do precisely that, stopping as soon we find a different result.
+ */
+ for (final ModifiedNode child : children) {
+ if (child.getModificationType() != ModificationType.UNMODIFIED) {
+ modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
+ return ret;
+ }
}
}
+ // The merge operation did not have any children, or all of them turned out to be UNMODIFIED, hence do not
+ // replace the metadata node.
modification.resolveModificationType(ModificationType.UNMODIFIED);
- return ret;
+ return currentMeta;
}
@Override