Do not emit empty lists to NormalizedNodes 58/82558/7
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 18 Jun 2019 19:26:20 +0000 (21:26 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 16 Aug 2019 13:55:29 +0000 (13:55 +0000)
This patch changes the representation of empty Binding lists in
DOM world. Empty lists are equivalent to being non-present, hence
they are not emitted.

This mirrors the getFoo()/nonnullFoo() accessor duality, i.e. empty
lists can be instantiated on-demand as placeholders for nulls.

In DOM world, list nodes are virtual containers, which do not hold
any semantics and are created on demand -- hence it does not make
sense to pass them down to DOM.

This does not create data tree operation changes, as lists are not
directly addressable, hence their WRITE/MERGE semantics are shared
by their parent. Empty merges have not semantic value, as empty
lists disappear on touch. Empty writes end up correctly observing
lifecycle -- empty lists end up disappearing, just as they would
be expected if they have undergone a lifecycle event (such as an
empty merge).

JIRA: MDSAL-456
Change-Id: I78efc4aa4c3b4100ff52490fefe94e2f65f43efc
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/AugmentationSubstitutionTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/NormalizedNodeSerializeDeserializeTest.java
binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java

index 9e652a558aad8b8d9971f8d4d52978d3a661955c..691a1ca3372ccc5e6c04a91e2b1552e2f3824439 100644 (file)
@@ -140,8 +140,9 @@ public abstract class DataObjectStreamer<T extends DataObject> implements DataOb
     protected static final <E extends DataObject> void streamList(final Class<E> childClass,
             final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
             final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
-        if (value != null) {
-            writer.startUnkeyedList(childClass, value.size());
+        final int size = nullSize(value);
+        if (size != 0) {
+            writer.startUnkeyedList(childClass, size);
             commonStreamList(registry, writer, childStreamer, value);
         }
     }
@@ -149,8 +150,9 @@ public abstract class DataObjectStreamer<T extends DataObject> implements DataOb
     protected static final <E extends DataObject & Identifiable<?>> void streamMap(final Class<E> childClass,
             final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
             final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
-        if (value != null) {
-            writer.startMapNode(childClass, value.size());
+        final int size = nullSize(value);
+        if (size != 0) {
+            writer.startMapNode(childClass, size);
             commonStreamList(registry, writer, childStreamer, value);
         }
     }
@@ -158,8 +160,9 @@ public abstract class DataObjectStreamer<T extends DataObject> implements DataOb
     protected static final <E extends DataObject & Identifiable<?>> void streamOrderedMap(final Class<E> childClass,
             final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
             final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
-        if (value != null) {
-            writer.startOrderedMapNode(childClass, value.size());
+        final int size = nullSize(value);
+        if (size != 0) {
+            writer.startOrderedMapNode(childClass, size);
             commonStreamList(registry, writer, childStreamer, value);
         }
     }
@@ -208,4 +211,8 @@ public abstract class DataObjectStreamer<T extends DataObject> implements DataOb
     private static <T extends DataObject> boolean tryCache(final BindingStreamEventWriter writer, final T value) {
         return writer instanceof BindingSerializer ? ((BindingSerializer<?, T>) writer).serialize(value) == null : true;
     }
+
+    private static int nullSize(final List<?> list) {
+        return list == null ? 0 : list.size();
+    }
 }
index 144a5b4d9a0a26eb7e3b2a84e47289fb2e173898..6f3a2cb121a685542db41c6c6d1db9ec4bab1dce 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.mdsal.binding.dom.codec.test;
 
 import static org.junit.Assert.assertEquals;
 
-import java.util.Collections;
 import java.util.Optional;
 import org.junit.Test;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.RpcComplexUsesAugment;
@@ -74,7 +73,6 @@ public class AugmentationSubstitutionTest extends AbstractBindingCodecTest {
                 .setContainerWithUses(new ContainerWithUsesBuilder()
                     .setLeafFromGrouping("foo")
                     .build())
-                .setListViaUses(Collections.emptyList())
                 .build();
     }
 }
index 731ce67c7037123786c6cd326532848b7c77c727..a5ed9879fb881e0d513533fc166a905a06c9a2de 100644 (file)
@@ -137,7 +137,7 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
     private static ContainerNode getEmptyTop() {
         return ImmutableContainerNodeBuilder.create()
                     .withNodeIdentifier(new NodeIdentifier(TOP_QNAME))
-                    .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build();
+                    .build();
     }
 
     private static final QName AGUMENT_STRING_Q = QName.create(TOP_QNAME, "augmented-string");
index f2e72f76cfa5d3321f7e3f75e04e680a5b16ef33..c3d0490cc5027f31c5911906b4bdb892150f3c0a 100644 (file)
@@ -58,6 +58,10 @@ public final class ListsBindingUtils {
         return path(key).augmentation(augmentation);
     }
 
+    public static Top top() {
+        return new TopBuilder().build();
+    }
+
     public static Top top(final TopLevelList... listItems) {
         return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build();
     }