X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=yang%2Fyang-data-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fimpl%2Fschema%2Ftree%2FInMemoryDataTreeModification.java;h=f86c71249bc53b20f2b3a5586cc16b49302c58cb;hb=69680caa00028178595852506403f91ca3e8a1d7;hp=4abee0dfb63e53b85bbdb1e064d73959fe390869;hpb=3911bcef14d0e323a67747fb4a5374561a1ef9f8;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java index 4abee0dfb6..f86c71249b 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java @@ -9,17 +9,19 @@ 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.collect.Iterables; +import java.util.Collection; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; 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.NormalizedNodes; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModificationCursor; +import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNodes; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version; -import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +40,7 @@ final class InMemoryDataTreeModification implements DataTreeModification { InMemoryDataTreeModification(final InMemoryDataTreeSnapshot snapshot, final RootModificationApplyOperation resolver) { this.snapshot = Preconditions.checkNotNull(snapshot); this.strategyTree = Preconditions.checkNotNull(resolver).snapshot(); - this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode(), false); + this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode(), strategyTree.getChildPolicy()); /* * We could allocate version beforehand, since Version contract @@ -71,19 +73,7 @@ final class InMemoryDataTreeModification implements DataTreeModification { public void merge(final YangInstanceIdentifier path, final NormalizedNode data) { checkSealed(); - mergeImpl(resolveModificationFor(path),data); - } - - private void mergeImpl(final OperationWithModification op,final NormalizedNode data) { - if (data instanceof NormalizedNodeContainer) { - @SuppressWarnings({ "rawtypes", "unchecked" }) - NormalizedNodeContainer> dataContainer = (NormalizedNodeContainer) data; - for(NormalizedNode child : dataContainer.getValue()) { - PathArgument childId = child.getIdentifier(); - mergeImpl(op.forChild(childId), child); - } - } - op.merge(data); + resolveModificationFor(path).merge(data); } @Override @@ -100,24 +90,23 @@ final class InMemoryDataTreeModification implements DataTreeModification { * the requested path which has been modified. If no such node exists, * we use the node itself. */ - final Entry entry = TreeNodeUtils.findClosestsOrFirstMatch(rootNode, path, ModifiedNode.IS_TERMINAL_PREDICATE); + final Entry entry = StoreTreeNodes.findClosestsOrFirstMatch(rootNode, path, ModifiedNode.IS_TERMINAL_PREDICATE); final YangInstanceIdentifier key = entry.getKey(); final ModifiedNode mod = entry.getValue(); final Optional result = resolveSnapshot(key, mod); if (result.isPresent()) { NormalizedNode data = result.get().getData(); - return NormalizedNodeUtils.findNode(key, data, path); + return NormalizedNodes.findNode(key, data, path); } else { return Optional.absent(); } } - private Optional resolveSnapshot(final YangInstanceIdentifier path, - final ModifiedNode modification) { - final Optional> potentialSnapshot = modification.getSnapshotCache(); - if (potentialSnapshot.isPresent()) { - return potentialSnapshot.get(); + private Optional resolveSnapshot(final YangInstanceIdentifier path, final ModifiedNode modification) { + final Optional potentialSnapshot = modification.getSnapshot(); + if (potentialSnapshot != null) { + return potentialSnapshot; } try { @@ -129,39 +118,49 @@ final class InMemoryDataTreeModification implements DataTreeModification { } } - private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) { - LOG.trace("Resolving modification apply strategy for {}", path); - if (rootNode.getType() == ModificationType.UNMODIFIED) { + private void upgradeIfPossible() { + if (rootNode.getOperation() == LogicalOperation.NONE) { strategyTree.upgradeIfPossible(); } + } + + private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) { + LOG.trace("Resolving modification apply strategy for {}", path); - return TreeNodeUtils.findNodeChecked(strategyTree, path); + upgradeIfPossible(); + return StoreTreeNodes.findNodeChecked(strategyTree, path); } private OperationWithModification resolveModificationFor(final YangInstanceIdentifier path) { - // We ensure strategy is present. - final ModificationApplyOperation operation = resolveModificationStrategy(path); - - final boolean isOrdered; - if (operation instanceof SchemaAwareApplyOperation) { - isOrdered = ((SchemaAwareApplyOperation) operation).isOrdered(); - } else { - isOrdered = true; - } + upgradeIfPossible(); + /* + * Walk the strategy and modification trees in-sync, creating modification nodes as needed. + * + * If the user has provided wrong input, we may end up with a bunch of TOUCH nodes present + * ending with an empty one, as we will throw the exception below. This fact could end up + * being a problem, as we'd have bunch of phantom operations. + * + * That is fine, as we will prune any empty TOUCH nodes in the last phase of the ready + * process. + */ + ModificationApplyOperation operation = strategyTree; ModifiedNode modification = rootNode; + + int i = 1; for (PathArgument pathArg : path.getPathArguments()) { - modification = modification.modifyChild(pathArg, isOrdered); - } - return OperationWithModification.from(operation, modification); - } + Optional potential = operation.getChild(pathArg); + if (!potential.isPresent()) { + throw new IllegalArgumentException(String.format("Child %s is not present in schema tree.", + Iterables.toString(Iterables.limit(path.getPathArguments(), i)))); + } + operation = potential.get(); + ++i; - @Override - public void ready() { - final boolean wasRunning = UPDATER.compareAndSet(this, 0, 1); - Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree."); + modification = modification.modifyChild(pathArg, operation.getChildPolicy()); + } - rootNode.seal(); + return OperationWithModification.from(operation, modification); } private void checkSealed() { @@ -177,7 +176,7 @@ final class InMemoryDataTreeModification implements DataTreeModification { public DataTreeModification newModification() { Preconditions.checkState(sealed == 1, "Attempted to chain on an unsealed modification"); - if (rootNode.getType() == ModificationType.UNMODIFIED) { + if (rootNode.getOperation() == LogicalOperation.NONE) { // Simple fast case: just use the underlying modification return snapshot.newModification(); } @@ -196,4 +195,61 @@ final class InMemoryDataTreeModification implements DataTreeModification { Version getVersion() { return version; } + + private static void applyChildren(final DataTreeModificationCursor cursor, final ModifiedNode node) { + final Collection children = node.getChildren(); + if (!children.isEmpty()) { + cursor.enter(node.getIdentifier()); + for (ModifiedNode child : children) { + applyNode(cursor, child); + } + cursor.exit(); + } + } + + private static void applyNode(final DataTreeModificationCursor cursor, final ModifiedNode node) { + switch (node.getOperation()) { + case NONE: + break; + case DELETE: + cursor.delete(node.getIdentifier()); + break; + case MERGE: + cursor.merge(node.getIdentifier(), node.getWrittenValue()); + applyChildren(cursor, node); + break; + case TOUCH: + // TODO: we could improve efficiency of cursor use if we could understand + // nested TOUCH operations. One way of achieving that would be a proxy + // cursor, which would keep track of consecutive enter and exit calls + // and coalesce them. + applyChildren(cursor, node); + break; + case WRITE: + cursor.write(node.getIdentifier(), node.getWrittenValue()); + applyChildren(cursor, node); + break; + default: + throw new IllegalArgumentException("Unhandled node operation " + node.getOperation()); + } + } + + @Override + public void applyToCursor(final DataTreeModificationCursor cursor) { + for (ModifiedNode child : rootNode.getChildren()) { + applyNode(cursor, child); + } + } + + @Override + public void ready() { + final boolean wasRunning = UPDATER.compareAndSet(this, 0, 1); + Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree."); + + AbstractReadyIterator current = AbstractReadyIterator.create(rootNode); + do { + current = current.process(); + } while (current != null); + } + }