Clean up GeneratedProperty use 62/89262/2
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 22 Apr 2020 10:25:20 +0000 (12:25 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 22 Apr 2020 11:07:01 +0000 (13:07 +0200)
Previous patch made a bit of a mess in GeneratedProperty. Clean this
up by creating a dedicated BuilderGeneratedProperty to hold the
properties created in java-api-generator.

Also add a few dedicated tests to ensure generated builders what
they are supposed to do.

JIRA: MDSAL-451
Change-Id: I594475019a997277560f77d4ef51144f2b281ffe
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
15 files changed:
binding/mdsal-binding-generator-api/src/main/java/org/opendaylight/mdsal/binding/model/api/GeneratedProperty.java
binding/mdsal-binding-generator-api/src/main/java/org/opendaylight/mdsal/binding/model/api/MethodSignature.java
binding/mdsal-binding-generator-api/src/main/java/org/opendaylight/mdsal/binding/model/api/type/builder/GeneratedPropertyBuilder.java
binding/mdsal-binding-generator-util/src/main/java/org/opendaylight/mdsal/binding/model/util/generated/type/builder/GeneratedPropertyBuilderImpl.java
binding/mdsal-binding-generator-util/src/main/java/org/opendaylight/mdsal/binding/model/util/generated/type/builder/GeneratedPropertyImpl.java
binding/mdsal-binding-generator-util/src/main/java/org/opendaylight/mdsal/binding/model/util/generated/type/builder/MethodSignatureBuilderImpl.java
binding/mdsal-binding-generator-util/src/main/java/org/opendaylight/mdsal/binding/model/util/generated/type/builder/MethodSignatureImpl.java
binding/mdsal-binding-generator-util/src/test/java/org/opendaylight/mdsal/binding/model/util/generated/type/builder/GeneratedPropertyTest.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratedProperty.java [new file with mode: 0644]
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend
binding/mdsal-binding-test-model/src/test/java/org/opendaylight/mdsal/binding/test/model/TestListSquashing.java [new file with mode: 0644]

index 011e7ad6121a1c29a0e6aa9dd78c2bcf8750f195..72b6724bfae3a451a2122b00649ec90fb1a8f936 100644 (file)
@@ -16,8 +16,6 @@ package org.opendaylight.mdsal.binding.model.api;
 // FIXME: 7.0.0: this interface (and others) need to be refactored:
 //               - getValue() is pretty much unused and its semantics are undefined
 //               - isReadOnly() is not related to getValue() and is not used together
-//               - nullifyEmpty() is applicable only to collection types and implies non-read-only and without value
-//               - this is misused by Builder spec :(
 public interface GeneratedProperty extends TypeMember {
 
     String getValue();
@@ -29,12 +27,4 @@ public interface GeneratedProperty extends TypeMember {
      * @return {@code true<} if the property is declared as read-only.
      */
     boolean isReadOnly();
-
-    /**
-     * Returns indication whether the value should be squashed from empty collection to a null. This property is valid
-     * only if {@link #getReturnType()} results in a well-known collection type: List or Map.
-     *
-     * @return True if empty collections should be turned to nulls
-     */
-    boolean nullifyEmpty();
 }
index 512213b556dd9be05ee000b3ed3214000078bb9f..be0e9e928362c58166ade066a638e1ba8415eb07 100644 (file)
@@ -53,7 +53,7 @@ public interface MethodSignature extends TypeMember {
      *
      * @return Associated mechanics
      */
-    ValueMechanics getMechanics();
+    @NonNull ValueMechanics getMechanics();
 
     /**
      * The Parameter interface is designed to hold the information of method
index 79bedba9b35db5d4fecf1e044fd60fba95be59ea..114e37b65157cb381075cc1a9529178509b12ec6 100644 (file)
@@ -7,7 +7,6 @@
  */
 package org.opendaylight.mdsal.binding.model.api.type.builder;
 
-import com.google.common.annotations.Beta;
 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
 import org.opendaylight.mdsal.binding.model.api.Type;
 
@@ -28,9 +27,6 @@ public interface GeneratedPropertyBuilder extends TypeMemberBuilder<GeneratedPro
      */
     GeneratedPropertyBuilder setReadOnly(boolean isReadOnly);
 
-    @Beta
-    GeneratedPropertyBuilder setNullifyEmpty(boolean flag);
-
     /**
      * Returns <code>new</code> <i>immutable</i> instance of Generated Property. <br>
      * The <code>definingType</code> param cannot be <code>null</code>. The
index 5c9665e086a7269f4abb38a34b058d340c6e3c2c..07e5492297d211a5ddf3448461a49444f1af6d93 100644 (file)
@@ -17,12 +17,10 @@ public final class GeneratedPropertyBuilderImpl extends AbstractTypeMemberBuilde
         implements GeneratedPropertyBuilder {
     private String value;
     private boolean readOnly;
-    private boolean nullifyEmpty;
 
     public GeneratedPropertyBuilderImpl(final String name) {
         super(name);
         this.readOnly = true;
-        this.nullifyEmpty = false;
     }
 
     @Override
@@ -37,12 +35,6 @@ public final class GeneratedPropertyBuilderImpl extends AbstractTypeMemberBuilde
         return this;
     }
 
-    @Override
-    public GeneratedPropertyBuilderImpl setNullifyEmpty(final boolean flag) {
-        this.nullifyEmpty = flag;
-        return this;
-    }
-
     @Override
     protected GeneratedPropertyBuilderImpl thisInstance() {
         return this;
@@ -52,7 +44,7 @@ public final class GeneratedPropertyBuilderImpl extends AbstractTypeMemberBuilde
     public GeneratedProperty toInstance(final Type definingType) {
         final List<AnnotationType> annotations = toAnnotationTypes();
         return new GeneratedPropertyImpl(definingType, getName(), annotations, getComment(), getAccessModifier(),
-                getReturnType(), isFinal(), isStatic(), this.readOnly, this.nullifyEmpty, this.value);
+                getReturnType(), isFinal(), isStatic(), this.readOnly, this.value);
     }
 
     @Override
index 24f81399f1dcbaaedeaa5de474f7a6615bddd42f..c83aefb7a6f5c9f2ef5871170fa3260b91ae68b2 100644 (file)
@@ -16,15 +16,13 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 final class GeneratedPropertyImpl extends AbstractTypeMember implements GeneratedProperty {
     private final String value;
     private final boolean readOnly;
-    private final boolean nullifyEmpty;
 
     GeneratedPropertyImpl(final Type definingType, final String name, final List<AnnotationType> annotations,
             final String comment, final AccessModifier accessModifier, final Type returnType, final boolean isFinal,
-            final boolean isStatic, final boolean isReadOnly, final boolean nullifyEmpty, final String value) {
+            final boolean isStatic, final boolean isReadOnly, final String value) {
         super(definingType, name, annotations, comment, accessModifier, returnType, isFinal, isStatic);
         this.value = value;
         this.readOnly = isReadOnly;
-        this.nullifyEmpty = nullifyEmpty;
     }
 
     @Override
@@ -37,11 +35,6 @@ final class GeneratedPropertyImpl extends AbstractTypeMember implements Generate
         return this.readOnly;
     }
 
-    @Override
-    public boolean nullifyEmpty() {
-        return this.nullifyEmpty;
-    }
-
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
index 1eff5819533bbe4fd1f68229f9814545ad7d7bb2..fa60f84a922f9358562183acc41b1ba94334f277 100644 (file)
@@ -44,7 +44,6 @@ final class MethodSignatureBuilderImpl extends AbstractTypeMemberBuilder<MethodS
         return this;
     }
 
-
     @Override
     public MethodSignatureBuilder setMechanics(final ValueMechanics newMechanics) {
         this.mechanics = requireNonNull(newMechanics);
index 5a211b9d415333ed69b914b3c11aad70f63179b6..4a0ac73a02493d9c4e14d18f6fc356ae6d4fac81 100644 (file)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.VisibleForTesting;
 import java.util.List;
 import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
 import org.opendaylight.mdsal.binding.model.api.AnnotationType;
 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
@@ -19,7 +20,7 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 
 class MethodSignatureImpl extends AbstractTypeMember implements MethodSignature {
     private final List<Parameter> params;
-    private final ValueMechanics mechanics;
+    private final @NonNull ValueMechanics mechanics;
     private final boolean isAbstract;
     private final boolean isDefault;
 
index eba0b9b297108345c2802d5fddd27a217e8ca347..5e3ef4e37f58fb07549aab8ef57e91a82b3a9344 100644 (file)
@@ -33,7 +33,7 @@ public class GeneratedPropertyTest {
     @Test
     public void testMethodsForGeneratedPropertyImpl() {
         final GeneratedPropertyImpl propertyImpl = new GeneratedPropertyImpl(null, "Test", null, "test property",
-            AccessModifier.PRIVATE, null, true, true, true, true, "test value");
+            AccessModifier.PRIVATE, null, true, true, true, "test value");
 
         assertEquals("test value", propertyImpl.getValue());
         assertTrue(propertyImpl.isReadOnly());
index 11e8629f95cd05a78fbddea37921cc4708b891ba..278abce291aee4a8c100679688e4da55b5ea91cc 100644 (file)
@@ -36,7 +36,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
     /**
      * Set of class attributes (fields) which are derived from the getter methods names.
      */
-    protected val Set<GeneratedProperty> properties
+    protected val Set<BuilderGeneratedProperty> properties
 
     /**
      * GeneratedType for key type, null if this type does not have a key.
@@ -46,7 +46,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
     protected val GeneratedType targetType;
 
     new(AbstractJavaGeneratedType javaType, GeneratedType type, GeneratedType targetType,
-            Set<GeneratedProperty> properties, Type augmentType, Type keyType) {
+            Set<BuilderGeneratedProperty> properties, Type augmentType, Type keyType) {
         super(javaType, type)
         this.targetType = targetType
         this.properties = properties
@@ -54,7 +54,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
         this.keyType = keyType
     }
 
-    new(GeneratedType type, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+    new(GeneratedType type, GeneratedType targetType, Set<BuilderGeneratedProperty> properties, Type augmentType,
             Type keyType) {
         super(type)
         this.targetType = targetType
@@ -87,7 +87,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
         '''
     }
 
-    override generateToString(Collection<GeneratedProperty> properties) '''
+    override generateToString(Collection<? extends GeneratedProperty> properties) '''
         «IF properties !== null»
             @«OVERRIDE.importedName»
             public «STRING.importedName» toString() {
@@ -154,7 +154,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
 
     def protected abstract CharSequence generateCopyKeys(List<GeneratedProperty> keyProps)
 
-    def protected abstract CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props)
+    def protected abstract CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props)
 
     def protected abstract CharSequence generateCopyAugmentation(Type implType)
 
@@ -169,7 +169,7 @@ abstract class AbstractBuilderTemplate extends BaseTemplate {
         return false;
     }
 
-    private def void removeProperty(Collection<GeneratedProperty> props, String name) {
+    private def void removeProperty(Collection<BuilderGeneratedProperty> props, String name) {
         val iter = props.iterator
         while (iter.hasNext) {
             if (name.equals(iter.next.name)) {
index d5c2c174634bd98b6720ed64131fab5455d1d48c..a17d9c7ca33927238d94ba3549b815928d4c52f6 100644 (file)
@@ -401,7 +401,7 @@ abstract class BaseTemplate extends JavaFileTemplate {
         return sb.append(lineBuilder).append(NEW_LINE).toString
     }
 
-    def protected generateToString(Collection<GeneratedProperty> properties) '''
+    def protected generateToString(Collection<? extends GeneratedProperty> properties) '''
         «IF !properties.empty»
             @«OVERRIDE.importedName»
             public «STRING.importedName» toString() {
@@ -474,7 +474,7 @@ abstract class BaseTemplate extends JavaFileTemplate {
        «ENDFOR»
     '''
 
-    def protected hashCodeResult(Collection<GeneratedProperty> properties) '''
+    def protected hashCodeResult(Collection<? extends GeneratedProperty> properties) '''
         final int prime = 31;
         int result = 1;
         «FOR property : properties»
diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratedProperty.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratedProperty.java
new file mode 100644 (file)
index 0000000..46c18e0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 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.mdsal.binding.java.api.generator;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.opendaylight.mdsal.binding.model.api.AccessModifier;
+import org.opendaylight.mdsal.binding.model.api.AnnotationType;
+import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
+import org.opendaylight.mdsal.binding.model.api.Type;
+
+final class BuilderGeneratedProperty implements GeneratedProperty {
+    private final MethodSignature method;
+    private final String name;
+
+    BuilderGeneratedProperty(final String name, final MethodSignature method) {
+        this.name = requireNonNull(name);
+        this.method = requireNonNull(method);
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Type getReturnType() {
+        return method.getReturnType();
+    }
+
+    ValueMechanics getMechanics() {
+        return method.getMechanics();
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof BuilderGeneratedProperty)) {
+            return false;
+        }
+        final BuilderGeneratedProperty other = (BuilderGeneratedProperty) obj;
+        return name.equals(other.name) && method.equals(other.method);
+    }
+
+    @Override
+    public String getComment() {
+        throw uoe();
+    }
+
+    @Override
+    public List<AnnotationType> getAnnotations() {
+        throw uoe();
+    }
+
+    @Override
+    public AccessModifier getAccessModifier() {
+        throw uoe();
+    }
+
+    @Override
+    public boolean isStatic() {
+        throw uoe();
+    }
+
+    @Override
+    public boolean isFinal() {
+        throw uoe();
+    }
+
+    @Override
+    public Type getDefiningType() {
+        throw uoe();
+    }
+
+    @Override
+    public String getValue() {
+        throw uoe();
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        throw uoe();
+    }
+
+    private static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException("Method not supported");
+    }
+}
index b7ac63f7b2450432f5c7f040bb441eb07e075234..ab5944b573d8e43a6a255ce703795e9f2ff555c7 100644 (file)
@@ -22,18 +22,15 @@ import java.util.Set;
 import org.eclipse.xtext.xbase.lib.StringExtensions;
 import org.opendaylight.mdsal.binding.model.api.CodeGenerator;
 import org.opendaylight.mdsal.binding.model.api.DefaultType;
-import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
-import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.mdsal.binding.model.util.Types;
-import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder;
 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTypeBuilder;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
@@ -175,13 +172,13 @@ public final class BuilderGenerator implements CodeGenerator {
      * @param set of method signature instances which should be transformed to list of properties
      * @return set of generated property instances which represents the getter <code>methods</code>
      */
-    private static Set<GeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
+    private static Set<BuilderGeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
         if (methods == null || methods.isEmpty()) {
             return Collections.emptySet();
         }
-        final Set<GeneratedProperty> result = new LinkedHashSet<>();
+        final Set<BuilderGeneratedProperty> result = new LinkedHashSet<>();
         for (MethodSignature m : methods) {
-            final GeneratedProperty createdField = propertyFromGetter(m);
+            final BuilderGeneratedProperty createdField = propertyFromGetter(m);
             if (createdField != null) {
                 result.add(createdField);
             }
@@ -201,7 +198,7 @@ public final class BuilderGenerator implements CodeGenerator {
      *  <li>if the return type of the <code>method</code> equals <code>null</code></li>
      * </ul>
      */
-    private static GeneratedProperty propertyFromGetter(final MethodSignature method) {
+    private static BuilderGeneratedProperty propertyFromGetter(final MethodSignature method) {
         checkArgument(method != null);
         checkArgument(method.getReturnType() != null);
         checkArgument(method.getName() != null);
@@ -209,21 +206,13 @@ public final class BuilderGenerator implements CodeGenerator {
         if (method.isDefault()) {
             return null;
         }
+
         final String prefix = BindingMapping.getGetterPrefix(Types.BOOLEAN.equals(method.getReturnType()));
         if (!method.getName().startsWith(prefix)) {
             return null;
         }
 
-        final String fieldName = StringExtensions.toFirstLower(method.getName().substring(prefix.length()));
-        final GeneratedTOBuilder tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"));
-        final GeneratedPropertyBuilder builder = tmpGenTO.addProperty(fieldName).setReturnType(method.getReturnType());
-        switch (method.getMechanics()) {
-            case NULLIFY_EMPTY:
-                builder.setNullifyEmpty(true);
-                break;
-            default:
-                break;
-        }
-        return tmpGenTO.build().getProperties().get(0);
+        return new BuilderGeneratedProperty(StringExtensions.toFirstLower(method.getName().substring(prefix.length())),
+            method);
     }
 }
index 53bffa4f18f504f946880282d73b985f1fe3f9d6..4228b2c11a8821dfe54e4202267618902e0a163b 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.util.Types
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
 import org.opendaylight.yangtools.yang.binding.AbstractAugmentable
+import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics
 
 class BuilderImplTemplate extends AbstractBuilderTemplate {
     val Type builderType;
@@ -147,9 +148,9 @@ class BuilderImplTemplate extends AbstractBuilderTemplate {
         «ENDFOR»
     '''
 
-    override protected  CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props) '''
+    override protected CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props) '''
         «FOR field : props»
-            «IF field.nullifyEmpty»
+            «IF field.mechanics === ValueMechanics.NULLIFY_EMPTY»
                 this.«field.fieldName» = «CODEHELPERS.importedName».emptyToNull(base.«field.getterMethodName»());
             «ELSE»
                 this.«field.fieldName» = base.«field.getterMethodName»();
index e4e093ae06554308394910dd888e6973647429fb..a6f9449d8b8ebf97458d4193d36e5ccfa748a27a 100644 (file)
@@ -49,7 +49,7 @@ class BuilderTemplate extends AbstractBuilderTemplate {
      * Constructs new instance of this class.
      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
      */
-    new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+    new(GeneratedType genType, GeneratedType targetType, Set<BuilderGeneratedProperty> properties, Type augmentType,
             Type keyType) {
         super(genType, targetType, properties, augmentType, keyType)
     }
@@ -429,11 +429,12 @@ class BuilderTemplate extends AbstractBuilderTemplate {
 
     override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
         this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
-        «generateCopyNonKeys(keyProps)»
+        «FOR field : keyProps»
+            this.«field.fieldName» = base.«field.getterMethodName»();
+        «ENDFOR»
     '''
 
-
-    override protected  CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props) '''
+    override protected CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props) '''
         «FOR field : props»
             this.«field.fieldName» = base.«field.getterMethodName»();
         «ENDFOR»
diff --git a/binding/mdsal-binding-test-model/src/test/java/org/opendaylight/mdsal/binding/test/model/TestListSquashing.java b/binding/mdsal-binding-test-model/src/test/java/org/opendaylight/mdsal/binding/test/model/TestListSquashing.java
new file mode 100644 (file)
index 0000000..349c435
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020 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.mdsal.binding.test.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.Def;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.DefBuilder;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.Lst;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.LstBuilder;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.LstKey;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.Container;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.ContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Keyed;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedKey;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Unkeyed;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.UnkeyedBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.pattern.rev170101.Cont;
+import org.opendaylight.yang.gen.v1.urn.test.pattern.rev170101.ContBuilder;
+
+public class TestListSquashing {
+    @Test
+    public void testEmptyLeafList() {
+        final Cont obj = new ContBuilder().setTest3(List.of()).build();
+        // Eventhough return type is List, it should be retained
+        assertEquals(List.of(), obj.getTest3());
+    }
+
+    @Test
+    public void testEmptyUserOrderedList() {
+        final Container cont = new ContainerBuilder()
+                .setKeyed(List.of())
+                .setUnkeyed(List.of())
+                .build();
+        // Empty Lists should become null
+        assertNull(cont.getKeyed());
+        assertNull(cont.getUnkeyed());
+    }
+
+    @Test
+    public void testUserOrderedList() {
+        final Keyed keyed = new KeyedBuilder().withKey(new KeyedKey("a")).build();
+        final Unkeyed unkeyed = new UnkeyedBuilder().build();
+        final Container cont = new ContainerBuilder()
+                .setKeyed(List.of(keyed))
+                .setUnkeyed(List.of(unkeyed))
+                .build();
+        // Non-empty Lists should be retained
+        assertEquals(List.of(keyed), cont.getKeyed());
+        assertEquals(List.of(unkeyed), cont.getUnkeyed());
+    }
+
+    @Test
+    public void testEmptySystemOrderedList() {
+        final Def cont = new DefBuilder().setLst(Map.of()).build();
+        // Empty Map should become null
+        assertNull(cont.getLst());
+    }
+
+    @Test
+    public void testSystemOrderedList() {
+        final Lst lst = new LstBuilder().withKey(new LstKey("a")).build();
+        final Def cont = new DefBuilder().setLst(Map.of(lst.key(), lst)).build();
+        // Non-empty Map should be retained
+        assertEquals(Map.of(lst.key(), lst), cont.getLst());
+    }
+}