<module>yang-data-api</module>
<module>yang-data-util</module>
<module>yang-data-impl</module>
+ <module>yang-data-operations</module>
<module>yang-model-api</module>
<module>yang-maven-plugin</module>
<module>yang-maven-plugin-it</module>
<artifactId>yang-data-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-util</artifactId>
protected I nodeIdentifier;
protected AbstractImmutableDataContainerNodeBuilder() {
- this.value = Maps.newLinkedHashMap();
+ this.value = Maps.newHashMap();
}
@Override
final Map<QName, String> attributes) {
super(ImmutableMap.copyOf(children), nodeIdentifier, attributes);
}
-
}
}
import java.util.Map;
+import com.google.common.collect.ImmutableSet;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
public AbstractImmutableDataContainerNode(
final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
- super(nodeIdentifier, ImmutableList.copyOf(children.values()));
+ super(nodeIdentifier, ImmutableSet.copyOf(children.values()));
this.children = children;
}
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
import org.opendaylight.yangtools.yang.model.api.*;
public static Optional<ChoiceCaseNode> detectCase(ChoiceNode schema, DataContainerChild<?, ?> child) {
for (ChoiceCaseNode choiceCaseNode : schema.getCases()) {
- for (DataSchemaNode childFromCase : choiceCaseNode.getChildNodes()) {
- if (childFromCase.getQName().equals(child.getNodeType())) {
- return Optional.of(choiceCaseNode);
- }
+ if (child instanceof AugmentationNode
+ && belongsToCaseAugment(choiceCaseNode,
+ (InstanceIdentifier.AugmentationIdentifier) child.getIdentifier())) {
+ return Optional.of(choiceCaseNode);
+ } else if (choiceCaseNode.getDataChildByName(child.getNodeType()) != null) {
+ return Optional.of(choiceCaseNode);
}
}
return Optional.absent();
}
+
+ public static boolean belongsToCaseAugment(ChoiceCaseNode caseNode, InstanceIdentifier.AugmentationIdentifier childToProcess) {
+ for (AugmentationSchema augmentationSchema : caseNode.getAvailableAugmentations()) {
+
+ Set<QName> currentAugmentChildNodes = Sets.newHashSet();
+ for (DataSchemaNode dataSchemaNode : augmentationSchema.getChildNodes()) {
+ currentAugmentChildNodes.add(dataSchemaNode.getQName());
+ }
+
+ if(childToProcess.getPossibleChildNames().equals(currentAugmentChildNodes)){
+ return true;
+ }
+ }
+
+ return false;
+ }
}
namespace = "";
}
- // SKip namespace definitions
+ // Skip namespace definitions
if(namespace.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
continue;
}
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreComments(true);
- XMLAssert.assertXMLEqual(XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
- XMLUnit.buildTestDocument(toString(el)));
+ System.err.println(toString(doc.getDocumentElement()));
+ System.err.println(toString(el));
+
+ boolean diff = new Diff(XMLUnit.buildControlDocument(toString(doc.getDocumentElement())), XMLUnit.buildTestDocument(toString(el))).similar();
}
private Document loadDocument(String xmlPath) throws Exception {
import java.net.URISyntaxException;
import java.util.Set;
+import com.google.common.collect.Sets;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
assertNotNull(serialized.iterator().hasNext());
CompositeNode compNode = TestUtils.prepareCompositeNodeStruct();
- assertEquals(serialized.iterator().next(), compNode);
+
+ assertEquals(serialized.iterator().next().getNodeType(), compNode.getNodeType());
+
+ Set<Node<?>> value = Sets.newHashSet(((CompositeNode)serialized.iterator().next()).getValue());
+ assertEquals(value, Sets.newHashSet(compNode.getValue()));
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>yang-data-operations</artifactId>
+ <name>${project.artifactId}</name>
+ <description>${project.artifactId}</description>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ org.opendaylight.yangtools.yang.data.operations
+ </Export-Package>
+ <Import-Package>
+ *
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+abstract class AbstractContainerNodeModification<S, N extends DataContainerNode<?>> implements Modification<S, N> {
+
+ @Override
+ public final Optional<N> modify(S schema, Optional<N> actual, Optional<N> modification,
+ OperationStack operationStack) throws DataModificationException {
+
+ operationStack.enteringNode(modification);
+
+ Optional<N> result;
+ QName nodeQName = getQName(schema);
+
+ switch (operationStack.getCurrentOperation()) {
+ case DELETE: {
+ DataModificationException.DataMissingException.check(nodeQName, actual);
+ }
+ case REMOVE: {
+ result = Optional.absent();
+ break;
+ }
+ case CREATE: {
+ DataModificationException.DataExistsException.check(nodeQName, actual, null);
+ }
+ case REPLACE: {
+ result = modification;
+ break;
+ }
+ case NONE: {
+ DataModificationException.DataMissingException.check(nodeQName, actual);
+ }
+ case MERGE: {
+ // Recursively modify all child nodes
+ result = modifyContainer(schema, actual, modification, operationStack);
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown",
+ operationStack.getCurrentOperation(), schema));
+ }
+
+ operationStack.exitingNode(modification);
+ return result;
+ }
+
+ protected abstract QName getQName(S schema);
+
+ private Optional<N> modifyContainer(S schema, Optional<N> actual, Optional<N> modification,
+ OperationStack operationStack) throws DataModificationException {
+
+ if (actual.isPresent() == false) {
+ return modification;
+ }
+
+ if (modification.isPresent() == false) {
+ return actual;
+ }
+
+ Set<InstanceIdentifier.PathArgument> toProcess = getChildrenToProcess(schema, actual, modification);
+
+ List<? extends DataContainerChild<?, ?>> merged = modifyContainerChildNodes(schema, operationStack,
+ actual.get(), modification.get(), toProcess);
+ return build(schema, merged);
+ }
+
+ private List<? extends DataContainerChild<?, ?>> modifyContainerChildNodes(S schema, OperationStack operationStack,
+ N actual, N modification, Set<InstanceIdentifier.PathArgument> toProcess) throws DataModificationException {
+ List<DataContainerChild<?, ?>> result = Lists.newArrayList();
+
+ for (InstanceIdentifier.PathArgument childToProcessId : toProcess) {
+ Object schemaOfChildToProcess = findSchema(schema, childToProcessId);
+
+ Optional<? extends DataContainerChild<?, ?>> modifiedValues = modifyContainerNode(operationStack, actual,
+ modification, childToProcessId, schemaOfChildToProcess);
+
+ if (modifiedValues.isPresent()) {
+ result.add(modifiedValues.get());
+ }
+ }
+
+ return result;
+ }
+
+ private Optional<? extends DataContainerChild<?, ?>> modifyContainerNode(OperationStack operationStack, N actual,
+ N modification, InstanceIdentifier.PathArgument childToProcess, Object schemaChild)
+ throws DataModificationException {
+
+ Optional<DataContainerChild<?, ?>> storedChildren = actual.getChild(childToProcess);
+ Optional<DataContainerChild<?, ?>> modifiedChildren = modification.getChild(childToProcess);
+
+ return NodeDispatcher.dispatchChildModification(schemaChild, storedChildren, modifiedChildren, operationStack);
+ }
+
+ private Object findSchema(S schema, InstanceIdentifier.PathArgument childToProcessId) {
+ if (childToProcessId instanceof InstanceIdentifier.AugmentationIdentifier) {
+ return findSchemaForAugment(schema, (InstanceIdentifier.AugmentationIdentifier) childToProcessId);
+ } else {
+ return findSchemaForChild(schema, childToProcessId.getNodeType());
+ }
+ }
+
+ protected abstract Object findSchemaForChild(S schema, QName nodeType);
+
+ protected abstract Object findSchemaForAugment(S schema, InstanceIdentifier.AugmentationIdentifier childToProcessId);
+
+ private Optional<N> build(S schema, List<? extends DataContainerChild<?, ?>> result) {
+ DataContainerNodeBuilder<?, N> b = getBuilder(schema);
+
+ // TODO skip empty container nodes ? e.g. if container looses all its child nodes
+// if(result.isEmpty()) {
+// return Optional.absent();
+// }
+
+ for (DataContainerChild<?, ?> dataContainerChild : result) {
+ b.withChild(dataContainerChild);
+ }
+ return Optional.of(b.build());
+ }
+
+ protected abstract DataContainerNodeBuilder<?, N> getBuilder(S schema);
+
+ protected Set<InstanceIdentifier.PathArgument> getChildrenToProcess(S schema, Optional<N> actual,
+ Optional<N> modification) throws DataModificationException {
+ Set<InstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
+
+ qNames.addAll(getChildQNames(actual));
+ qNames.addAll(getChildQNames(modification));
+ return qNames;
+ }
+
+ private Set<? extends InstanceIdentifier.PathArgument> getChildQNames(Optional<N> actual) {
+ Set<InstanceIdentifier.PathArgument> qNames = Sets.newLinkedHashSet();
+
+ for (DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> child : actual.get().getValue()) {
+ qNames.add(child.getIdentifier());
+ }
+
+ return qNames;
+ }
+
+ private static final class NodeDispatcher {
+
+ private static final LeafNodeModification LEAF_NODE_MODIFICATION = new LeafNodeModification();
+ private static final LeafSetNodeModification LEAF_SET_NODE_MODIFICATION = new LeafSetNodeModification();
+ private static final AugmentationNodeModification AUGMENTATION_NODE_MODIFICATION = new AugmentationNodeModification();
+ private static final MapNodeModification MAP_NODE_MODIFICATION = new MapNodeModification();
+ private static final ContainerNodeModification CONTAINER_NODE_MODIFICATION = new ContainerNodeModification();
+ private static final ChoiceNodeModification CHOICE_NODE_MODIFICATION = new ChoiceNodeModification();
+
+ static Optional<? extends DataContainerChild<?, ?>> dispatchChildModification(Object schemaChild,
+ Optional<DataContainerChild<?, ?>> actual, Optional<DataContainerChild<?, ?>> modification,
+ OperationStack operations) throws DataModificationException {
+
+ if (schemaChild instanceof LeafSchemaNode) {
+ return onLeafNode((LeafSchemaNode) schemaChild, actual, modification, operations);
+ } else if (schemaChild instanceof ContainerSchemaNode) {
+ return onContainerNode((ContainerSchemaNode) schemaChild, actual, modification, operations);
+ } else if (schemaChild instanceof LeafListSchemaNode) {
+ return onLeafSetNode((LeafListSchemaNode) schemaChild, actual, modification, operations);
+ } else if (schemaChild instanceof AugmentationSchema) {
+ return onAugmentationNode((AugmentationSchema) schemaChild, actual, modification, operations);
+ } else if (schemaChild instanceof ListSchemaNode) {
+ return onMapNode((ListSchemaNode) schemaChild, actual, modification, operations);
+ } else if (schemaChild instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
+ return onChoiceNode((org.opendaylight.yangtools.yang.model.api.ChoiceNode) schemaChild, actual,
+ modification, operations);
+ }
+
+ throw new IllegalArgumentException("Unknown schema node type " + schemaChild);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onChoiceNode(
+ org.opendaylight.yangtools.yang.model.api.ChoiceNode schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, ChoiceNode.class);
+ checkType(modification, ChoiceNode.class);
+ return CHOICE_NODE_MODIFICATION.modify(schemaChild, (Optional<ChoiceNode>) actual,
+ (Optional<ChoiceNode>) modification, operations);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onMapNode(ListSchemaNode schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, MapNode.class);
+ checkType(modification, MapNode.class);
+ return MAP_NODE_MODIFICATION.modify(schemaChild, (Optional<MapNode>) actual,
+ (Optional<MapNode>) modification, operations);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onAugmentationNode(AugmentationSchema schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, AugmentationNode.class);
+ checkType(modification, AugmentationNode.class);
+ return AUGMENTATION_NODE_MODIFICATION.modify(schemaChild, (Optional<AugmentationNode>) actual,
+ (Optional<AugmentationNode>) modification, operations);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onLeafSetNode(LeafListSchemaNode schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, LeafSetNode.class);
+ checkType(modification, LeafSetNode.class);
+ return LEAF_SET_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafSetNode<?>>) actual,
+ (Optional<LeafSetNode<?>>) modification, operations);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onContainerNode(ContainerSchemaNode schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, ContainerNode.class);
+ checkType(modification, ContainerNode.class);
+ return CONTAINER_NODE_MODIFICATION.modify(schemaChild, (Optional<ContainerNode>) actual,
+ (Optional<ContainerNode>) modification, operations);
+ }
+
+ private static Optional<? extends DataContainerChild<?, ?>> onLeafNode(LeafSchemaNode schemaChild,
+ Optional<? extends DataContainerChild<?, ?>> actual,
+ Optional<? extends DataContainerChild<?, ?>> modification, OperationStack operations)
+ throws DataModificationException {
+ checkType(actual, LeafNode.class);
+ checkType(modification, LeafNode.class);
+ return LEAF_NODE_MODIFICATION.modify(schemaChild, (Optional<LeafNode<?>>) actual,
+ (Optional<LeafNode<?>>) modification, operations);
+ }
+
+ private static void checkType(Optional<? extends DataContainerChild<?, ?>> actual, Class<?> leafNodeClass) {
+ if (actual.isPresent()) {
+ Preconditions.checkArgument(leafNodeClass.isAssignableFrom(actual.get().getClass()),
+ "Unexpected node type, should be: %s, but was: %s, for: %s", leafNodeClass, actual.getClass(),
+ actual);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+
+final class AugmentationNodeModification extends AbstractContainerNodeModification<AugmentationSchema, AugmentationNode> {
+
+ @Override
+ protected QName getQName(AugmentationSchema schema) {
+ // FIXME null qname for AUGMENT
+ return null;
+ }
+
+ @Override
+ protected Object findSchemaForChild(AugmentationSchema schema, QName nodeType) {
+ return SchemaUtils.findSchemaForChild(schema, nodeType);
+ }
+
+ @Override
+ protected Object findSchemaForAugment(AugmentationSchema schema, InstanceIdentifier.AugmentationIdentifier childToProcessId) {
+ throw new UnsupportedOperationException("Augmentation cannot be augmented directly, " + schema);
+ }
+
+ @Override
+ protected DataContainerNodeBuilder<?, AugmentationNode> getBuilder(AugmentationSchema schema) {
+ return Builders.augmentationBuilder(schema);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+
+final class ChoiceNodeModification extends
+ AbstractContainerNodeModification<ChoiceNode, org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode> {
+
+ @Override
+ protected QName getQName(ChoiceNode schema) {
+ return schema.getQName();
+ }
+
+ @Override
+ protected Object findSchemaForChild(ChoiceNode schema, QName nodeType) {
+ return SchemaUtils.findSchemaForChild(schema, nodeType);
+ }
+
+ @Override
+ protected Set<InstanceIdentifier.PathArgument> getChildrenToProcess(ChoiceNode schema,
+ Optional<org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode> actual,
+ Optional<org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode> modification)
+ throws DataModificationException {
+ Set<InstanceIdentifier.PathArgument> childrenToProcess = super.getChildrenToProcess(schema, actual,
+ modification);
+
+ if (modification.isPresent() == false) {
+ return childrenToProcess;
+ }
+
+ // Detect case node from modification
+ ChoiceCaseNode detectedCase = null;
+ for (DataContainerChild<? extends InstanceIdentifier.PathArgument, ?> child : modification.get().getValue()) {
+ Optional<ChoiceCaseNode> detectedCaseForChild = SchemaUtils.detectCase(schema, child);
+
+ if(detectedCaseForChild.isPresent() == false) {
+ DataModificationException.IllegalChoiceValuesException.throwUnknownChild(schema.getQName(),
+ child.getNodeType());
+ }
+
+ if (detectedCase != null && detectedCase.equals(detectedCaseForChild.get()) == false) {
+ DataModificationException.IllegalChoiceValuesException.throwMultipleCasesReferenced(schema.getQName(),
+ modification.get(), detectedCase.getQName(), detectedCaseForChild.get().getQName());
+ }
+ detectedCase = detectedCaseForChild.get();
+ }
+
+ if (detectedCase == null)
+ return childrenToProcess;
+
+ // Filter out child nodes that do not belong to detected case =
+ // Nodes from other cases present in actual
+ Set<InstanceIdentifier.PathArgument> childrenToProcessFiltered = Sets.newLinkedHashSet();
+ for (InstanceIdentifier.PathArgument childToProcess : childrenToProcess) {
+ // child from other cases, skip
+ if (childToProcess instanceof AugmentationNode
+ && SchemaUtils.belongsToCaseAugment(detectedCase,
+ (InstanceIdentifier.AugmentationIdentifier) childToProcess) == false) {
+ continue;
+ } else if (belongsToCase(detectedCase, childToProcess) == false) {
+ continue;
+ }
+
+ childrenToProcessFiltered.add(childToProcess);
+ }
+
+ return childrenToProcessFiltered;
+ }
+
+ private boolean belongsToCase(ChoiceCaseNode detectedCase, InstanceIdentifier.PathArgument childToProcess) {
+ return detectedCase.getDataChildByName(childToProcess.getNodeType()) != null;
+ }
+
+ @Override
+ protected Object findSchemaForAugment(ChoiceNode schema, InstanceIdentifier.AugmentationIdentifier childToProcessId) {
+ return SchemaUtils.findSchemaForAugment(schema, childToProcessId.getPossibleChildNames());
+ }
+
+ @Override
+ protected DataContainerNodeBuilder<?, org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode> getBuilder(
+ ChoiceNode schema) {
+ return Builders.choiceBuilder(schema);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+final class ContainerNodeModification extends AbstractContainerNodeModification<ContainerSchemaNode, ContainerNode> {
+
+ // FIXME normalized nodes as a result from merge contain attributes e.g. operation="merge" from modification
+
+ @Override
+ protected QName getQName(ContainerSchemaNode schema) {
+ return schema.getQName();
+ }
+
+ @Override
+ protected Object findSchemaForChild(ContainerSchemaNode schema, QName nodeType) {
+ return SchemaUtils.findSchemaForChild(schema, nodeType);
+ }
+
+ @Override
+ protected Object findSchemaForAugment(ContainerSchemaNode schema,
+ InstanceIdentifier.AugmentationIdentifier childToProcessId) {
+ return SchemaUtils.findSchemaForAugment(schema, childToProcessId.getPossibleChildNames());
+ }
+
+ @Override
+ protected DataContainerNodeBuilder<?, ContainerNode> getBuilder(ContainerSchemaNode schema) {
+ return Builders.containerBuilder(schema);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+
+public class DataModificationException extends Exception {
+ // TODO replace QName as identifier for node with schema or NodeIdentifier,
+ // Augmentation does not have a QName
+
+ private static final long serialVersionUID = 1L;
+ private final QName node;
+
+ public DataModificationException(String message, QName node) {
+ super(message);
+ this.node = node;
+ }
+
+ public QName getNodeQName() {
+ return node;
+ }
+
+ public static final class DataMissingException extends DataModificationException {
+ private static final long serialVersionUID = 1L;
+
+ public DataMissingException(QName nodeType) {
+ super(String.format("Data missing for node: %s", nodeType), nodeType);
+ }
+
+ public DataMissingException(QName nodeType, Node<?> modificationNode) {
+ super(String.format("Data missing for node: %s, %s", nodeType, modificationNode), nodeType);
+ }
+
+ static void check(QName nodeQName, Optional<? extends NormalizedNode<?, ?>> actualNode) throws DataMissingException {
+ if (actualNode.isPresent() == false) {
+ throw new DataMissingException(nodeQName);
+ }
+ }
+
+ static void check(QName nodeQName, Optional<LeafSetNode<?>> actualNodes, LeafSetEntryNode<?> modificationNode)
+ throws DataMissingException {
+ if (actualNodes.isPresent()==false || actualNodes.get().getChild(modificationNode.getIdentifier()).isPresent() == false) {
+ throw new DataMissingException(nodeQName, modificationNode);
+ }
+ }
+
+ static void check(QName nodeQName, Optional<MapNode> actualNodes, MapEntryNode modificationNode)
+ throws DataModificationException {
+ if (actualNodes.isPresent()==false || actualNodes.get().getChild(modificationNode.getIdentifier()).isPresent() == false) {
+ throw new DataMissingException(nodeQName, modificationNode);
+ }
+ }
+ }
+
+ public static final class DataExistsException extends DataModificationException {
+ private static final long serialVersionUID = 1L;
+
+ public DataExistsException(QName nodeType, NormalizedNode<?, ?> actualNode, NormalizedNode<?, ?> modificationNode) {
+ super(String
+ .format("Data already exists for node: %s, current value: %s. modification value: %s", nodeType, actualNode, modificationNode),
+ nodeType);
+ }
+
+ static void check(QName nodeQName, Optional<? extends NormalizedNode<?, ?>> actualNode, NormalizedNode<?, ?> modificationNode) throws DataExistsException {
+ if (actualNode.isPresent()) {
+ throw new DataExistsException(nodeQName, actualNode.get(), modificationNode);
+ }
+ }
+
+ static void check(QName nodeQName, Optional<LeafSetNode<?>> actualNodes, LeafSetEntryNode<?> modificationNode)
+ throws DataExistsException {
+ if (actualNodes.isPresent() && actualNodes.get().getChild(modificationNode.getIdentifier()).isPresent()) {
+ throw new DataExistsException(nodeQName, actualNodes.get(), modificationNode);
+ }
+ }
+
+ public static void check(QName qName, Optional<MapNode> actualNodes, MapEntryNode listModification)
+ throws DataModificationException {
+ if (actualNodes.isPresent() && actualNodes.get().getChild(listModification.getIdentifier()).isPresent()) {
+ throw new DataExistsException(qName, actualNodes.get(), listModification);
+ }
+ }
+ }
+
+ public static final class IllegalChoiceValuesException extends DataModificationException {
+ private static final long serialVersionUID = 1L;
+
+ public IllegalChoiceValuesException(String message, QName node) {
+ super(message, node);
+ }
+
+ public static void throwMultipleCasesReferenced(QName choiceQName, ChoiceNode modification,
+ QName case1QName, QName case2QName) throws IllegalChoiceValuesException {
+ throw new IllegalChoiceValuesException(String.format(
+ "Child nodes from multiple cases present in modification: %s, choice: %s, case1: %s, case2: %s",
+ modification, choiceQName, case1QName, case2QName), choiceQName);
+ }
+
+ public static void throwUnknownChild(QName choiceQName, QName nodeQName) throws IllegalChoiceValuesException {
+ throw new IllegalChoiceValuesException(String.format(
+ "Unknown child node detected, choice: %s, child node: %s",
+ choiceQName, nodeQName), choiceQName);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Optional;
+
+/**
+ * Edit config operations utilities.
+ */
+public final class DataOperations {
+
+ private DataOperations() {}
+
+ public static Optional<ContainerNode> modify(ContainerSchemaNode schema, ContainerNode stored,
+ ContainerNode modified) throws DataModificationException {
+ return modify(schema, stored, modified, ModifyAction.MERGE);
+ }
+
+ public static Optional<MapNode> modify(ListSchemaNode schema, MapNode stored, MapNode modified)
+ throws DataModificationException {
+ return modify(schema, stored, modified, ModifyAction.MERGE);
+ }
+
+ public static Optional<ContainerNode> modify(ContainerSchemaNode schema, ContainerNode stored,
+ ContainerNode modified, ModifyAction defaultOperation) throws DataModificationException {
+
+ OperationStack operations = new OperationStack(defaultOperation);
+
+ return new ContainerNodeModification().modify(schema, Optional.fromNullable(stored),
+ Optional.fromNullable(modified), operations);
+ }
+
+ public static Optional<MapNode> modify(ListSchemaNode schema, MapNode stored, MapNode modified,
+ ModifyAction defaultOperation) throws DataModificationException {
+
+ OperationStack operations = new OperationStack(defaultOperation);
+
+ return new MapNodeModification().modify(schema, Optional.fromNullable(stored), Optional.fromNullable(modified),
+ operations);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+import com.google.common.base.Optional;
+
+final class LeafNodeModification implements Modification<LeafSchemaNode, LeafNode<?>> {
+
+ @Override
+ public Optional<LeafNode<?>> modify(LeafSchemaNode schema, Optional<LeafNode<?>> actualNode,
+ Optional<LeafNode<?>> modificationNode, OperationStack operationStack) throws DataModificationException {
+
+ operationStack.enteringNode(modificationNode);
+
+ Optional<LeafNode<?>> result;
+
+ // Returns either actual node, modification node or empty in case of removal
+ switch (operationStack.getCurrentOperation()) {
+ case MERGE: {
+ result = modificationNode.isPresent() ? modificationNode : actualNode;
+ break;
+ }
+ case CREATE: {
+ DataModificationException.DataExistsException.check(schema.getQName(), actualNode, null);
+ }
+ case REPLACE: {
+ result = modificationNode;
+ break;
+ }
+ case DELETE: {
+ DataModificationException.DataMissingException.check(schema.getQName(), actualNode);
+ }
+ case REMOVE: {
+ result = Optional.absent();
+ break;
+ }
+ case NONE: {
+ result = actualNode;
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown", operationStack.getCurrentOperation(), schema));
+
+ }
+
+ operationStack.exitingNode(modificationNode);
+
+ return result;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+final class LeafSetNodeModification implements Modification<LeafListSchemaNode, LeafSetNode<?>> {
+
+ @Override
+ public Optional<LeafSetNode<?>> modify(LeafListSchemaNode schema, Optional<LeafSetNode<?>> actual,
+ Optional<LeafSetNode<?>> modification, OperationStack operationStack) throws DataModificationException {
+
+ List<LeafSetEntryNode<?>> resultNodes = Lists.newArrayList();
+ if(actual.isPresent()) {
+ Iterables.addAll(resultNodes, actual.get().getValue());
+ }
+
+ // TODO implement ordering for modification nodes
+
+ for (LeafSetEntryNode<?> leafListModification : modification.get().getValue()) {
+ operationStack.enteringNode(leafListModification);
+
+ switch (operationStack.getCurrentOperation()) {
+ case MERGE:
+ case CREATE: {
+ DataModificationException.DataExistsException.check(schema.getQName(), actual, leafListModification);
+ }
+ case REPLACE: {
+ if (contains(actual, leafListModification) == false) {
+ resultNodes.add(leafListModification);
+ }
+ break;
+ }
+ case DELETE: {
+ DataModificationException.DataMissingException.check(schema.getQName(), actual, leafListModification);
+ }
+ case REMOVE: {
+ if (resultNodes.contains(leafListModification)) {
+ resultNodes.remove(leafListModification);
+ }
+ break;
+ }
+ case NONE: {
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown", operationStack.getCurrentOperation(), schema));
+ }
+
+ operationStack.exitingNode(leafListModification);
+ }
+ return build(schema, resultNodes);
+ }
+
+ private Optional<LeafSetNode<?>> build(LeafListSchemaNode schemaNode, List<LeafSetEntryNode<?>> resultNodes) {
+ if(resultNodes.isEmpty())
+ return Optional.absent();
+
+ ListNodeBuilder<Object, LeafSetEntryNode<Object>> b = Builders.leafSetBuilder(schemaNode);
+ for (LeafSetEntryNode<?> resultNode : resultNodes) {
+ // FIXME: can we do something about this SuppressWarnings?
+ @SuppressWarnings("unchecked")
+ final LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>) resultNode;
+ b.withChild(child);
+ }
+
+ return Optional.<LeafSetNode<?>>of(b.build());
+ }
+
+ private static boolean contains(Optional<LeafSetNode<?>> actual, LeafSetEntryNode<?> leafListModification) {
+ boolean contains = actual.isPresent();
+ contains &= actual.get().getChild(leafListModification.getIdentifier()).isPresent();
+
+ return contains;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+final class MapEntryNodeModification extends AbstractContainerNodeModification<ListSchemaNode, MapEntryNode> {
+
+ @Override
+ protected QName getQName(ListSchemaNode schema) {
+ return schema.getQName();
+ }
+
+ @Override
+ protected Object findSchemaForChild(ListSchemaNode schema, QName nodeType) {
+ return SchemaUtils.findSchemaForChild(schema, nodeType);
+ }
+
+ @Override
+ protected Object findSchemaForAugment(ListSchemaNode schema, InstanceIdentifier.AugmentationIdentifier childToProcessId) {
+ return SchemaUtils.findSchemaForAugment(schema, childToProcessId.getPossibleChildNames());
+ }
+
+ @Override
+ protected DataContainerNodeBuilder<?, MapEntryNode> getBuilder(ListSchemaNode schema) {
+ return Builders.mapEntryBuilder(schema);
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2013 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.operations;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+
+public class MapNodeModification implements Modification<ListSchemaNode, MapNode> {
+
+ public static final MapEntryNodeModification MAP_ENTRY_NODE_MODIFICATION = new MapEntryNodeModification();
+
+ @Override
+ public Optional<MapNode> modify(ListSchemaNode schema, Optional<MapNode> actual,
+ Optional<MapNode> modification, OperationStack operationStack) throws DataModificationException {
+
+ // Merge or None operation on parent, leaving actual if modification not present
+ if (modification.isPresent() == false)
+ return actual;
+
+ Map<InstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> resultNodes = Maps.newLinkedHashMap();
+ if(actual.isPresent())
+ resultNodes.putAll(mapEntries(actual.get()));
+
+ // TODO implement ordering for modification nodes
+
+ for (MapEntryNode mapEntryModification : modification.get().getValue()) {
+
+ operationStack.enteringNode(mapEntryModification);
+
+ InstanceIdentifier.NodeIdentifierWithPredicates entryKey = mapEntryModification.getIdentifier();
+
+ switch (operationStack.getCurrentOperation()) {
+ case NONE:
+ DataModificationException.DataMissingException.check(schema.getQName(), actual, mapEntryModification);
+ case MERGE: {
+ MapEntryNode mergedListNode;
+ if (resultNodes.containsKey(entryKey)) {
+ MapEntryNode actualEntry = resultNodes.get(entryKey);
+ mergedListNode = MAP_ENTRY_NODE_MODIFICATION.modify(schema, Optional.of(actualEntry), Optional.of(mapEntryModification), operationStack).get();
+ } else {
+ mergedListNode = mapEntryModification;
+ }
+
+ resultNodes.put(mergedListNode.getIdentifier(), mergedListNode);
+ break;
+ }
+ case CREATE: {
+ DataModificationException.DataExistsException.check(schema.getQName(), actual, mapEntryModification);
+ }
+ case REPLACE: {
+ resultNodes.put(entryKey, mapEntryModification);
+ break;
+ }
+ case DELETE: {
+ DataModificationException.DataMissingException.check(schema.getQName(), actual, mapEntryModification);
+ }
+ case REMOVE: {
+ if (resultNodes.containsKey(entryKey)) {
+ resultNodes.remove(entryKey);
+ }
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException(String.format("Unable to perform operation: %s on: %s, unknown", operationStack.getCurrentOperation(), schema));
+ }
+
+ operationStack.exitingNode(mapEntryModification);
+ }
+ return build(schema, resultNodes);
+ }
+
+ private Optional<MapNode> build(ListSchemaNode schema, Map<InstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> resultNodes) {
+ if(resultNodes.isEmpty())
+ return Optional.absent();
+
+ CollectionNodeBuilder<MapEntryNode, MapNode> b = Builders.mapBuilder(schema);
+
+ for (MapEntryNode child : resultNodes.values()) {
+ b.withChild(child);
+ }
+
+ return Optional.of(b.build());
+ }
+
+ private Map<InstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntries(MapNode mapNode) {
+ Map<InstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapped = Maps.newLinkedHashMap();
+
+ for (MapEntryNode mapEntryNode : mapNode.getValue()) {
+ mapped.put(mapEntryNode.getIdentifier(), mapEntryNode);
+ }
+
+ return mapped;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+
+/**
+ * Strategy interface for data modification.
+ *
+ * @param <S> SchemaNode type
+ * @param <N> LeafNode type
+ *
+ */
+interface Modification<S, N extends NormalizedNode<?, ?>> {
+
+ Optional<N> modify(S schemaNode, Optional<N> actual, Optional<N> modification, OperationStack operations) throws DataModificationException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 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.operations;
+
+import java.net.URI;
+import java.util.Deque;
+import java.util.LinkedList;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+/**
+ * Tracks netconf operations on nested nodes.
+ */
+final class OperationStack {
+
+ private static final Logger logger = LoggerFactory.getLogger(OperationStack.class);
+ private static final QName OPERATION_NAME = new QName(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), "operation");
+
+ private final Deque<ModifyAction> operations = new LinkedList<>();
+
+ public OperationStack(ModifyAction operation) {
+ operations.add(operation);
+ }
+
+ public void enteringNode(Optional<? extends NormalizedNode<?, ?>> modificationNode) {
+ if (modificationNode.isPresent() == false) {
+ return;
+ }
+
+ NormalizedNode<?, ?> modification = modificationNode.get();
+
+ enteringNode(modification);
+ }
+
+ public void enteringNode(NormalizedNode<?, ?> modificationNode) {
+ ModifyAction operation = getOperation(modificationNode);
+ if (operation == null) {
+ return;
+ }
+
+ addOperation(operation);
+ }
+
+ private ModifyAction getOperation(NormalizedNode<?, ?> modificationNode) {
+ if (modificationNode instanceof AttributesContainer == false)
+ return null;
+
+ String operationString = ((AttributesContainer) modificationNode).getAttributes().get(OPERATION_NAME);
+
+ return operationString == null ? null : ModifyAction.fromXmlValue(operationString);
+ }
+
+ private void addOperation(ModifyAction operation) {
+ // Add check for permitted operation
+ operations.add(operation);
+ logger.trace("Operation added {}, {}", operation, this);
+ }
+
+ public ModifyAction getCurrentOperation() {
+ return operations.getLast();
+ }
+
+ public void exitingNode(Optional<? extends NormalizedNode<?, ?>> modificationNode) {
+ if (modificationNode.isPresent() == false) {
+ return;
+ }
+
+ NormalizedNode<?, ?> modification = modificationNode.get();
+
+ exitingNode(modification);
+ }
+
+ public void exitingNode(NormalizedNode<?, ?> modification) {
+ ModifyAction operation = getOperation(modification);
+ if (operation == null) {
+ return;
+ }
+
+ Preconditions.checkState(operations.size() > 1);
+ Preconditions.checkState(operations.peekLast().equals(operation), "Operations mismatch %s, %s",
+ operations.peekLast(), operation);
+
+ ModifyAction removed = operations.removeLast();
+ logger.trace("Operation removed {}, {}", removed, this);
+ }
+
+ @Override
+ public String toString() {
+ return operations.toString();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.operations;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class YangDataOperationsNegativeTest extends YangDataOperationsTest{
+
+ private static final String XML_NEG_FOLDER_NAME = "/xmls-negative";
+ private final Class<? extends DataModificationException> expected;
+
+ @Parameterized.Parameters()
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+
+ // Container
+ { "/containerTest_noneContainerActualMissing", DataModificationException.DataMissingException.class },
+ { "/containerTest_createContainerActualPresent", DataModificationException.DataExistsException.class },
+ { "/containerTest_deleteContainerActualMissing", DataModificationException.DataMissingException.class },
+ // List
+ { "/listTest_createListActualPresent", DataModificationException.DataExistsException.class },
+ { "/listTest_deleteListActualMissing", DataModificationException.DataMissingException.class },
+ { "/listTest_noneListActualMissing", DataModificationException.DataMissingException.class },
+ // Leaf
+ { "/leafTest_createLeafActualPresent", DataModificationException.DataExistsException.class },
+ { "/leafTest_deleteLeafActualMissing", DataModificationException.DataMissingException.class },
+ // Leaf list
+ { "/leafListTest_createLeafActualPresent", DataModificationException.DataExistsException.class },
+ { "/leafListTest_deleteLeafActualMissing", DataModificationException.DataMissingException.class },
+ });
+ }
+
+ public YangDataOperationsNegativeTest(String testDir, Class<? extends DataModificationException> e) throws Exception {
+ super(testDir);
+ this.expected = e;
+ }
+
+ @Override
+ protected String getXmlFolderName() {
+ return XML_NEG_FOLDER_NAME;
+ }
+
+ @Test
+ public void testModification() throws Exception {
+ try {
+ DataOperations.modify(containerNode,
+ currentConfig.orNull(), modification.orNull(), modifyAction);
+ fail("Negative test for " + testDirName + " should have failed");
+ } catch (Exception e) {
+ assertEquals(e.getClass(), expected);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.operations;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+@RunWith(Parameterized.class)
+public class YangDataOperationsTest {
+
+ public static final String CURRENT_XML_NAME = "/current.xml";
+ public static final String MODIFICATION_XML_NAME = "/merge.xml";
+ private static final String XML_FOLDER_NAME = "/xmls";
+ public static final String RESULT_XML_NAME = "/result.xml";
+ private static final Object OPERATION_XML_NAME = "/defaultOperation.txt";
+
+ protected final ContainerSchemaNode containerNode;
+ protected final String testDirName;
+ protected final Optional<ContainerNode> currentConfig;
+ protected final Optional<ContainerNode> modification;
+ protected final ModifyAction modifyAction;
+
+ @Parameterized.Parameters()
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ // Container
+ { "/containerTest_createContainer" },
+ { "/containerTest_deleteContainer" },
+ { "/containerTest_innerContainerContainer" },
+ { "/containerTest_innerLeavesBaseOperationsContainer" },
+ { "/containerTest_noneContainer" },
+ { "/containerTest_removeContainer"},
+ { "/containerTest_replaceContainer"},
+ { "/containerTest_choiceActualModificationSameCase"},
+ { "/containerTest_choiceActualModificationDifferentCases"},
+ { "/containerTest_choiceActualOneCaseModificationOtherCase"},
+// LeafList
+ { "/leafListTest" },
+ // List
+ { "/listTest" },
+ // Additional
+ {"/none_NoChange"},
+ {"/listTest_alterInnerValue"}
+ });
+ }
+
+ public YangDataOperationsTest(String testDir) throws Exception {
+ SchemaContext schema = parseTestSchema();
+ containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+ this.testDirName = testDir;
+
+ currentConfig = loadXmlToCompositeNode(getXmlFolderName() + testDirName + CURRENT_XML_NAME);
+ modification = loadXmlToCompositeNode(getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
+ Preconditions.checkState(modification.isPresent(), "Modification xml has to be present under "
+ + getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
+
+ modifyAction = loadModifyAction(getXmlFolderName() + testDirName + OPERATION_XML_NAME);
+ }
+
+ protected String getXmlFolderName() {
+ return XML_FOLDER_NAME;
+ }
+
+ // TODO unite testing resources e.g. schemas with yang-data-impl
+ // TODO create extract common testing infrastructure from this and yang-data-impl e.g. xml dom handling
+
+ @Test
+ public void testModification() throws Exception {
+
+ Optional<ContainerNode> result = DataOperations.modify(containerNode,
+ currentConfig.orNull(), modification.orNull(), modifyAction);
+
+ String expectedResultXmlPath = getXmlFolderName() + testDirName + RESULT_XML_NAME;
+ Optional<ContainerNode> expectedResult = loadXmlToCompositeNode(expectedResultXmlPath);
+
+ if (result.isPresent()) {
+ verifyModificationResult(result, expectedResult);
+ } else {
+ junit.framework.Assert.assertNull("Result of modification is empty node, result xml should not be present "
+ + expectedResultXmlPath, getClass().getResourceAsStream(expectedResultXmlPath));
+ }
+
+ }
+
+ private ModifyAction loadModifyAction(String path) throws Exception {
+ URL resource = getClass().getResource(path);
+ if (resource == null) {
+ return ModifyAction.MERGE;
+ }
+
+ return ModifyAction.fromXmlValue(Files.toString(new File(resource.getFile()), Charsets.UTF_8).trim());
+ }
+
+ private void verifyModificationResult(Optional<ContainerNode> result, Optional<ContainerNode> expectedResult)
+ throws UnsupportedDataTypeException {
+ Assert.assertEquals(
+ String.format(
+ "Test result %n %s %n Expected %n %s %n",
+ toString(toDom(result.get())),
+ toString(toDom(expectedResult.get()))), expectedResult.get(), result.get());
+ }
+
+ private Element toDom(ContainerNode container) {
+ Iterable<Element> a =
+ DomFromNormalizedNodeSerializerFactory.getInstance(newDocument(), DomUtils.defaultValueCodecProvider())
+ .getContainerNodeSerializer().serialize(containerNode, container);
+ return a.iterator().next();
+ }
+
+ private Document newDocument() {
+ try {
+ return BUILDERFACTORY.newDocumentBuilder().newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ }
+
+ private Optional<ContainerNode> loadXmlToCompositeNode(String xmlPath) throws IOException, SAXException {
+ InputStream resourceAsStream = getClass().getResourceAsStream(xmlPath);
+ if (resourceAsStream == null) {
+ return Optional.absent();
+ }
+
+ Document currentConfigElement = readXmlToDocument(resourceAsStream);
+ Preconditions.checkNotNull(currentConfigElement);
+
+ return Optional.of(DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider()).getContainerNodeParser().parse(
+ Collections.singletonList(currentConfigElement.getDocumentElement()), containerNode));
+ }
+
+ SchemaContext parseTestSchema() {
+ YangParserImpl yangParserImpl = new YangParserImpl();
+ Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(getTestYangs());
+ return yangParserImpl.resolveSchemaContext(modules);
+ }
+
+ List<InputStream> getTestYangs() {
+
+ return Lists.newArrayList(Collections2.transform(Lists.newArrayList("/schemas/test.yang"),
+ new Function<String, InputStream>() {
+ @Override
+ public InputStream apply(String input) {
+ InputStream resourceAsStream = getClass().getResourceAsStream(input);
+ Preconditions.checkNotNull(resourceAsStream, "File %s was null", resourceAsStream);
+ return resourceAsStream;
+ }
+ }));
+ }
+
+ DataSchemaNode getSchemaNode(SchemaContext context, String moduleName, String childNodeName) {
+ for (Module module : context.getModules()) {
+ if (module.getName().equals(moduleName)) {
+ for (DataSchemaNode dataSchemaNode : module.getChildNodes()) {
+ if (dataSchemaNode.getQName().getLocalName().equals(childNodeName))
+ return dataSchemaNode;
+ }
+ }
+ }
+
+ throw new IllegalStateException("Unable to find child node " + childNodeName);
+ }
+
+ private static final DocumentBuilderFactory BUILDERFACTORY;
+
+ static {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ factory.setIgnoringElementContentWhitespace(true);
+ factory.setIgnoringComments(true);
+ BUILDERFACTORY = factory;
+ }
+
+ private Document readXmlToDocument(InputStream xmlContent) throws IOException, SAXException {
+ DocumentBuilder dBuilder;
+ try {
+ dBuilder = BUILDERFACTORY.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException("Failed to parse XML document", e);
+ }
+ Document doc = dBuilder.parse(xmlContent);
+
+ doc.getDocumentElement().normalize();
+ return doc;
+ }
+
+ public static String toString(Element xml) {
+ try {
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ StreamResult result = new StreamResult(new StringWriter());
+ DOMSource source = new DOMSource(xml);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+ throw new RuntimeException("Unable to serialize xml element " + xml, e);
+ }
+ }
+
+}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:test";
+ prefix "test";
+
+ organization "Cisco Systems, Inc.";
+
+ revision "2013-2-21" {
+ description
+ "Initial revision";
+ }
+
+ grouping listGroup {
+ list list {
+ key "uint32";
+
+ leaf uint32 {
+ type uint32;
+ }
+
+ container containerInList{
+ leaf uint32 {
+ type uint32;
+ }
+ leaf uint16 {
+ type uint16;
+ }
+ }
+ }
+ }
+
+ grouping innerContainerGrouping {
+ container innerContainer {
+ leaf uint16 {
+ type uint16;
+ }
+
+ container innerInnerContainer {
+
+ leaf uint16 {
+ type uint16;
+ }
+
+ leaf uint32 {
+ type uint32;
+ }
+ }
+ }
+ }
+
+ container container {
+ leaf uint32 {
+ type uint32;
+ }
+
+ leaf decimal64 {
+ type decimal64 {
+ fraction-digits 2;
+ }
+ }
+
+ leaf boolean {
+ type boolean;
+ }
+
+ leaf binary {
+ type binary;
+ }
+
+ leaf string {
+ type string;
+ }
+
+ uses listGroup;
+ uses innerContainerGrouping;
+
+ choice choice{}
+ choice choice2{}
+
+ leaf-list leafList {
+ type string;
+ }
+
+ leaf identityRef {
+ type identityref {
+ base test-identity;
+ }
+ }
+
+ /* TODO test modification with empty type
+ leaf empty {
+ type empty;
+ }
+ */
+ }
+
+ augment "/container/" {
+ leaf augumentUint32 {
+ type uint32;
+ }
+ }
+
+ augment "/container/choice/" {
+ case test-identity-augument {
+ when "/container/identityRef = 'test-identity'";
+ leaf augumentString1 {
+ type string;
+ }
+
+ leaf augumentInt1 {
+ type uint32;
+ }
+ }
+ case test-identity-augument2 {
+ when "/container/identityRef = 'test-identity2'";
+ leaf augumentString2 {
+ type string;
+ }
+
+ leaf augumentInt2 {
+ type uint32;
+ }
+ }
+ }
+
+ augment "/container/choice2/" {
+ case test-identity-augument {
+ when "/container/identityRef = 'test-identity'";
+ container augumentContainer {
+ leaf augumentStringInAugumentContainer {
+ type string;
+ }
+ }
+ }
+ case test-identity-augument2 {
+ when "/container/identityRef = 'test-identity2'";
+ list augumentedList {
+ leaf augumentStringInAugumentList {
+ type string;
+ }
+ }
+ }
+ }
+
+
+ identity test-identity {}
+ identity test-identity2 {
+ base test-identity;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <identityRef>test-identity2</identityRef>
+
+ <augumentedList>
+ <augumentStringInAugumentList>str1</augumentStringInAugumentList>
+ </augumentedList>
+
+ <augumentContainer>
+ </augumentContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="create">
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="delete">
+</container>
\ No newline at end of file
--- /dev/null
+none
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <!--container is not present and default value is NONE-->
+ <uint32 nc:operation="replace">88</uint32>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <leafList>currentLeafList1</leafList>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <leafList nc:operation="create">currentLeafList1</leafList>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+ <leafList nc:operation="delete">currentLeafList1</leafList>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>44</uint32>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+
+ <uint32 nc:operation="create">88</uint32>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="merge">
+
+ <uint32 nc:operation="delete">88</uint32>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ </list>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <list nc:operation="create">
+ <uint32>1</uint32>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ </list>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <list nc:operation="delete">
+ <uint32>2</uint32>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ </list>
+</container>
\ No newline at end of file
--- /dev/null
+none
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <list>
+ <uint32>2</uint32>
+ <containerInList nc:operation="create">
+ <uint32>88</uint32>
+ </containerInList>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity2</identityRef>
+ <augumentString2>augumentString2</augumentString2>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <augumentedList nc:operation="create">
+ <augumentStringInAugumentList>str1</augumentStringInAugumentList>
+ </augumentedList>
+
+ <augumentedList nc:operation="replace">
+ <augumentStringInAugumentList>str2</augumentStringInAugumentList>
+ </augumentedList>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity2</identityRef>
+
+ <augumentedList>
+ <augumentStringInAugumentList>str1</augumentStringInAugumentList>
+ </augumentedList>
+
+ <augumentedList>
+ <augumentStringInAugumentList>str2</augumentStringInAugumentList>
+ </augumentedList>
+
+ <augumentString2>augumentString2</augumentString2>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity</identityRef>
+ <augumentString1>augumentString1</augumentString1>
+ <augumentContainer>
+ <augumentStringInAugumentContainer>
+ case1
+ </augumentStringInAugumentContainer>
+ </augumentContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <augumentString1>augumentString1</augumentString1>
+
+ <augumentContainer>
+ <augumentStringInAugumentContainer nc:operation="delete">
+ </augumentStringInAugumentContainer>
+ </augumentContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity</identityRef>
+
+ <augumentString1>augumentString1</augumentString1>
+
+ <augumentContainer>
+ </augumentContainer>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity</identityRef>
+ <augumentContainer>
+ <augumentStringInAugumentContainer>
+ case1
+ </augumentStringInAugumentContainer>
+ </augumentContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <identityRef>test-identity2</identityRef>
+ <augumentedList nc:operation="create">
+ <augumentStringInAugumentList>str1</augumentStringInAugumentList>
+ </augumentedList>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <identityRef>test-identity2</identityRef>
+ <augumentedList>
+ <augumentStringInAugumentList>str1</augumentStringInAugumentList>
+ </augumentedList>
+</container>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="create">
+
+ <!--Inner operations under create are ignored-->
+ <uint32 nc:operation="replace">88</uint32>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>88</uint32>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete">
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <innerContainer>
+ <uint16>11</uint16>
+ <innerInnerContainer>
+ <uint32>11</uint32>
+ <uint16>11</uint16>
+ </innerInnerContainer>
+ </innerContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <innerContainer nc:operation="merge">
+ <!--TODO integer element has to contain value in order to be parsed even if its being removed-->
+ <uint16 nc:operation="remove">-1</uint16>
+ <innerInnerContainer nc:operation="replace">
+ <uint16>22</uint16>
+ </innerInnerContainer>
+ </innerContainer>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <innerContainer>
+ <innerInnerContainer>
+ <uint16>22</uint16>
+ </innerInnerContainer>
+ </innerContainer>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+ <augumentUint32>1</augumentUint32>
+ <string>currentString</string>
+ <decimal64>45.1</decimal64>
+ <binary>abcd</binary>
+
+ <identityRef>test-identity</identityRef>
+ <augumentString1>augumentString1</augumentString1>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <uint32 nc:operation="replace">88</uint32>
+ <augumentUint32>88</augumentUint32>
+ <decimal64 nc:operation="merge">88.1</decimal64>
+ <string nc:operation="delete"></string>
+ <binary nc:operation="remove"></binary>
+ <boolean nc:operation="create">false</boolean>
+
+ <identityRef>test-identity2</identityRef>
+ <augumentString2 nc:operation="create">augumentString2</augumentString2>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>88</uint32>
+ <augumentUint32>88</augumentUint32>
+ <decimal64>88.1</decimal64>
+ <identityRef>test-identity2</identityRef>
+ <boolean>false</boolean>
+ <augumentString2>augumentString2</augumentString2>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+ <string>currentString</string>
+ <decimal64>45.1</decimal64>
+</container>
\ No newline at end of file
--- /dev/null
+none
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <uint32 nc:operation="replace">88</uint32>
+ <decimal64 nc:operation="merge">88.1</decimal64>
+ <string>newString</string>
+ <binary >aaa</binary>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>88</uint32>
+ <string>currentString</string>
+ <decimal64>88.1</decimal64>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+</container>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="remove"
+ >
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+ <string>currentString</string>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"
+ nc:operation="replace">
+ <!--Inner strategies under replace are ignored-->
+ <uint32 nc:operation="remove">88</uint32>
+ <string nc:operation="delete">string</string>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>88</uint32>
+ <string>string</string>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+ <leafList>currentLeafList1</leafList>
+ <leafList>currentLeafList2</leafList>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <leafList nc:operation="delete">currentLeafList1</leafList>
+ <leafList nc:operation="remove">random</leafList>
+ <leafList nc:operation="create">currentLeafList3</leafList>
+ <leafList nc:operation="replace">currentLeafList4</leafList>
+ <leafList nc:operation="merge">currentLeafList5</leafList>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+ <leafList>currentLeafList2</leafList>
+ <leafList>currentLeafList3</leafList>
+ <leafList>currentLeafList4</leafList>
+ <leafList>currentLeafList5</leafList>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+
+ <list>
+ <uint32>1</uint32>
+ <containerInList>
+ <uint32>32</uint32>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>2</uint32>
+ <containerInList>
+ <uint32>32</uint32>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>6</uint32>
+ <containerInList>
+ <uint32>32</uint32>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>3</uint32>
+ </list>
+ <list>
+ <uint32>4</uint32>
+ </list>
+ <list>
+ <uint32>5</uint32>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <list nc:operation="merge">
+ <uint32>1</uint32>
+ <containerInList>
+ <uint32>88</uint32>
+ </containerInList>
+ </list>
+ <list nc:operation="merge">
+ <uint32>2</uint32>
+ <containerInList nc:operation="replace">
+ </containerInList>
+ </list>
+ <list nc:operation="delete">
+ <uint32>3</uint32>
+ </list>
+ <list nc:operation="remove">
+ <uint32>4</uint32>
+ </list>
+ <list>
+ <uint32>5</uint32>
+ <containerInList nc:operation="create">
+ <uint32>88</uint32>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>6</uint32>
+ <containerInList>
+ <uint32 nc:operation="delete">-1</uint32>
+ </containerInList>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ <containerInList>
+ <uint32>88</uint32>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>2</uint32>
+ <containerInList>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>6</uint32>
+ <containerInList>
+ </containerInList>
+ </list>
+ <list>
+ <uint32>5</uint32>
+ <containerInList>
+ <uint32>88</uint32>
+ </containerInList>
+ </list>
+
+</container>
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ <containerInList>
+ <uint32>32</uint32>
+ <uint16>32</uint16>
+ </containerInList>
+ </list>
+</container>
\ No newline at end of file
--- /dev/null
+none
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <list>
+ <uint32>1</uint32>
+ <containerInList>
+ <uint16 nc:operation="delete">88</uint16>
+ </containerInList>
+ </list>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <list>
+ <uint32>1</uint32>
+ <containerInList>
+ <uint32>32</uint32>
+ </containerInList>
+ </list>
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+ <string>currentString</string>
+ <decimal64>45.1</decimal64>
+</container>
\ No newline at end of file
--- /dev/null
+none
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test"
+ xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <uint32>88</uint32>
+ <decimal64>88.1</decimal64>
+ <string>newString</string>
+ <binary >aaa</binary>
+
+</container>
\ No newline at end of file
--- /dev/null
+<container xmlns="urn:opendaylight:params:xml:ns:yang:controller:test">
+ <uint32>1</uint32>
+ <string>currentString</string>
+ <decimal64>45.1</decimal64>
+</container>
\ No newline at end of file