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=4e2d84e13f99dfca366f1a5ae85eb47a0f099bf1;hpb=f4be08675ce4837517c449ef5888383756fd4902;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 4e2d84e13f..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,22 +9,26 @@ 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.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.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.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); @@ -63,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 @@ -88,13 +92,14 @@ 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 = StoreTreeNodes.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(); + final NormalizedNode data = result.get().getData(); return NormalizedNodes.findNode(key, data, path); } else { return Optional.absent(); @@ -108,15 +113,14 @@ 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 void upgradeIfPossible() { + void upgradeIfPossible() { if (rootNode.getOperation() == LogicalOperation.NONE) { strategyTree.upgradeIfPossible(); } @@ -146,29 +150,21 @@ final class InMemoryDataTreeModification implements DataTreeModification { ModifiedNode modification = rootNode; int i = 1; - for(PathArgument pathArg : path.getPathArguments()) { - Optional potential = operation.getChild(pathArg); + for(final PathArgument pathArg : path.getPathArguments()) { + final Optional potential = operation.getChild(pathArg); if (!potential.isPresent()) { - throw new IllegalArgumentException(String.format("Child %s is not present in tree.", - Iterables.toString(Iterables.limit(path.getPathArguments(), i)))); + 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.getChildPolicy()); + modification = modification.modifyChild(pathArg, operation, version); } 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."); - - rootNode.seal(); - } - private void checkSealed() { Preconditions.checkState(sealed == 0, "Data Tree is sealed. No further modifications allowed."); } @@ -191,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); + } }