import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
import org.opendaylight.yangtools.yang.data.api.schema.tree.IncorrectDataStructureException;
import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.MutableTreeNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory;
import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
return null;
}
- public static boolean checkConflicting(final InstanceIdentifier path, final boolean condition, final String message) throws ConflictingModificationAppliedException {
+ public static boolean checkConflicting(final YangInstanceIdentifier path, final boolean condition, final String message) throws ConflictingModificationAppliedException {
if(!condition) {
throw new ConflictingModificationAppliedException(path, message);
}
}
}
- private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws ConflictingModificationAppliedException {
+ private static final void checkNotConflicting(final YangInstanceIdentifier path, final TreeNode original, final TreeNode current) throws ConflictingModificationAppliedException {
checkConflicting(path, original.getVersion().equals(current.getVersion()),
"Node was replaced by other transaction.");
checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
}
@Override
- public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
+ public final void checkApplicable(final YangInstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
switch (modification.getType()) {
case DELETE:
checkDeleteApplicable(modification, current);
}
- protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
+ protected void checkMergeApplicable(final YangInstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
/*
}
}
- protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
+ /**
+ * Checks if write operation can be applied to current TreeNode.
+ * The operation checks if original tree node to which the modification is going to be applied exists and if
+ * current node in TreeNode structure exists.
+ *
+ * @param path Path from current node in TreeNode
+ * @param modification modification to apply
+ * @param current current node in TreeNode for modification to apply
+ * @throws DataValidationFailedException
+ */
+ protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification,
+ final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
checkNotConflicting(path, original.get(), current.get());
} else if(original.isPresent()) {
throw new ConflictingModificationAppliedException(path,"Node was deleted by other transaction.");
+ } else if(current.isPresent()) {
+ throw new ConflictingModificationAppliedException(path,"Node was created by other transaction.");
}
}
}
}
+ boolean isOrdered() {
+ return false;
+ }
+
@Override
public final Optional<TreeNode> apply(final ModifiedNode modification,
final Optional<TreeNode> currentMeta, final Version version) {
case MERGE:
if(currentMeta.isPresent()) {
return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(), version)));
- } // Fallback to write is intentional - if node is not preexisting merge is same as write
+ }
+ // intentional fall-through: if the node does not exist a merge is same as a write
case WRITE:
return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, version)));
case UNMODIFIED:
* @throws ConflictingModificationAppliedException If subtree was changed in conflicting way
* @throws IncorrectDataStructureException If subtree modification is not applicable (e.g. leaf node).
*/
- protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
+ protected abstract void checkSubtreeModificationApplicable(YangInstanceIdentifier path, final NodeModification modification,
final Optional<TreeNode> current) throws DataValidationFailedException;
protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
entryStrategy = Optional.<ModificationApplyOperation> of(new DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy(schema));
}
+ @Override
+ boolean isOrdered() {
+ return true;
+ }
+
@Override
protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
final Version version) {
@Override
protected TreeNode applyWrite(final ModifiedNode modification,
final Optional<TreeNode> currentMeta, final Version version) {
- return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), version);
+ final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
+ final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
+
+ if (Iterables.isEmpty(modification.getChildren())) {
+ return newValueMeta;
+ }
+
+ /*
+ * This is where things get interesting. The user has performed a write and
+ * then she applied some more modifications to it. So we need to make sense
+ * of that an apply the operations on top of the written value. We could have
+ * done it during the write, but this operation is potentially expensive, so
+ * we have left it out of the fast path.
+ *
+ * As it turns out, once we materialize the written data, we can share the
+ * code path with the subtree change. So let's create an unsealed TreeNode
+ * and run the common parts on it -- which end with the node being sealed.
+ */
+ final MutableTreeNode mutable = newValueMeta.mutable();
+ mutable.setSubtreeVersion(version);
+
+ @SuppressWarnings("rawtypes")
+ final NormalizedNodeContainerBuilder dataBuilder = ImmutableUnkeyedListEntryNodeBuilder
+ .create((UnkeyedListEntryNode) newValue);
+
+ return mutateChildren(mutable, dataBuilder, version, modification.getChildren());
+ }
+
+ /**
+ * Applies write/remove diff operation for each modification child in modification subtree.
+ * Operation also sets the Data tree references for each Tree Node (Index Node) in meta (MutableTreeNode) structure.
+ *
+ * @param meta MutableTreeNode (IndexTreeNode)
+ * @param data DataBuilder
+ * @param nodeVersion Version of TreeNode
+ * @param modifications modification operations to apply
+ * @return Sealed immutable copy of TreeNode structure with all Data Node references set.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data,
+ final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
+
+ for (ModifiedNode mod : modifications) {
+ final PathArgument id = mod.getIdentifier();
+ final Optional<TreeNode> cm = meta.getChild(id);
+
+ Optional<TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
+ if (result.isPresent()) {
+ final TreeNode tn = result.get();
+ meta.addChild(tn);
+ data.addChild(tn.getData());
+ } else {
+ meta.removeChild(id);
+ data.removeChild(id);
+ }
+ }
+
+ meta.setData(data.build());
+ return meta.seal();
}
@Override
}
@Override
- protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
+ protected void checkSubtreeModificationApplicable(final YangInstanceIdentifier path, final NodeModification modification,
final Optional<TreeNode> current) throws IncorrectDataStructureException {
throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}