Tolerate children not being present 30/99930/4
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 2 Mar 2022 08:15:03 +0000 (09:15 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 2 Mar 2022 12:41:52 +0000 (13:41 +0100)
Augment's children are subject to deviations at augment target, hence
we need to check for their presence. Also add some sorely-missing
documentation as to what this class actually does.

JIRA: YANGTOOLS-1404
Change-Id: I98cce6b2a8bff74658a16553f93eb8049dd038b7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
model/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/EffectiveAugmentationSchema.java
model/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1404Test.java [new file with mode: 0644]
model/yang-model-util/src/test/resources/yt1404/dev.yang [new file with mode: 0644]
model/yang-model-util/src/test/resources/yt1404/foo.yang [new file with mode: 0644]

index 0813e676d7b468933b9a4dba6f3b85ad77e800d9..5c4a8598a165e23b78dafbc7f999df6c32e00c2f 100644 (file)
@@ -32,9 +32,29 @@ import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression.QualifiedBound;
 
 /**
- * Proxy for AugmentationSchema. Child node schemas are replaced with actual schemas from parent.
+ * Proxy for AugmentationSchema. Child node schemas are replaced with actual schemas from parent. This is needed to
+ * correctly interpret constructs like this:
+ * <pre>
+ *   <code>
+ *     container foo;
+ *
+ *     augment /foo {
+ *       container bar;
+ *     }
+ *
+ *     augment /foo/bar {
+ *       container baz;
+ *     }
+ *   </code>
+ * </pre>
+ * The {@link AugmentationSchemaNode} returned for {@code augment /foo} contains bare {@code container bar}, e.g. it
+ * does not show {@code augment /foo/bar} as an available augmentation -- this is only visible in {@code foo}'s schema
+ * nodes.
+ *
+ * <p>
+ * Note this class only handles {@link DataSchemaNode}s, not all {@code schema tree} statements, as it strictly should.
  */
-// FIXME: 7.0.0: re-evaluate the need for this class and potentially its effective statement replacement
+// FIXME: YANGTOOLS-1403: this functionality should be integrated into EffectiveAugmentStatement/AugmentationSchemaNode
 public final class EffectiveAugmentationSchema implements AugmentationSchemaNode {
     private final AugmentationSchemaNode delegate;
     private final ImmutableSet<DataSchemaNode> realChildSchemas;
@@ -42,7 +62,7 @@ public final class EffectiveAugmentationSchema implements AugmentationSchemaNode
 
     public EffectiveAugmentationSchema(final AugmentationSchemaNode augmentSchema,
             final Collection<? extends DataSchemaNode> realChildSchemas) {
-        this.delegate = requireNonNull(augmentSchema);
+        delegate = requireNonNull(augmentSchema);
         this.realChildSchemas = ImmutableSet.copyOf(realChildSchemas);
 
         final Map<QName, DataSchemaNode> m = new HashMap<>(realChildSchemas.size());
@@ -50,7 +70,7 @@ public final class EffectiveAugmentationSchema implements AugmentationSchemaNode
             m.put(realChildSchema.getQName(), realChildSchema);
         }
 
-        this.mappedChildSchemas = ImmutableMap.copyOf(m);
+        mappedChildSchemas = ImmutableMap.copyOf(m);
     }
 
     /**
@@ -61,10 +81,16 @@ public final class EffectiveAugmentationSchema implements AugmentationSchemaNode
      * @return Adjusted Augmentation schema
      * @throws NullPointerException if any of the arguments is null
      */
+    // FIXME: 8.0.0: integrate this method into the constructor
     public static AugmentationSchemaNode create(final AugmentationSchemaNode schema, final DataNodeContainer parent) {
-        Set<DataSchemaNode> children = new HashSet<>();
+        final Set<DataSchemaNode> children = new HashSet<>();
         for (DataSchemaNode augNode : schema.getChildNodes()) {
-            children.add(parent.getDataChildByName(augNode.getQName()));
+            // parent may have the corresponding child removed via 'deviate unsupported', i.e. the child is effectively
+            // not present at the target site
+            final DataSchemaNode child = parent.dataChildByName(augNode.getQName());
+            if (child != null) {
+                children.add(child);
+            }
         }
         return new EffectiveAugmentationSchema(schema, children);
     }
diff --git a/model/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1404Test.java b/model/yang-model-util/src/test/java/org/opendaylight/yangtools/yang/model/util/YT1404Test.java
new file mode 100644 (file)
index 0000000..63e597f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.model.util;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import com.google.common.collect.Iterables;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class YT1404Test {
+    private static final QName FOO = QName.create("foo", "foo");
+    private static final QName BAR = QName.create("foo", "bar");
+    private static final QName BAZ = QName.create("foo", "baz");
+
+    @Test
+    public void testDeviatedEffectiveAugmentationSchema() {
+        final var module = YangParserTestUtils.parseYangResourceDirectory("/yt1404").findModule("foo").orElseThrow();
+        final var augment = Iterables.getOnlyElement(module.getAugmentations());
+        assertEquals(2, augment.getChildNodes().size());
+        assertThat(augment.dataChildByName(BAR), instanceOf(LeafSchemaNode.class));
+        assertThat(augment.dataChildByName(BAZ), instanceOf(LeafSchemaNode.class));
+
+        final var foo = module.getDataChildByName(FOO);
+        assertThat(foo, instanceOf(ContainerSchemaNode.class));
+        final var fooCont = (ContainerSchemaNode) foo;
+        assertEquals(1, fooCont.getChildNodes().size());
+        final var fooBar = fooCont.dataChildByName(BAR);
+        assertThat(fooBar, instanceOf(LeafSchemaNode.class));
+
+        final var fooAugment = Iterables.getOnlyElement(fooCont.getAvailableAugmentations());
+        assertSame(augment, fooAugment);
+
+        final var effectiveAug = EffectiveAugmentationSchema.create(augment, fooCont);
+        assertEquals(1, effectiveAug.getChildNodes().size());
+        assertSame(fooBar, effectiveAug.getDataChildByName(BAR));
+    }
+}
diff --git a/model/yang-model-util/src/test/resources/yt1404/dev.yang b/model/yang-model-util/src/test/resources/yt1404/dev.yang
new file mode 100644 (file)
index 0000000..30f50a5
--- /dev/null
@@ -0,0 +1,13 @@
+module dev {
+  namespace dev;
+  prefix dev;
+
+  import foo {
+    prefix foo;
+  }
+
+  deviation /foo:foo/foo:baz {
+    deviate not-supported;
+  }
+}
+
diff --git a/model/yang-model-util/src/test/resources/yt1404/foo.yang b/model/yang-model-util/src/test/resources/yt1404/foo.yang
new file mode 100644 (file)
index 0000000..93fdf34
--- /dev/null
@@ -0,0 +1,16 @@
+module foo {
+  namespace foo;
+  prefix foo;
+
+  container foo;
+
+  augment /foo {
+    leaf bar {
+      type string;
+    }
+    leaf baz {
+      type string;
+    }
+  }
+}
+