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%2FAbstractNodeContainerModificationStrategy.java;h=831c818647ff333b89366e02032f23126ae179ee;hb=e55154f0d2e8208ce1356ddbad281df343f3a508;hp=3b0b586b12c9d5d2107667392b2a0fea6b7c246f;hpb=4e46c35c7b2a5f6db3ac2303e62aebd6e8504cfa;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java index 3b0b586b12..831c818647 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractNodeContainerModificationStrategy.java @@ -10,8 +10,10 @@ 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 com.google.common.base.Verify; import java.util.Collection; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.NormalizedNodeContainer; import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException; @@ -57,6 +59,21 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl } } + protected void recursivelyVerifyStructure(NormalizedNode value) { + final NormalizedNodeContainer container = (NormalizedNodeContainer) value; + for (final Object child : container.getValue()) { + checkArgument(child instanceof NormalizedNode); + final NormalizedNode castedChild = (NormalizedNode) child; + final Optional childOp = getChild(castedChild.getIdentifier()); + if (childOp.isPresent()) { + childOp.get().recursivelyVerifyStructure(castedChild); + } else { + throw new SchemaValidationFailedException( + String.format("Child %s is not valid child according to schema.", castedChild.getIdentifier())); + } + } + } + @Override protected TreeNode applyWrite(final ModifiedNode modification, final Optional currentMeta, final Version version) { @@ -130,12 +147,100 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl } @Override - protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, - final Version version) { - // For Node Containers - merge is same as subtree change - we only replace children. + protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { + /* + * The node which we are merging exists. We now need to expand any child operations implied by the value. Once + * we do that, ModifiedNode children will look like this node were a TOUCH and we will let applyTouch() do the + * heavy lifting of applying the children recursively (either through here or through applyWrite(). + */ + final NormalizedNode value = modification.getWrittenValue(); + + Verify.verify(value instanceof NormalizedNodeContainer, "Attempted to merge non-container %s", value); + @SuppressWarnings({"unchecked", "rawtypes"}) + final Collection> children = ((NormalizedNodeContainer) value).getValue(); + for (NormalizedNode c : children) { + final PathArgument id = c.getIdentifier(); + modification.modifyChild(id, resolveChildOperation(id).getChildPolicy(), version); + } return applyTouch(modification, currentMeta, version); } + private void mergeChildrenIntoModification(final ModifiedNode modification, + final Collection> children, final Version version) { + for (NormalizedNode c : children) { + final ModificationApplyOperation childOp = resolveChildOperation(c.getIdentifier()); + final ModifiedNode childNode = modification.modifyChild(c.getIdentifier(), childOp.getChildPolicy(), version); + childOp.mergeIntoModifiedNode(childNode, c, version); + } + } + + @Override + final void mergeIntoModifiedNode(final ModifiedNode modification, final NormalizedNode value, + final Version version) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + final Collection> children = ((NormalizedNodeContainer)value).getValue(); + + switch (modification.getOperation()) { + case NONE: + // Fresh node, just record a MERGE with a value + recursivelyVerifyStructure(value); + modification.updateValue(LogicalOperation.MERGE, value); + return; + case TOUCH: + + mergeChildrenIntoModification(modification, children, 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 children was modified + // before. + modification.updateValue(LogicalOperation.MERGE, createEmptyValue(value)); + return; + case MERGE: + // Merging into an existing node. Merge data children modifications (maybe recursively) and mark as MERGE, + // invalidating cached snapshot + mergeChildrenIntoModification(modification, children, version); + modification.updateOperationType(LogicalOperation.MERGE); + return; + case DELETE: + // Delete performs a data dependency check on existence of the node. Performing a merge on DELETE means 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()) { + // Version does not matter here as we'll throw it out + final Optional current = apply(modification, modification.getOriginal(), Version.initial()); + if (current.isPresent()) { + modification.updateValue(LogicalOperation.WRITE, current.get().getData()); + mergeChildrenIntoModification(modification, children, version); + return; + } + } + + modification.updateValue(LogicalOperation.WRITE, value); + return; + 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); + modification.updateOperationType(LogicalOperation.WRITE); + return; + } + + throw new IllegalArgumentException("Unsupported operation " + modification.getOperation()); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private NormalizedNode createEmptyValue(NormalizedNode value, + Collection> children) { + NormalizedNodeContainerBuilder builder = createBuilder(value); + for (NormalizedNode child : children) { + builder.removeChild(child.getIdentifier()); + } + return builder.build(); + } + @Override protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { /* @@ -176,7 +281,7 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl @Override protected void checkTouchApplicable(final YangInstanceIdentifier path, final NodeModification modification, - final Optional current) throws DataValidationFailedException { + final Optional current, final Version version) throws DataValidationFailedException { if (!modification.getOriginal().isPresent() && !current.isPresent()) { throw new ModifiedNodeDoesNotExistException(path, String.format("Node %s does not exist. Cannot apply modification to its children.", path)); } @@ -185,7 +290,7 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl throw new ConflictingModificationAppliedException(path, "Node was deleted by other transaction."); } - checkChildPreconditions(path, modification, current.get()); + checkChildPreconditions(path, modification, current.get(), version); } /** @@ -195,24 +300,31 @@ abstract class AbstractNodeContainerModificationStrategy extends SchemaAwareAppl * @param modification current modification * @param current Current data tree node. */ - private void checkChildPreconditions(final YangInstanceIdentifier path, final NodeModification modification, final TreeNode current) throws DataValidationFailedException { + private void checkChildPreconditions(final YangInstanceIdentifier path, final NodeModification modification, + final TreeNode current, final Version version) throws DataValidationFailedException { for (final NodeModification childMod : modification.getChildren()) { final YangInstanceIdentifier.PathArgument childId = childMod.getIdentifier(); final Optional childMeta = current.getChild(childId); final YangInstanceIdentifier childPath = path.node(childId); - resolveChildOperation(childId).checkApplicable(childPath, childMod, childMeta); + resolveChildOperation(childId).checkApplicable(childPath, childMod, childMeta, version); } } @Override protected void checkMergeApplicable(final YangInstanceIdentifier path, final NodeModification modification, - final Optional current) throws DataValidationFailedException { + final Optional current, final Version version) throws DataValidationFailedException { if (current.isPresent()) { - checkChildPreconditions(path, modification, current.get()); + checkChildPreconditions(path, modification, current.get(), version); } } + protected boolean verifyChildrenStructure() { + return verifyChildrenStructure; + } + @SuppressWarnings("rawtypes") protected abstract NormalizedNodeContainerBuilder createBuilder(NormalizedNode original); + + protected abstract NormalizedNode createEmptyValue(NormalizedNode original); }