Add PathArgument uint adaptation 31/87031/12
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 17 Dec 2019 11:56:56 +0000 (12:56 +0100)
committerRobert Varga <nite@hq.sk>
Sun, 19 Jan 2020 14:35:02 +0000 (14:35 +0000)
Legacy uint values may be present in NodeWithValue as well
as NodeIdentifierWithPredicates. Make sure we run the appropriate
adaptation.

JIRA: CONTROLLER-1923
Change-Id: Ib59adb9e596e4d63110cbe9987feeedc61cb05eb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPruner.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPrunerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-ctlr1923.yang [new file with mode: 0644]

index d6d409ceadf5683d04dce9a438b183feeb34825b..ae6950df6fcf8291893c357e25de72b86815ec0c 100644 (file)
@@ -45,13 +45,13 @@ abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWrite
     }
 
     @FunctionalInterface
     }
 
     @FunctionalInterface
-    private interface WriterMethod<T extends PathArgument> {
+    interface WriterMethod<T extends PathArgument> {
 
         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name) throws IOException;
     }
 
     @FunctionalInterface
 
         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name) throws IOException;
     }
 
     @FunctionalInterface
-    private interface SizedWriterMethod<T extends PathArgument> {
+    interface SizedWriterMethod<T extends PathArgument> {
 
         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name, int childSizeHint) throws IOException;
     }
 
         void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name, int childSizeHint) throws IOException;
     }
@@ -185,7 +185,7 @@ abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWrite
     public void scalarValue(final Object value) throws IOException {
         checkNotSealed();
         if (unknown == 0) {
     public void scalarValue(final Object value) throws IOException {
         checkNotSealed();
         if (unknown == 0) {
-            delegate.scalarValue(translateScalar(stack.peek(), value));
+            delegate.scalarValue(translateScalar(currentSchema(), value));
         }
     }
 
         }
     }
 
@@ -256,7 +256,7 @@ abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWrite
         }
 
         final DataSchemaContextNode<?> schema;
         }
 
         final DataSchemaContextNode<?> schema;
-        final DataSchemaContextNode<?> parent = stack.peek();
+        final DataSchemaContextNode<?> parent = currentSchema();
         if (parent != null) {
             schema = parent.getChild(name);
         } else {
         if (parent != null) {
             schema = parent.getChild(name);
         } else {
@@ -277,16 +277,20 @@ abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWrite
         return true;
     }
 
         return true;
     }
 
-    private <A extends PathArgument> void enter(final WriterMethod<A> method, final A name) throws IOException {
+    final <A extends PathArgument> void enter(final WriterMethod<A> method, final A name) throws IOException {
         if (enter(name)) {
             method.apply(delegate, name);
         }
     }
 
         if (enter(name)) {
             method.apply(delegate, name);
         }
     }
 
-    private <A extends PathArgument> void enter(final SizedWriterMethod<A> method, final A name, final int size)
+    final <A extends PathArgument> void enter(final SizedWriterMethod<A> method, final A name, final int size)
             throws IOException {
         if (enter(name)) {
             method.apply(delegate, name, size);
         }
     }
             throws IOException {
         if (enter(name)) {
             method.apply(delegate, name, size);
         }
     }
+
+    final DataSchemaContextNode<?> currentSchema() {
+        return stack.peek();
+    }
 }
 }
index 751c96f2b65f290429b91487bd3dbada16c71f4d..f4e5133586563726fa09cd57bfc65908c0891bd1 100644 (file)
@@ -8,26 +8,10 @@
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
 import com.google.common.annotations.Beta;
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
 import com.google.common.annotations.Beta;
-import java.io.IOException;
-import java.math.BigInteger;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.Uint16;
-import org.opendaylight.yangtools.yang.common.Uint32;
-import org.opendaylight.yangtools.yang.common.Uint64;
-import org.opendaylight.yangtools.yang.common.Uint8;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding
 
 /**
  * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding
@@ -54,41 +38,6 @@ public abstract class ReusableNormalizedNodePruner extends AbstractNormalizedNod
         }
     }
 
         }
     }
 
-    private static final class UintAdaptingPruner extends ReusableNormalizedNodePruner {
-        private static final Logger LOG = LoggerFactory.getLogger(UintAdaptingPruner.class);
-
-        UintAdaptingPruner(final DataSchemaContextTree tree) {
-            super(tree);
-        }
-
-        @Override
-        public ReusableNormalizedNodePruner duplicate() {
-            return new UintAdaptingPruner(getTree());
-        }
-
-        @Override
-        Object translateScalar(final DataSchemaContextNode<?> context, final Object value) throws IOException {
-            final DataSchemaNode schema = context.getDataSchemaNode();
-            if (schema instanceof TypedDataSchemaNode) {
-                final TypeDefinition<?> type = ((TypedDataSchemaNode) schema).getType();
-                if (value instanceof Short && type instanceof Uint8TypeDefinition) {
-                    LOG.trace("Translating legacy uint8 {}", value);
-                    return Uint8.valueOf((Short) value);
-                } else if (value instanceof Integer && type instanceof Uint16TypeDefinition) {
-                    LOG.trace("Translating legacy uint16 {}", value);
-                    return Uint16.valueOf((Integer) value);
-                } else if (value instanceof Long && type instanceof Uint32TypeDefinition) {
-                    LOG.trace("Translating legacy uint32 {}", value);
-                    return Uint32.valueOf((Long) value);
-                } else if (value instanceof BigInteger && type instanceof Uint64TypeDefinition) {
-                    LOG.trace("Translating legacy uint64 {}", value);
-                    return Uint64.valueOf((BigInteger) value);
-                }
-            }
-            return value;
-        }
-    }
-
     ReusableNormalizedNodePruner(final SchemaContext schemaContext) {
         super(schemaContext);
     }
     ReusableNormalizedNodePruner(final SchemaContext schemaContext) {
         super(schemaContext);
     }
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPruner.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPruner.java
new file mode 100644 (file)
index 0000000..b3e02a4
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2019 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.controller.cluster.datastore.node.utils.transformer;
+
+import static com.google.common.base.Verify.verify;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.Function;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.impl.schema.ReusableImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class UintAdaptingPruner extends ReusableNormalizedNodePruner {
+    @FunctionalInterface
+    private interface NipAdapter extends Function<NodeIdentifierWithPredicates, NodeIdentifierWithPredicates> {
+
+    }
+
+    private enum ValueAdapter implements Function<Object, Object> {
+        UINT8 {
+            @Override
+            public Object apply(final Object obj) {
+                if (obj instanceof Short) {
+                    LOG.trace("Translating legacy uint8 {}", obj);
+                    return Uint8.valueOf((Short) obj);
+                }
+                return obj;
+            }
+        },
+        UINT16 {
+            @Override
+            public Object apply(final Object obj) {
+                if (obj instanceof Integer) {
+                    LOG.trace("Translating legacy uint16 {}", obj);
+                    return Uint16.valueOf((Integer) obj);
+                }
+                return obj;
+            }
+        },
+        UINT32 {
+            @Override
+            public Object apply(final Object obj) {
+                if (obj instanceof Long) {
+                    LOG.trace("Translating legacy uint32 {}", obj);
+                    return Uint32.valueOf((Long) obj);
+                }
+                return obj;
+            }
+        },
+        UINT64 {
+            @Override
+            public Object apply(final Object obj) {
+                if (obj instanceof BigInteger) {
+                    LOG.trace("Translating legacy uint64 {}", obj);
+                    return Uint64.valueOf((BigInteger) obj);
+                }
+                return obj;
+            }
+        };
+
+        private static final Logger LOG = LoggerFactory.getLogger(ValueAdapter.class);
+
+        static @Nullable ValueAdapter forType(final TypeDefinition<?> type) {
+            if (type instanceof Uint8TypeDefinition) {
+                return UINT8;
+            } else if (type instanceof Uint16TypeDefinition) {
+                return UINT16;
+            } else if (type instanceof Uint32TypeDefinition) {
+                return UINT32;
+            } else if (type instanceof Uint64TypeDefinition) {
+                return UINT64;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private static final LoadingCache<ListSchemaNode, NipAdapter> NIP_ADAPTERS = CacheBuilder.newBuilder()
+            .weakKeys().build(new AdapterCacheLoader());
+
+    UintAdaptingPruner(final DataSchemaContextTree tree) {
+        super(tree);
+    }
+
+    @Override
+    public ReusableNormalizedNodePruner duplicate() {
+        return new UintAdaptingPruner(getTree());
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+            throws IOException {
+        enter(this::adaptEntry, identifier, childSizeHint);
+    }
+
+    @Override
+    public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+        enter(this::adaptEntry, name);
+    }
+
+    @Override
+    Object translateScalar(final DataSchemaContextNode<?> context, final Object value) throws IOException {
+        final DataSchemaNode schema = context.getDataSchemaNode();
+        return schema instanceof TypedDataSchemaNode ? adaptValue(((TypedDataSchemaNode) schema).getType(), value)
+                : value;
+    }
+
+    private void adaptEntry(final ReusableImmutableNormalizedNodeStreamWriter writer, final NodeWithValue<?> name) {
+        final NodeWithValue<?> adapted;
+        final DataSchemaNode schema = currentSchema().getDataSchemaNode();
+        if (schema instanceof TypedDataSchemaNode) {
+            final Object oldValue = name.getValue();
+            final Object newValue = adaptValue(((TypedDataSchemaNode) schema).getType(), oldValue);
+            adapted = newValue == oldValue ? name : new NodeWithValue<>(name.getNodeType(), newValue);
+        } else {
+            adapted = name;
+        }
+
+        writer.startLeafSetEntryNode(adapted);
+    }
+
+    private void adaptEntry(final ReusableImmutableNormalizedNodeStreamWriter writer,
+            final NodeIdentifierWithPredicates name, final int size) {
+        final NodeIdentifierWithPredicates adapted;
+        final DataSchemaNode schema = currentSchema().getDataSchemaNode();
+        if (schema instanceof ListSchemaNode) {
+            adapted = NIP_ADAPTERS.getUnchecked((ListSchemaNode) schema).apply(name);
+        } else {
+            adapted = name;
+        }
+
+        writer.startMapEntryNode(adapted, size);
+    }
+
+    private static Object adaptValue(final TypeDefinition<?> type, final Object value) {
+        final ValueAdapter adapter = ValueAdapter.forType(type);
+        return adapter != null ? adapter.apply(value) : value;
+    }
+
+    private static final class AdapterCacheLoader extends CacheLoader<ListSchemaNode, NipAdapter> {
+        @Override
+        public NipAdapter load(final ListSchemaNode key) {
+            final Map<QName, ValueAdapter> adapters = new HashMap<>();
+
+            for (QName qname : key.getKeyDefinition()) {
+                final DataSchemaNode child;
+                try {
+                    child = key.findDataTreeChild(qname).orElseThrow();
+                } catch (NoSuchElementException e) {
+                    throw new IllegalStateException("Failed to find child " + qname, e);
+                }
+
+                verify(child instanceof LeafSchemaNode, "Key references non-leaf child %s", child);
+                final ValueAdapter adapter = ValueAdapter.forType(((LeafSchemaNode) child).getType());
+                if (adapter != null) {
+                    adapters.put(qname, adapter);
+                }
+            }
+
+            return adapters.isEmpty() ? name -> name : new TransformingNipAdapter(adapters);
+        }
+    }
+
+    private static final class TransformingNipAdapter implements NipAdapter {
+        private final ImmutableMap<QName, ValueAdapter> adapters;
+
+        TransformingNipAdapter(final Map<QName, ValueAdapter> toTransform) {
+            adapters = ImmutableMap.copyOf(toTransform);
+        }
+
+        @Override
+        public NodeIdentifierWithPredicates apply(final NodeIdentifierWithPredicates name) {
+            final Set<Entry<QName, Object>> entries = name.entrySet();
+            final ImmutableMap.Builder<QName, Object> newEntries = ImmutableMap.builderWithExpectedSize(entries.size());
+            for (Entry<QName, Object> e : entries) {
+                final QName qname = e.getKey();
+                final ValueAdapter adapter = adapters.get(qname);
+                newEntries.put(qname, adapter != null ? adapter.apply(e.getValue()) : e.getValue());
+            }
+
+            return NodeIdentifierWithPredicates.of(name.getNodeType(), newEntries.build());
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPrunerTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/UintAdaptingPrunerTest.java
new file mode 100644 (file)
index 0000000..33f6058
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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.controller.cluster.datastore.node.utils.transformer;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.math.BigInteger;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.Uint16;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
+import org.opendaylight.yangtools.yang.common.Uint8;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class UintAdaptingPrunerTest {
+    private static final QName CONT = QName.create("urn:odl-ctlr1923", "cont");
+    private static final QName LST = QName.create(CONT, "lst");
+    private static final QName LFLST8 = QName.create(CONT, "lf-lst8");
+    private static final QName LFLST16 = QName.create(CONT, "lf-lst16");
+    private static final QName LFLST32 = QName.create(CONT, "lf-lst32");
+    private static final QName LFLST64 = QName.create(CONT, "lf-lst64");
+    private static final QName A = QName.create(LST, "a");
+    private static final QName B = QName.create(LST, "b");
+    private static final QName C = QName.create(LST, "c");
+    private static final QName D = QName.create(LST, "d");
+    private static final QName E = QName.create(LST, "e");
+    private static final QName F = QName.create(LST, "f");
+    private static final QName G = QName.create(LST, "g");
+    private static final QName H = QName.create(LST, "h");
+
+    private static EffectiveModelContext CONTEXT;
+
+    @BeforeClass
+    public static void beforeClass() {
+        CONTEXT = YangParserTestUtils.parseYangResource("/odl-ctlr1923.yang");
+    }
+
+    @Test
+    public void testListTranslation() throws IOException {
+        assertEquals(Builders.mapBuilder()
+            .withNodeIdentifier(new NodeIdentifier(LST))
+            .withChild(Builders.mapEntryBuilder()
+                .withNodeIdentifier(NodeIdentifierWithPredicates.of(LST, ImmutableMap.<QName, Object>builder()
+                    .put(A, (byte) 1)
+                    .put(B, (short) 1)
+                    .put(C, 1)
+                    .put(D, 1L)
+                    .put(E, Uint8.ONE)
+                    .put(F, Uint16.ONE)
+                    .put(G, Uint32.ONE)
+                    .put(H, Uint64.ONE)
+                    .build()))
+                .withChild(ImmutableNodes.leafNode(A, (byte) 1))
+                .withChild(ImmutableNodes.leafNode(B, (short) 1))
+                .withChild(ImmutableNodes.leafNode(C, 1))
+                .withChild(ImmutableNodes.leafNode(D, 1L))
+                .withChild(ImmutableNodes.leafNode(E, Uint8.ONE))
+                .withChild(ImmutableNodes.leafNode(F, Uint16.ONE))
+                .withChild(ImmutableNodes.leafNode(G, Uint32.ONE))
+                .withChild(ImmutableNodes.leafNode(H, Uint64.ONE))
+                .build())
+            .build(),
+            prune(Builders.mapBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LST))
+                .withChild(Builders.mapEntryBuilder()
+                    .withNodeIdentifier(NodeIdentifierWithPredicates.of(LST,  ImmutableMap.<QName, Object>builder()
+                        .put(A, (byte) 1)
+                        .put(B, (short) 1)
+                        .put(C, 1)
+                        .put(D, 1L)
+                        .put(E, (short) 1)
+                        .put(F, 1)
+                        .put(G, 1L)
+                        .put(H, BigInteger.ONE)
+                        .build()))
+                    .withChild(ImmutableNodes.leafNode(A, (byte) 1))
+                    .withChild(ImmutableNodes.leafNode(B, (short) 1))
+                    .withChild(ImmutableNodes.leafNode(C, 1))
+                    .withChild(ImmutableNodes.leafNode(D, 1L))
+                    .withChild(ImmutableNodes.leafNode(E, (short) 1))
+                    .withChild(ImmutableNodes.leafNode(F, 1))
+                    .withChild(ImmutableNodes.leafNode(G, 1L))
+                    .withChild(ImmutableNodes.leafNode(H, BigInteger.ONE))
+                    .build())
+                .build()));
+    }
+
+    @Test
+    public void testContainerTranslation() throws IOException {
+        assertEquals(Builders.containerBuilder()
+            .withNodeIdentifier(new NodeIdentifier(CONT))
+            .withChild(ImmutableNodes.leafNode(A, (byte) 1))
+            .withChild(ImmutableNodes.leafNode(B, (short) 1))
+            .withChild(ImmutableNodes.leafNode(C, 1))
+            .withChild(ImmutableNodes.leafNode(D, 1L))
+            .withChild(ImmutableNodes.leafNode(E, Uint8.ONE))
+            .withChild(ImmutableNodes.leafNode(F, Uint16.ONE))
+            .withChild(ImmutableNodes.leafNode(G, Uint32.ONE))
+            .withChild(ImmutableNodes.leafNode(H, Uint64.ONE))
+            .build(),
+            prune(Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(CONT))
+                .withChild(ImmutableNodes.leafNode(A, (byte) 1))
+                .withChild(ImmutableNodes.leafNode(B, (short) 1))
+                .withChild(ImmutableNodes.leafNode(C, 1))
+                .withChild(ImmutableNodes.leafNode(D, 1L))
+                .withChild(ImmutableNodes.leafNode(E, (short) 1))
+                .withChild(ImmutableNodes.leafNode(F, 1))
+                .withChild(ImmutableNodes.leafNode(G, 1L))
+                .withChild(ImmutableNodes.leafNode(H, BigInteger.ONE))
+                .build()));
+    }
+
+    @Test
+    public void testLeafList8() throws IOException {
+        assertEquals(Builders.leafSetBuilder()
+            .withNodeIdentifier(new NodeIdentifier(LFLST8))
+            .withChild(Builders.leafSetEntryBuilder()
+                .withNodeIdentifier(new NodeWithValue<>(LFLST8, Uint8.ONE))
+                .withValue(Uint8.ONE)
+                .build())
+            .build(),
+            prune(Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LFLST8))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(LFLST8, (short) 1))
+                    .withValue((short) 1)
+                    .build())
+                .build()));
+    }
+
+    @Test
+    public void testLeafList16() throws IOException {
+        assertEquals(Builders.leafSetBuilder()
+            .withNodeIdentifier(new NodeIdentifier(LFLST16))
+            .withChild(Builders.leafSetEntryBuilder()
+                .withNodeIdentifier(new NodeWithValue<>(LFLST16, Uint16.ONE))
+                .withValue(Uint16.ONE)
+                .build())
+            .build(),
+            prune(Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LFLST16))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(LFLST16,  1))
+                    .withValue(1)
+                    .build())
+                .build()));
+    }
+
+    @Test
+    public void testLeafList32() throws IOException {
+        assertEquals(Builders.leafSetBuilder()
+            .withNodeIdentifier(new NodeIdentifier(LFLST32))
+            .withChild(Builders.leafSetEntryBuilder()
+                .withNodeIdentifier(new NodeWithValue<>(LFLST32, Uint32.ONE))
+                .withValue(Uint32.ONE)
+                .build())
+            .build(),
+            prune(Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LFLST32))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(LFLST32, 1L))
+                    .withValue(1L)
+                    .build())
+                .build()));
+    }
+
+    @Test
+    public void testLeafList64() throws IOException {
+        assertEquals(Builders.leafSetBuilder()
+            .withNodeIdentifier(new NodeIdentifier(LFLST64))
+            .withChild(Builders.leafSetEntryBuilder()
+                .withNodeIdentifier(new NodeWithValue<>(LFLST64, Uint64.ONE))
+                .withValue(Uint64.ONE)
+                .build())
+            .build(),
+            prune(Builders.leafSetBuilder()
+                .withNodeIdentifier(new NodeIdentifier(LFLST64))
+                .withChild(Builders.leafSetEntryBuilder()
+                    .withNodeIdentifier(new NodeWithValue<>(LFLST64, BigInteger.ONE))
+                    .withValue(BigInteger.ONE)
+                    .build())
+                .build()));
+    }
+
+    private static NormalizedNode<?, ?> prune(final NormalizedNode<?, ?> node) throws IOException {
+        final ReusableNormalizedNodePruner pruner = ReusableNormalizedNodePruner.forSchemaContext(CONTEXT)
+                .withUintAdaption();
+        pruner.initializeForPath(YangInstanceIdentifier.create(node.getIdentifier()));
+
+        try (NormalizedNodeWriter writer = NormalizedNodeWriter.forStreamWriter(pruner)) {
+            writer.write(node);
+        }
+        pruner.close();
+        return pruner.getResult().get();
+    }
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-ctlr1923.yang b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/odl-ctlr1923.yang
new file mode 100644 (file)
index 0000000..9ad4f72
--- /dev/null
@@ -0,0 +1,43 @@
+module odl-ctrl1923 {
+  yang-version 1;
+  namespace "urn:odl-ctlr1923";
+  prefix "ctrl1923";
+
+  description "Sodium -> Magnesium uint translation";
+
+  grouping grp {
+    leaf a { type int8; }
+    leaf b { type int16; }
+    leaf c { type int32; }
+    leaf d { type int64; }
+    leaf e { type uint8; }
+    leaf f { type uint16; }
+    leaf g { type uint32; }
+    leaf h { type uint64; }
+  }
+
+  container cont {
+    uses grp;
+  }
+
+  list lst {
+    key "a b c d e f g h";
+    uses grp;
+  }
+
+  leaf-list lf-lst8 {
+    type uint8;
+  }
+
+  leaf-list lf-lst16 {
+    type uint16;
+  }
+
+  leaf-list lf-lst32 {
+    type uint32;
+  }
+
+  leaf-list lf-lst64 {
+    type uint64;
+  }
+}