--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception thrown when the backed of a {@link DataTreeSnapshotCursor}
+ * detects an errors which prevents it from completing the requested operation.
+ */
+@Beta
+public class BackendFailedException extends IllegalStateException {
+ private static final long serialVersionUID = 1L;
+
+ public BackendFailedException(final String message) {
+ super(message);
+ }
+
+ public BackendFailedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A {@link DataTreeModification} which allows creation of a {@link DataTreeModificationCursor}.
+ */
+@Beta
+public interface CursorAwareDataTreeModification extends DataTreeModification, CursorAwareDataTreeSnapshot {
+ /**
+ * Create a new {@link DataTreeModificationCursor} at specified path. May fail
+ * if specified path does not exist. It is a programming error to use normal
+ *
+ *
+ * @param path Path at which the cursor is to be anchored
+ * @return A new cursor, or null if the path does not exist.
+ * @throws IllegalStateException if there is another cursor currently open,
+ * or the modification is already {@link #ready()}.
+ */
+ @Override
+ @Nullable DataTreeModificationCursor createCursor(@Nonnull YangInstanceIdentifier path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * A {@link DataTreeSnapshot} which allows creation of a {@link DataTreeSnapshotCursor}.
+ */
+@Beta
+public interface CursorAwareDataTreeSnapshot extends DataTreeSnapshot {
+ /**
+ * Create a new {@link DataTreeSnapshotCursor} at specified path. May fail
+ * if specified path does not exist.
+ *
+ * @param path Path at which the cursor is to be anchored
+ * @return A new cursor, or null if the path does not exist.
+ * @throws IllegalStateException if there is another cursor currently open.
+ */
+ @Nullable DataTreeSnapshotCursor createCursor(@Nonnull YangInstanceIdentifier path);
+}
}
public static void applyToModification(final DataTreeModification modification, final DataTreeCandidate candidate) {
- applyNode(modification, candidate.getRootPath(), candidate.getRootNode());
+ if (modification instanceof CursorAwareDataTreeModification) {
+ try (DataTreeModificationCursor cursor = ((CursorAwareDataTreeModification) modification).createCursor(candidate.getRootPath())) {
+ applyNode(cursor, candidate.getRootNode());
+ }
+ } else {
+ applyNode(modification, candidate.getRootPath(), candidate.getRootNode());
+ }
}
private static void applyNode(final DataTreeModification modification, final YangInstanceIdentifier path, final DataTreeCandidateNode node) {
throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
}
}
+
+ private static void applyNode(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
+ switch (node.getModificationType()) {
+ case DELETE:
+ cursor.delete(node.getIdentifier());
+ break;
+ case SUBTREE_MODIFIED:
+ cursor.enter(node.getIdentifier());
+ for (DataTreeCandidateNode child : node.getChildNodes()) {
+ applyNode(cursor, child);
+ }
+ cursor.exit();
+ break;
+ case UNMODIFIED:
+ // No-op
+ break;
+ case WRITE:
+ cursor.write(node.getIdentifier(), node.getDataAfter().get());
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+ }
+ }
}
*/
package org.opendaylight.yangtools.yang.data.api.schema.tree;
+import javax.annotation.Nonnull;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
/**
* Finish creation of a modification, making it ready for application
- * to the data tree. Any calls to this object's methods will result
+ * to the data tree. Any calls to this object's methods except
+ * {@link #applyToCursor(DataTreeModificationCursor)} will result
* in undefined behavior, possibly with an
* {@link IllegalStateException} being thrown.
*/
void ready();
+
+ /**
+ * Apply the contents of this modification to a cursor. This can be used
+ * to replicate this modification onto another one. The cursor's position
+ * must match the root of this modification, otherwise performing this
+ * operation will result in undefined behavior.
+ *
+ * @param cursor cursor to which this modification
+ */
+ void applyToCursor(@Nonnull DataTreeModificationCursor cursor);
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Extension to the {@link DataTreeSnapshotCursor} which allows modifying the data tree.
+ * An instance of this interface can be obtained from {@link CursorAwareDataTreeModification}
+ * and modifications made through this interface are staged in the parent modification.
+ */
+@Beta
+public interface DataTreeModificationCursor extends DataTreeSnapshotCursor {
+ /**
+ * Delete the specified child.
+ *
+ * @param child Child identifier
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ */
+ void delete(PathArgument child);
+
+ /**
+ * Merge the specified data with the currently-present data
+ * at specified path.
+ *
+ * @param child Child identifier
+ * @param data Data to be merged
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ */
+ void merge(PathArgument child, NormalizedNode<?, ?> data);
+
+ /**
+ * Replace the data at specified path with supplied data.
+ *
+ * @param child Child identifier
+ * @param data New node data
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ */
+ void write(PathArgument child, NormalizedNode<?, ?> data);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.api.schema.tree;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import javax.annotation.Nonnull;
+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;
+
+/**
+ * A cursor holding a logical position within a {@link DataTreeSnapshot}. It allows
+ * operations relative to that position, as well as moving the position up or down
+ * the tree.
+ */
+@Beta
+public interface DataTreeSnapshotCursor extends AutoCloseable {
+ /**
+ * Move the cursor to the specified child of the current position.
+ *
+ * @param child Child identifier
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ * @throws IllegalArgumentException when specified identifier does not identify
+ * a valid child, or if that child is not an
+ * instance of {@link NormalizedNodeContainer}.
+ */
+ void enter(@Nonnull PathArgument child);
+
+ /**
+ * Move the cursor to the specified child of the current position. This is
+ * the equivalent of multiple invocations of {@link #enter(PathArgument)},
+ * except the operation is performed atomically.
+ *
+ * @param path Nested child identifier
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ * @throws IllegalArgumentException when specified path does not identify
+ * a valid child, or if that child is not an
+ * instance of {@link NormalizedNodeContainer}.
+ */
+ void enter(@Nonnull PathArgument... path);
+
+ /**
+ * Move the cursor to the specified child of the current position. This is
+ * equivalent to {@link #enter(PathArgument...)}, except it takes an {@link Iterable}
+ * argument.
+ *
+ * @param path Nested child identifier
+ * @throws BackendFailedException when implementation-specific errors occurs
+ * while servicing the request.
+ * @throws IllegalArgumentException when specified path does not identify
+ * a valid child, or if that child is not an
+ * instance of {@link NormalizedNodeContainer}.
+ */
+ void enter(@Nonnull Iterable<PathArgument> path);
+
+ /**
+ * Move the cursor up to the parent of current position. This is equivalent of
+ * invoking <code>exit(1)</code>.
+ *
+ * @throws IllegalStateException when exiting would violate containment, typically
+ * by attempting to exit more levels than previously
+ * entered.
+ */
+ void exit();
+
+ /**
+ * Move the cursor up by specified amounts of steps from the current position.
+ * This is equivalent of invoking {@link #exit()} multiple times, except the
+ * operation is performed atomically.
+ *
+ * @param depth number of steps to exit
+ * @throws IllegalArgumentException when depth is negative.
+ * @throws IllegalStateException when exiting would violate containment, typically
+ * by attempting to exit more levels than previously
+ * entered.
+ */
+ void exit(int depth);
+
+ /**
+ * Read a particular node from the snapshot.
+ *
+ * @param child Child identifier
+ * @return Optional result encapsulating the presence and value of the node
+ * @throws BackendFailedException when implementation-specific error occurs while
+ * servicing the request.
+ * @throws IllegalArgumentException when specified path does not identify a valid child.
+ */
+ Optional<NormalizedNode<?, ?>> readNode(@Nonnull PathArgument child);
+
+ /**
+ * Close this cursor. Attempting any further operations on the cursor will lead
+ * to undefined behavior.
+ */
+ @Override
+ void close();
+}
public synchronized void ready() {
delegate.ready();
}
+
+ @Override
+ public synchronized void applyToCursor(final DataTreeModificationCursor cursor) {
+ delegate.applyToCursor(cursor);
+ }
}
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.schema.NormalizedNode;
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.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;
Version getVersion() {
return version;
}
+
+ private static void applyChildren(final DataTreeModificationCursor cursor, final ModifiedNode node) {
+ final Collection<ModifiedNode> 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);
+ }
+ }
}