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%2FInMemoryDataTreeModification.java;h=cf9b2c3377c92f46daf21b8361bccd345e603335;hb=fce70654f7d387a8454c8441459ffff03ad0f1a2;hp=f23cadcf0d2d57b55de1eca7cd975d94c9622bc1;hpb=f8de4c0ccb1426b54f038c1986f05838424bf91f;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 f23cadcf0d..cf9b2c3377 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,21 +9,26 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import java.util.Collection; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import org.opendaylight.yangtools.yang.common.QName; 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.NormalizedNodes; +import org.opendaylight.yangtools.yang.data.api.schema.tree.CursorAwareDataTreeModification; 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.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class InMemoryDataTreeModification implements DataTreeModification { - private static final AtomicIntegerFieldUpdater UPDATER = +final class InMemoryDataTreeModification extends AbstractCursorAware implements CursorAwareDataTreeModification { + private static final AtomicIntegerFieldUpdater SEALED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(InMemoryDataTreeModification.class, "sealed"); private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModification.class); @@ -37,7 +42,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 @@ -62,15 +67,15 @@ final class InMemoryDataTreeModification implements DataTreeModification { @Override public void write(final YangInstanceIdentifier path, final NormalizedNode data) { checkSealed(); - + checkIdentifierReferencesData(path, data); resolveModificationFor(path).write(data); } @Override public void merge(final YangInstanceIdentifier path, final NormalizedNode data) { checkSealed(); - - resolveModificationFor(path).merge(data); + checkIdentifierReferencesData(path, data); + resolveModificationFor(path).merge(data, version); } @Override @@ -87,14 +92,15 @@ 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); + final NormalizedNode data = result.get().getData(); + return NormalizedNodes.findNode(key, data, path); } else { return Optional.absent(); } @@ -107,47 +113,56 @@ final class InMemoryDataTreeModification implements DataTreeModification { } try { - return resolveModificationStrategy(path).apply(modification, modification.getOriginal(), - version); - } catch (Exception e) { + return resolveModificationStrategy(path).apply(modification, modification.getOriginal(), version); + } catch (final Exception e) { LOG.error("Could not create snapshot for {}:{}", path, modification, e); throw e; } } - private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) { - LOG.trace("Resolving modification apply strategy for {}", path); - if (rootNode.getType() == ModificationType.UNMODIFIED) { + void upgradeIfPossible() { + if (rootNode.getOperation() == LogicalOperation.NONE) { strategyTree.upgradeIfPossible(); } + } - return TreeNodeUtils.findNodeChecked(strategyTree, path); + private ModificationApplyOperation resolveModificationStrategy(final YangInstanceIdentifier path) { + LOG.trace("Resolving modification apply strategy for {}", 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; - for (PathArgument pathArg : path.getPathArguments()) { - modification = modification.modifyChild(pathArg, isOrdered); - } - return OperationWithModification.from(operation, modification); - } - @Override - public void ready() { - final boolean wasRunning = UPDATER.compareAndSet(this, 0, 1); - Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree."); + int i = 1; + for(final PathArgument pathArg : path.getPathArguments()) { + final Optional potential = operation.getChild(pathArg); + if (!potential.isPresent()) { + throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.", + path.getAncestor(i))); + } + operation = potential.get(); + ++i; + + modification = modification.modifyChild(pathArg, operation, version); + } - rootNode.seal(); + return OperationWithModification.from(operation, modification); } private void checkSealed() { @@ -163,7 +178,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(); } @@ -172,14 +187,97 @@ final class InMemoryDataTreeModification implements DataTreeModification { * We will use preallocated version, this means returned snapshot will * have same version each time this method is called. */ - TreeNode originalSnapshotRoot = snapshot.getRootNode(); - Optional tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), version); + final TreeNode originalSnapshotRoot = snapshot.getRootNode(); + final Optional tempRoot = strategyTree.apply(rootNode, Optional.of(originalSnapshotRoot), version); + Preconditions.checkState(tempRoot.isPresent(), "Data tree root is not present, possibly removed by previous modification"); - InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree); + final InMemoryDataTreeSnapshot tempTree = new InMemoryDataTreeSnapshot(snapshot.getSchemaContext(), tempRoot.get(), strategyTree); return tempTree.newModification(); } Version getVersion() { return version; } + + boolean isSealed() { + return sealed == 1; + } + + private static void applyChildren(final DataTreeModificationCursor cursor, final ModifiedNode node) { + final Collection children = node.getChildren(); + if (!children.isEmpty()) { + cursor.enter(node.getIdentifier()); + for (final 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 (final ModifiedNode child : rootNode.getChildren()) { + applyNode(cursor, child); + } + } + + static void checkIdentifierReferencesData(final PathArgument arg, final NormalizedNode data) { + Preconditions.checkArgument(arg.equals(data.getIdentifier()), + "Instance identifier references %s but data identifier is %s", arg, data.getIdentifier()); + } + + private static void checkIdentifierReferencesData(final YangInstanceIdentifier path, final NormalizedNode data) { + if (!path.isEmpty()) { + final PathArgument lastArg = path.getLastPathArgument(); + Preconditions.checkArgument(lastArg != null, "Instance identifier %s has invalid null path argument", path); + checkIdentifierReferencesData(lastArg, data); + } else { + final QName type = data.getNodeType(); + Preconditions.checkArgument(SchemaContext.NAME.equals(type), "Incorrect name %s of root node", type); + } + } + + @Override + public DataTreeModificationCursor createCursor(final YangInstanceIdentifier path) { + final OperationWithModification op = resolveModificationFor(path); + return openCursor(new InMemoryDataTreeModificationCursor(this, path, op)); + } + + @Override + public void ready() { + final boolean wasRunning = SEALED_UPDATER.compareAndSet(this, 0, 1); + Preconditions.checkState(wasRunning, "Attempted to seal an already-sealed Data Tree."); + + AbstractReadyIterator current = AbstractReadyIterator.create(rootNode, strategyTree); + do { + current = current.process(version); + } while (current != null); + } }