Bug 7945: Fix schema validation for augmentation of case 25/52025/4
authorXiao Liang <shaw.leon@gmail.com>
Fri, 17 Feb 2017 23:51:58 +0000 (07:51 +0800)
committerXiao Liang <shaw.leon@gmail.com>
Fri, 10 Mar 2017 01:22:41 +0000 (01:22 +0000)
Add modification strategy of case augmentation as child of choice.

Change-Id: Ie7b03f2d06ab279f0dd9c85edf9a370b0f63abac
Signed-off-by: Xiao Liang <shaw.leon@gmail.com>
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseEnforcer.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ChoiceModificationStrategy.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/case-augment-test.yang [new file with mode: 0644]

index 09d061197dc27e5c9ffca9afb963a95a340c8685..548bfb5f9d4fa3b5701fd4edcb2f074e3b57ce50 100644 (file)
@@ -10,40 +10,59 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.Sets;
+
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+
 import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 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 Map<AugmentationIdentifier, AugmentationSchema> augmentations;
     private final MandatoryLeafEnforcer enforcer;
 
-    private CaseEnforcer(final Map<NodeIdentifier, DataSchemaNode> children, final MandatoryLeafEnforcer enforcer) {
+    private CaseEnforcer(final Map<NodeIdentifier, DataSchemaNode> children,
+                         final Map<AugmentationIdentifier, AugmentationSchema> augmentations,
+                         final MandatoryLeafEnforcer enforcer) {
         this.children = Preconditions.checkNotNull(children);
+        this.augmentations = Preconditions.checkNotNull(augmentations);
         this.enforcer = Preconditions.checkNotNull(enforcer);
     }
 
     static CaseEnforcer forTree(final ChoiceCaseNode schema, final DataTreeConfiguration treeConfig) {
         final TreeType type = treeConfig.getTreeType();
-        final Builder<NodeIdentifier, DataSchemaNode> builder = ImmutableMap.builder();
+        final Builder<NodeIdentifier, DataSchemaNode> childrenBuilder = ImmutableMap.builder();
+        final Builder<AugmentationIdentifier, AugmentationSchema> augmentationsBuilder = ImmutableMap.builder();
         if (SchemaAwareApplyOperation.belongsToTree(type, schema)) {
             for (final DataSchemaNode child : schema.getChildNodes()) {
                 if (SchemaAwareApplyOperation.belongsToTree(type, child)) {
-                    builder.put(NodeIdentifier.create(child.getQName()), child);
+                    childrenBuilder.put(NodeIdentifier.create(child.getQName()), child);
+                }
+            }
+            for (final AugmentationSchema augment : schema.getAvailableAugmentations()) {
+                if (augment.getChildNodes().stream()
+                        .anyMatch(child -> SchemaAwareApplyOperation.belongsToTree(type, child))) {
+                    augmentationsBuilder.put(SchemaUtils.getNodeIdentifierForAugmentation(augment), augment);
                 }
             }
         }
 
-        final Map<NodeIdentifier, DataSchemaNode> children = builder.build();
-        return children.isEmpty() ? null : new CaseEnforcer(children, MandatoryLeafEnforcer.forContainer(schema,
-                treeConfig));
+        final Map<NodeIdentifier, DataSchemaNode> children = childrenBuilder.build();
+        final Map<AugmentationIdentifier, AugmentationSchema> augmentations = augmentationsBuilder.build();
+        return children.isEmpty() ? null
+                : new CaseEnforcer(children, augmentations, MandatoryLeafEnforcer.forContainer(schema, treeConfig));
     }
 
     Set<Entry<NodeIdentifier, DataSchemaNode>> getChildEntries() {
@@ -54,6 +73,18 @@ final class CaseEnforcer implements Immutable {
         return children.keySet();
     }
 
+    Set<Entry<AugmentationIdentifier, AugmentationSchema>> getAugmentationEntries() {
+        return augmentations.entrySet();
+    }
+
+    Set<AugmentationIdentifier> getAugmentationIdentifiers() {
+        return augmentations.keySet();
+    }
+
+    Set<PathArgument> getAllChildIdentifiers() {
+        return Sets.union(children.keySet(), augmentations.keySet());
+    }
+
     void enforceOnTreeNode(final NormalizedNode<?, ?> normalizedNode) {
         enforcer.enforceOnData(normalizedNode);
     }
index c991f71a767683d8b3a2ae4c39594294b3c2697f..6dab3184519449deb6210b3384cb4b08f8d90477 100644 (file)
@@ -21,6 +21,8 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
@@ -32,6 +34,7 @@ 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.ImmutableChoiceNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -54,6 +57,10 @@ final class ChoiceModificationStrategy extends AbstractNodeContainerModification
                     childBuilder.put(e.getKey(), SchemaAwareApplyOperation.from(e.getValue(), treeConfig));
                     enforcerBuilder.put(e.getKey(), enforcer);
                 }
+                for (final Entry<AugmentationIdentifier, AugmentationSchema> e : enforcer.getAugmentationEntries()) {
+                    childBuilder.put(e.getKey(), new AugmentationModificationStrategy(e.getValue(), caze, treeConfig));
+                    enforcerBuilder.put(e.getKey(), enforcer);
+                }
             }
         }
         childNodes = childBuilder.build();
@@ -101,7 +108,7 @@ final class ChoiceModificationStrategy extends AbstractNodeContainerModification
 
             // Make sure no leaves from other cases are present
             for (final CaseEnforcer other : exclusions.get(enforcer)) {
-                for (final NodeIdentifier id : other.getChildIdentifiers()) {
+                for (final PathArgument id : other.getAllChildIdentifiers()) {
                     final Optional<NormalizedNode<?, ?>> maybeChild = NormalizedNodes.getDirectChild(normalizedNode, id);
                     Preconditions.checkArgument(!maybeChild.isPresent(),
                         "Child %s (from case %s) implies non-presence of child %s (from case %s), which is %s",
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/CaseAugmentTest.java
new file mode 100644 (file)
index 0000000..282aa3c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017 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.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+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;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+public class CaseAugmentTest {
+
+    private SchemaContext schemaContext;
+    private final QName CHOICE1_QNAME = QName.create(TestModel.TEST_QNAME, "choice1");
+    private final QName C1L1_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf1");
+    private final QName C1L2_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf2");
+    private final QName C1L3_QNAME = QName.create(TestModel.TEST_QNAME, "case1-leaf3");
+    private final QName C2L1_QNAME = QName.create(TestModel.TEST_QNAME, "case2-leaf1");
+    private final NodeIdentifier CHOICE_ID = new NodeIdentifier(CHOICE1_QNAME);
+    private final AugmentationIdentifier AUGMENT_ID =
+            new AugmentationIdentifier(ImmutableSet.<QName>builder().add(C1L2_QNAME).add(C1L3_QNAME).build());
+
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = TestModel.createTestContext("/case-augment-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 testWriteAugment() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(augmentationNode)
+                                .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 testWriteCase1All() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .withChild(leafNode(C1L3_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders
+                .containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value"))
+                                .withChild(augmentationNode)
+                                .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(expected = IllegalArgumentException.class)
+    public void testWriteConflict() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree = initDataTree();
+
+        AugmentationNode augmentationNode = Builders.augmentationBuilder()
+                .withNodeIdentifier(AUGMENT_ID)
+                .withChild(leafNode(C1L2_QNAME, "leaf-value"))
+                .build();
+
+        final ContainerNode container = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        Builders.choiceBuilder().withNodeIdentifier(CHOICE_ID)
+                                .withChild(augmentationNode)
+                                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value"))
+                                .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) {
+            assertTrue(e.getMessage().contains("implies non-presence of child"));
+            throw e;
+        }
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/resources/case-augment-test.yang b/yang/yang-data-impl/src/test/resources/case-augment-test.yang
new file mode 100644 (file)
index 0000000..4ee9820
--- /dev/null
@@ -0,0 +1,33 @@
+module case-augment-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 {
+        choice choice1 {
+            case case1 {
+                leaf case1-leaf1 {
+                    type string;
+                }
+            }
+            case case2 {
+                leaf case2-leaf1 {
+                    type string;
+                }
+            }
+        }
+    }
+
+    augment "/test/choice1/case1" {
+        leaf case1-leaf2 {
+            type string;
+        }
+        leaf case1-leaf3 {
+            type string;
+        }
+    }
+}