import java.util.Set;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
final class CaseEnforcer implements Immutable {
private final Map<NodeIdentifier, DataSchemaNode> children;
+ private final MandatoryLeafEnforcer enforcer;
- private CaseEnforcer(final Map<NodeIdentifier, DataSchemaNode> children) {
+ private CaseEnforcer(final Map<NodeIdentifier, DataSchemaNode> children, final MandatoryLeafEnforcer enforcer) {
this.children = Preconditions.checkNotNull(children);
+ this.enforcer = Preconditions.checkNotNull(enforcer);
}
- static CaseEnforcer forTree(final ChoiceCaseNode schema, final TreeType treeType) {
+ static CaseEnforcer forTree(final ChoiceCaseNode schema, final TreeType type) {
final Builder<NodeIdentifier, DataSchemaNode> builder = ImmutableMap.builder();
- if (SchemaAwareApplyOperation.belongsToTree(treeType, schema)) {
+ if (SchemaAwareApplyOperation.belongsToTree(type, schema)) {
for (DataSchemaNode child : schema.getChildNodes()) {
- if (SchemaAwareApplyOperation.belongsToTree(treeType, child)) {
+ if (SchemaAwareApplyOperation.belongsToTree(type, child)) {
builder.put(NodeIdentifier.create(child.getQName()), child);
}
}
}
final Map<NodeIdentifier, DataSchemaNode> children = builder.build();
- return children.isEmpty() ? null : new CaseEnforcer(children);
+ return children.isEmpty() ? null : new CaseEnforcer(children, MandatoryLeafEnforcer.forContainer(schema, type));
}
Set<Entry<NodeIdentifier, DataSchemaNode>> getChildEntries() {
Set<NodeIdentifier> getChildIdentifiers() {
return children.keySet();
}
+
+ void enforceOnTreeNode(final TreeNode tree) {
+ enforcer.enforceOnTreeNode(tree);
+ }
+
+ void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode) {
+ enforcer.enforceOnTreeNode(normalizedNode);
+ }
}
firstChild.getIdentifier(), enforcer, id, other, maybeChild.orNull());
}
}
+
+ // Make sure all mandatory children are present
+ enforcer.enforceOnTreeNode(normalizedNode);
}
}
--- /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.impl.schema.tree;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+
+/**
+ * General container modification strategy. Used by {@link PresenceContainerModificationStrategy} via subclassing
+ * and by {@link StructuralContainerModificationStrategy} as a delegate.
+ */
+class ContainerModificationStrategy extends AbstractDataNodeContainerModificationStrategy<ContainerSchemaNode> {
+ ContainerModificationStrategy(final ContainerSchemaNode schemaNode, final TreeType treeType) {
+ super(schemaNode, ContainerNode.class, treeType);
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ protected final DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
+ checkArgument(original instanceof ContainerNode);
+ return ImmutableContainerNodeBuilder.create((ContainerNode) original);
+ }
+}
final ModificationApplyOperation rootNode;
if (rootSchemaNode instanceof ContainerSchemaNode) {
- rootNode = new PresenceContainerModificationStrategy((ContainerSchemaNode) rootSchemaNode, treeType);
+ // FIXME: real root needs to enfore presence, but that require pre-population
+ rootNode = new ContainerModificationStrategy((ContainerSchemaNode) rootSchemaNode, treeType);
} else {
rootNode = SchemaAwareApplyOperation.from(rootSchemaNode, treeType);
}
package org.opendaylight.yangtools.yang.data.impl.schema.tree;
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.base.Optional;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
final class ListEntryModificationStrategy extends AbstractDataNodeContainerModificationStrategy<ListSchemaNode> {
+ private final MandatoryLeafEnforcer enforcer;
+
ListEntryModificationStrategy(final ListSchemaNode schema, final TreeType treeType) {
super(schema, MapEntryNode.class, treeType);
+ enforcer = MandatoryLeafEnforcer.forContainer(schema, treeType);
+ }
+
+ @Override
+ protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+ final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
+ }
+
+ @Override
+ protected TreeNode applyWrite(final ModifiedNode modification, final Optional<TreeNode> currentMeta,
+ final Version version) {
+ final TreeNode ret = super.applyWrite(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
+ }
+
+ @Override
+ protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+ final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
}
@Override
--- /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.impl.schema.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection.Builder;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.TreeType;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: would making this Serializable be useful (for Functions and similar?)
+abstract class MandatoryLeafEnforcer implements Immutable {
+ private static final class NoOp extends MandatoryLeafEnforcer {
+ @Override
+ protected void enforceOnTreeNode(final TreeNode tree) {
+ // Intentional no-op
+ }
+
+ @Override
+ protected void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode) {
+ // Intentional no-op
+ }
+ };
+ private static final class Strict extends MandatoryLeafEnforcer {
+ private final Collection<YangInstanceIdentifier> mandatoryNodes;
+
+ private Strict(final Collection<YangInstanceIdentifier> mandatoryNodes) {
+ this.mandatoryNodes = Preconditions.checkNotNull(mandatoryNodes);
+ }
+
+ @Override
+ protected void enforceOnTreeNode(final TreeNode tree) {
+ enforceOnTreeNode(tree.getData());
+ }
+
+ @Override
+ protected void enforceOnTreeNode(final NormalizedNode<?, ?> data) {
+ for (YangInstanceIdentifier id : mandatoryNodes) {
+ final Optional<NormalizedNode<?, ?>> descandant = NormalizedNodes.findNode(data, id);
+ Preconditions.checkArgument(descandant.isPresent(), "Node %s is missing mandatory descendant %s",
+ data.getIdentifier(), id);
+ }
+ }
+ };
+
+ private static final Logger LOG = LoggerFactory.getLogger(MandatoryLeafEnforcer.class);
+ private static final MandatoryLeafEnforcer NOOP_ENFORCER = new NoOp();
+
+ protected abstract void enforceOnTreeNode(final TreeNode tree);
+
+ protected abstract void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode);
+
+ private static void findMandatoryNodes(final Builder<YangInstanceIdentifier> builder,
+ final YangInstanceIdentifier id, final DataNodeContainer schema, final TreeType type) {
+ for (DataSchemaNode child : schema.getChildNodes()) {
+ if (SchemaAwareApplyOperation.belongsToTree(type, child)) {
+ if (child instanceof ContainerSchemaNode) {
+ final ContainerSchemaNode container = (ContainerSchemaNode) child;
+ if (!container.isPresenceContainer()) {
+ findMandatoryNodes(builder, id.node(NodeIdentifier.create(child.getQName())), container, type);
+ }
+ } else {
+ final ConstraintDefinition constraints = child.getConstraints();
+ final Integer minElements = constraints.getMinElements();
+ if (constraints.isMandatory() || (minElements != null && minElements > 0)) {
+ final YangInstanceIdentifier childId = id.node(NodeIdentifier.create(child.getQName()));
+ LOG.debug("Adding mandatory child {}", childId);
+ builder.add(childId.toOptimized());
+ }
+ }
+ }
+ }
+ }
+
+ static MandatoryLeafEnforcer forContainer(final DataNodeContainer schema, final TreeType type) {
+ switch (type) {
+ case CONFIGURATION:
+ final Builder<YangInstanceIdentifier> builder = ImmutableList.builder();
+ findMandatoryNodes(builder, YangInstanceIdentifier.EMPTY, schema, type);
+ final Collection<YangInstanceIdentifier> mandatoryNodes = builder.build();
+ return mandatoryNodes.isEmpty() ? NOOP_ENFORCER : new Strict(mandatoryNodes);
+ case OPERATIONAL:
+ return NOOP_ENFORCER;
+ default:
+ throw new UnsupportedOperationException(String.format("Not supported tree type %s", type));
+ }
+ }
+}
package org.opendaylight.yangtools.yang.data.impl.schema.tree;
-import static com.google.common.base.Preconditions.checkArgument;
-
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import com.google.common.base.Optional;
import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-final class PresenceContainerModificationStrategy extends
- AbstractDataNodeContainerModificationStrategy<ContainerSchemaNode> {
+/**
+ * Presence container modification strategy. In addition to {@link ContainerModificationStrategy} it also enforces
+ * presence of mandatory leaves.
+ */
+final class PresenceContainerModificationStrategy extends ContainerModificationStrategy {
+ private final MandatoryLeafEnforcer enforcer;
+
PresenceContainerModificationStrategy(final ContainerSchemaNode schemaNode, final TreeType treeType) {
- super(schemaNode, ContainerNode.class, treeType);
+ super(schemaNode, treeType);
+ enforcer = MandatoryLeafEnforcer.forContainer(schemaNode, treeType);
+ }
+
+ @Override
+ protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+ final TreeNode ret = super.applyMerge(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
+ }
+
+ @Override
+ protected TreeNode applyWrite(final ModifiedNode modification, final Optional<TreeNode> currentMeta,
+ final Version version) {
+ final TreeNode ret = super.applyWrite(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
}
@Override
- @SuppressWarnings("rawtypes")
- protected DataContainerNodeBuilder createBuilder(final NormalizedNode<?, ?> original) {
- checkArgument(original instanceof ContainerNode);
- return ImmutableContainerNodeBuilder.create((ContainerNode) original);
+ protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
+ final TreeNode ret = super.applyTouch(modification, currentMeta, version);
+ enforcer.enforceOnTreeNode(ret);
+ return ret;
}
-}
\ No newline at end of file
+}
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
/**
- * Structural containers are special in that they appear when implied by child
- * nodes and disappear whenever they are empty. We could implement this as a
- * subclass of {@link SchemaAwareApplyOperation}, but the automatic semantic
- * is quite different from all the other strategies. We create a
- * {@link PresenceContainerModificationStrategy} to tap into that logic, but
- * wrap it so we only call out into it
+ * Structural containers are special in that they appear when implied by child nodes and disappear whenever they are
+ * empty. We could implement this as a subclass of {@link SchemaAwareApplyOperation}, but the automatic semantic
+ * is quite different from all the other strategies. We create a {@link ContainerModificationStrategy} to tap into that
+ * logic, but wrap it so we only call out into it. We do not use {@link PresenceContainerModificationStrategy} because
+ * it enforces presence of mandatory leaves, which is not something we want here, as structural containers are not
+ * root anchors for that validation.
*/
final class StructuralContainerModificationStrategy extends ModificationApplyOperation {
/**
* {@link #apply(ModifiedNode, Optional, Version)} we will use the appropriate version as provided to us.
*/
private static final Version FAKE_VERSION = Version.initial();
- private final PresenceContainerModificationStrategy delegate;
+ private final ContainerModificationStrategy delegate;
StructuralContainerModificationStrategy(final ContainerSchemaNode schemaNode, final TreeType treeType) {
- this.delegate = new PresenceContainerModificationStrategy(schemaNode, treeType);
+ this.delegate = new ContainerModificationStrategy(schemaNode, treeType);
}
private Optional<TreeNode> fakeMeta(final Version version) {
--- /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.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class MandatoryLeafTest {
+
+ private SchemaContext schemaContext;
+
+ @Before
+ public void prepare() throws ReactorException {
+ schemaContext = RetestModel.createTestContext("/mandatory-leaf-test.yang");
+ assertNotNull("Schema context must not be null.", schemaContext);
+ }
+
+ private InMemoryDataTree initDataTree() {
+ InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
+ TreeType.CONFIGURATION);
+ inMemoryDataTree.setSchemaContext(schemaContext);
+ return inMemoryDataTree;
+ }
+
+ @Test
+ public void testCorrectMandatoryLeafWrite() throws DataValidationFailedException {
+ final InMemoryDataTree inMemoryDataTree = initDataTree();
+ final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+ final ContainerNode container = Builders
+ .containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+ .withChild(
+ Builders.choiceBuilder()
+ .withNodeIdentifier(choice1Id)
+ .withChild(
+ Builders.containerBuilder()
+ .withNodeIdentifier(
+ new NodeIdentifier(QName.create(TestModel.TEST_QNAME,
+ "case2-cont")))
+ .withChild(
+ leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"),
+ "leaf-value"))
+ .withChild(
+ leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"),
+ "leaf-value2")).build()).build()).build();
+
+ final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree.write(TestModel.TEST_PATH, container);
+ modificationTree.ready();
+
+ inMemoryDataTree.validate(modificationTree);
+ final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+ inMemoryDataTree.commit(prepare);
+ }
+
+ @Test
+ public void testCorrectMandatoryLeafChoiceWrite() throws DataValidationFailedException {
+ final InMemoryDataTree inMemoryDataTree = initDataTree();
+ // Container write
+ final ContainerNode container = Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+ final InMemoryDataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree1.write(TestModel.TEST_PATH, container);
+ modificationTree1.ready();
+
+ inMemoryDataTree.validate(modificationTree1);
+ final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+ inMemoryDataTree.commit(prepare1);
+
+ // Choice write
+ final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+ final ChoiceNode choice = Builders
+ .choiceBuilder()
+ .withNodeIdentifier(choice1Id)
+ .withChild(
+ Builders.containerBuilder()
+ .withNodeIdentifier(
+ new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+ .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value"))
+ .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"), "leaf-value2"))
+ .build()).build();
+
+ final InMemoryDataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+ modificationTree2.ready();
+
+ inMemoryDataTree.validate(modificationTree2);
+ final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+ inMemoryDataTree.commit(prepare2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMandatoryLeafViolation() throws DataValidationFailedException {
+ final InMemoryDataTree inMemoryDataTree = initDataTree();
+ final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+
+ final ContainerNode container = Builders
+ .containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+ .withChild(
+ Builders.choiceBuilder()
+ .withNodeIdentifier(choice1Id)
+ .withChild(
+ Builders.containerBuilder()
+ .withNodeIdentifier(
+ new NodeIdentifier(QName.create(TestModel.TEST_QNAME,
+ "case2-cont")))
+ .withChild(
+ leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"),
+ "leaf-value2")).build()).build()).build();
+ try {
+ final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree.write(TestModel.TEST_PATH, container);
+ modificationTree.ready();
+
+ inMemoryDataTree.validate(modificationTree);
+ final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+ inMemoryDataTree.commit(prepare);
+ } catch (IllegalArgumentException e) {
+ assertEquals(
+ "Node (urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?revision=2014-03-13)choice1 is missing mandatory descendant /(urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?revision=2014-03-13)case2-cont/case2-leaf1",
+ e.getMessage());
+ throw e;
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMandatoryLeafViolationChoiceWrite() throws DataValidationFailedException {
+ final InMemoryDataTree inMemoryDataTree = initDataTree();
+ // Container write
+ final ContainerNode container = Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build();
+
+ final InMemoryDataTreeModification modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree1.write(TestModel.TEST_PATH, container);
+ modificationTree1.ready();
+
+ inMemoryDataTree.validate(modificationTree1);
+ final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+ inMemoryDataTree.commit(prepare1);
+
+ // Choice write
+ final NodeIdentifier choice1Id = new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "choice1"));
+ final ChoiceNode choice = Builders
+ .choiceBuilder()
+ .withNodeIdentifier(choice1Id)
+ .withChild(
+ Builders.containerBuilder()
+ .withNodeIdentifier(
+ new NodeIdentifier(QName.create(TestModel.TEST_QNAME, "case2-cont")))
+ .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf2"), "leaf-value2"))
+ .build()).build();
+
+ try {
+ final InMemoryDataTreeModification modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
+ modificationTree2.write(TestModel.TEST_PATH.node(choice1Id), choice);
+ modificationTree2.ready();
+ inMemoryDataTree.validate(modificationTree2);
+ final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+ inMemoryDataTree.commit(prepare2);
+ } catch (IllegalArgumentException e) {
+ assertEquals(
+ "Node (urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?revision=2014-03-13)choice1 is missing mandatory descendant /(urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test?revision=2014-03-13)case2-cont/case2-leaf1",
+ e.getMessage());
+ throw e;
+ }
+ }
+}
}
container master-container {
- presence true;
list min-max-list {
min-elements 2;
max-elements 3;
--- /dev/null
+module mandatory-leaf-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+ prefix "store-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ container test {
+ presence true;
+ config true;
+ choice choice1 {
+ case case1 {
+ leaf case1-leaf1 {
+ type string;
+ }
+ }
+ case case2 {
+ container case2-cont {
+ leaf case2-leaf1 {
+ mandatory true;
+ type string;
+ }
+ leaf case2-leaf2 {
+ type string;
+ }
+ }
+ }
+ }
+
+ list outer-list {
+
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+
+ list inner-list2 {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+ }
+}