Map system-ordered keyed lists to Map 27/85827/21
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 18 Nov 2019 11:30:08 +0000 (12:30 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 17 Dec 2019 10:23:16 +0000 (11:23 +0100)
The type mapping for YANG lists which have a key and do not have
a user-specified order should point to java.util.Map, as that
provides the correct semantics.

This patch changes that mapping, resulting in better ergonomics,
as it is now possible to acquire specific items based on their
keys.

JIRA: MDSAL-434
Change-Id: If2f8980fde1c419bbc0b7c414d7deab329362b47
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
25 files changed:
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/ForwardedNotificationAdapterTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/Mdsal108Test.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/KeyedListNodeCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ListNodeCodecContext.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/Bug5845booleanKeyTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CachingCodecTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CaseSubstitutionTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/EmptyLeafTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/KeyInheritenceTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/NormalizedNodeSerializeDeserializeTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/NotificationProcessingTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/RpcDataSerializationTest.java
binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/AbstractTypeGenerator.java
binding/mdsal-binding-generator-impl/src/test/java/org/opendaylight/mdsal/binding/generator/impl/ChoiceCaseGenTypesTest.java
binding/mdsal-binding-generator-util/src/main/java/org/opendaylight/mdsal/binding/model/util/Types.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend
binding/mdsal-binding-test-model/src/main/java/org/opendaylight/mdsal/binding/test/model/util/ListsBindingUtils.java
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AssertDataObjectsTest.java
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/AugmentableExtensionTest.java
binding/mdsal-binding-test-utils/src/test/java/org/opendaylight/mdsal/binding/testutils/ExpectedObjects.java
yanglib/mdsal-yanglib-rfc7895/src/main/java/org/opendaylight/mdsal/yanglib/rfc7895/MountPointContextFactoryImpl.java
yanglib/mdsal-yanglib-rfc8525/src/main/java/org/opendaylight/mdsal/yanglib/rfc8525/MountPointContextFactoryImpl.java

index 0668b167baad2ff4efed80eae745076a30f5eb3b..f5a761c2cb10b5f24d63953078a82d034cd42c0c 100644 (file)
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.List;
@@ -45,9 +45,10 @@ public class ForwardedNotificationAdapterTest extends AbstractNotificationBroker
     }
 
     private static TwoLevelListChanged createTestData() {
-        final TwoLevelListChangedBuilder tb = new TwoLevelListChangedBuilder();
-        tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build()));
-        return tb.build();
+        final TopLevelListKey key = new TopLevelListKey("test");
+        return new TwoLevelListChangedBuilder()
+                .setTopLevelList(ImmutableMap.of(key, new TopLevelListBuilder().withKey(key).build()))
+                .build();
     }
 
     @Test
index cf4668a15fecf35827355e6fe94903f7b24912ca..006283538e84425229ddef84f6aecf53d9b82a20 100644 (file)
@@ -7,7 +7,7 @@
  */
 package org.opendaylight.mdsal.binding.dom.adapter.test;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
@@ -25,9 +25,9 @@ public class Mdsal108Test extends AbstractDataBrokerTest {
     public void testDelete() {
         DataBroker dataBroker = getDataBroker();
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-        ArrayList<TopLevelList> list = new ArrayList<>();
-        list.add(new TopLevelListBuilder().setName("name").build());
-        TopBuilder builder = new TopBuilder().setTopLevelList(list);
+
+        final TopLevelList item = new TopLevelListBuilder().setName("name").build();
+        TopBuilder builder = new TopBuilder().setTopLevelList(Collections.singletonMap(item.key(), item));
         writeTransaction.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Top.class), builder.build());
         assertCommit(writeTransaction.commit());
 
index 59ca8b0b8cb2e37e37259e6a9c17b8eef6b76579..ab8ef78532a18a633f27398ae589486ba3be6328 100644 (file)
@@ -144,10 +144,10 @@ 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 {
+            final BindingStreamEventWriter writer, final Map<?, ? extends E> value) throws IOException {
         if (value != null) {
             writer.startMapNode(childClass, value.size());
-            commonStreamList(registry, writer, childStreamer, value);
+            commonStreamList(registry, writer, childStreamer, value.values());
         }
     }
 
index f601b0628c5baa9f0cfb7e180ff0e2b4377a7d6b..5a3bc3d22908234d0adf63d896bb6b4e1dd7a079 100644 (file)
@@ -144,7 +144,7 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
         BindingStreamEventWriter.class, List.class);
     private static final StackManipulation STREAM_MAP = invokeMethod(DataObjectStreamer.class,
         "streamMap", Class.class, DataObjectStreamer.class, DataObjectSerializerRegistry.class,
-        BindingStreamEventWriter.class, List.class);
+        BindingStreamEventWriter.class, Map.class);
     private static final StackManipulation STREAM_ORDERED_MAP = invokeMethod(DataObjectStreamer.class,
         "streamOrderedMap", Class.class, DataObjectStreamer.class, DataObjectSerializerRegistry.class,
         BindingStreamEventWriter.class, List.class);
@@ -257,14 +257,17 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
             final String getterName = getter.getName();
             final Type childType = props.get(getterName);
             verify(childType instanceof ParameterizedType, "Unexpected type %s for %s", childType, getterName);
-            final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+            final Type[] params = ((ParameterizedType) childType).getActualTypeArguments();
+            final ListSchemaNode listSchema = (ListSchemaNode) childSchema;
             final Class<?> valueClass;
-            try {
-                valueClass = loader.loadClass(valueType.getFullyQualifiedName());
-            } catch (ClassNotFoundException e) {
-                throw new LinkageError("Failed to load " + valueType, e);
+            if (!listSchema.isUserOrdered() && !listSchema.getKeyDefinition().isEmpty()) {
+                loadTypeClass(loader, params[0]);
+                valueClass = loadTypeClass(loader, params[1]);
+            } else {
+                valueClass = loadTypeClass(loader, params[0]);
             }
-            return listChildStream(getter, valueClass.asSubclass(DataObject.class), (ListSchemaNode) childSchema);
+
+            return listChildStream(getter, valueClass.asSubclass(DataObject.class), listSchema);
         }
         if (childSchema instanceof ChoiceSchemaNode) {
             return choiceChildStream(getter);
@@ -374,6 +377,14 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
         }
     }
 
+    private static Class<?> loadTypeClass(final CodecClassLoader loader, final Type type) {
+        try {
+            return loader.loadClass(type.getFullyQualifiedName());
+        } catch (ClassNotFoundException e) {
+            throw new LinkageError("Failed to load " + type, e);
+        }
+    }
+
     private static final class SerializeImplementation implements Implementation {
         private final List<ChildStream> children;
         private final StackManipulation startEvent;
index 62dd9b74c196a3387d42e3e1d4a8404b1b54537b..33515324b23fc64a8ca636ba4a6b3d6f0753e1ab 100644 (file)
@@ -10,8 +10,16 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static java.util.Objects.requireNonNull;
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.IDENTIFIABLE_KEY_NAME;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
 import java.lang.reflect.Method;
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
@@ -20,12 +28,59 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-final class KeyedListNodeCodecContext<D extends DataObject & Identifiable<?>> extends ListNodeCodecContext<D> {
+abstract class KeyedListNodeCodecContext<D extends DataObject & Identifiable<?>> extends ListNodeCodecContext<D> {
+    private static final class Ordered<D extends DataObject & Identifiable<?>> extends KeyedListNodeCodecContext<D> {
+        Ordered(final DataContainerCodecPrototype<ListSchemaNode> prototype, final Method keyMethod,
+                final IdentifiableItemCodec codec) {
+            super(prototype, keyMethod, codec);
+        }
+    }
+
+    private static final class Unordered<D extends DataObject & Identifiable<?>> extends KeyedListNodeCodecContext<D> {
+        private static final MethodType KEY_TYPE = MethodType.methodType(Object.class, DataObject.class);
+
+        private final MethodHandle keyHandle;
+
+        Unordered(final DataContainerCodecPrototype<ListSchemaNode> prototype, final Method keyMethod,
+                final IdentifiableItemCodec codec) {
+            super(prototype, keyMethod, codec);
+
+            try {
+                this.keyHandle = MethodHandles.publicLookup().unreflect(keyMethod).asType(KEY_TYPE);
+            } catch (IllegalAccessException | WrongMethodTypeException e) {
+                throw new LinkageError("Failed to acquire method " + keyMethod, e);
+            }
+        }
+
+        @Override
+        Map<?, D> fromMap(final MapNode nodes) {
+            final Collection<MapEntryNode> value = nodes.getValue();
+            final Builder<Object, D> builder = ImmutableMap.builderWithExpectedSize(value.size());
+            // FIXME: Could be this lazy transformed map?
+            for (MapEntryNode node : value) {
+                final D entry = fromMapEntry(node);
+                builder.put(getKey(entry), entry);
+            }
+            return builder.build();
+        }
+
+        @SuppressWarnings("checkstyle:illegalCatch")
+        private Object getKey(final D entry) {
+            try {
+                return keyHandle.invokeExact(entry);
+            } catch (Throwable e) {
+                throw new LinkageError("Failed to extract key from " + entry, e);
+            }
+        }
+    }
+
     private final IdentifiableItemCodec codec;
 
-    private KeyedListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype,
+    KeyedListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype,
             final Method keyMethod, final IdentifiableItemCodec codec) {
         super(prototype, keyMethod);
         this.codec = requireNonNull(codec);
@@ -41,9 +96,10 @@ final class KeyedListNodeCodecContext<D extends DataObject & Identifiable<?>> ex
             throw new IllegalStateException("Required method not available", e);
         }
 
-        final IdentifiableItemCodec codec = prototype.getFactory().getPathArgumentCodec(bindingClass,
-            prototype.getSchema());
-        return new KeyedListNodeCodecContext<>(prototype, keyMethod, codec);
+        final ListSchemaNode schema = prototype.getSchema();
+        final IdentifiableItemCodec codec = prototype.getFactory().getPathArgumentCodec(bindingClass, schema);
+        return schema.isUserOrdered() ? new Ordered<>(prototype, keyMethod, codec)
+                : new Unordered<>(prototype, keyMethod, codec);
     }
 
     @Override
index 9d38ded24d93fc7e5eb2e8e700779a72658db84c..fe7a09363772304328ee30014d7faa949cf475da 100644 (file)
@@ -56,17 +56,17 @@ class ListNodeCodecContext<D extends DataObject> extends DataObjectCodecContext<
         }
     }
 
-    private List<D> fromMap(final MapNode nodes) {
+    Object fromMap(final MapNode nodes) {
         final Collection<MapEntryNode> value = nodes.getValue();
         final Builder<D> builder = ImmutableList.builderWithExpectedSize(value.size());
         // FIXME: Could be this lazy transformed list?
         for (MapEntryNode node : value) {
-            builder.add(fromMapEntry(node));
+            builder.add(createBindingProxy(node));
         }
         return builder.build();
     }
 
-    private D fromMapEntry(final MapEntryNode node) {
+    final D fromMapEntry(final MapEntryNode node) {
         return createBindingProxy(node);
     }
 
index 144a5b4d9a0a26eb7e3b2a84e47289fb2e173898..333a2eed4799a578cc829a0873e057d5ac26d078 100644 (file)
@@ -71,10 +71,8 @@ public class AugmentationSubstitutionTest extends AbstractBindingCodecTest {
 
     private static RpcComplexUsesAugment createComplexData() {
         return new RpcComplexUsesAugmentBuilder()
-                .setContainerWithUses(new ContainerWithUsesBuilder()
-                    .setLeafFromGrouping("foo")
-                    .build())
-                .setListViaUses(Collections.emptyList())
+                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
+                .setListViaUses(Collections.emptyMap())
                 .build();
     }
 }
index 7eb3b8919a8508c14e24841b5f6425b81ba84932..0cf3b744713f131fd45fa35200040be4da6674b6 100644 (file)
@@ -25,17 +25,19 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 public class Bug5845booleanKeyTest extends AbstractBindingCodecTest {
     @Test
     public void testBug5845() throws Exception {
-        final BooleanContainer booleanContainer = new BooleanContainerBuilder().setBooleanList(Collections
-                .singletonList(new BooleanListBuilder()
-                        .withKey(new BooleanListKey(true, true))
-                        .setBooleanLeaf1(true)
-                        .setBooleanLeaf2(true)
-                        .build()))
+        final BooleanListKey blk = new BooleanListKey(true, true);
+        final BooleanContainer booleanContainer = new BooleanContainerBuilder()
+                .setBooleanList(Collections.singletonMap(blk, new BooleanListBuilder()
+                    .withKey(blk)
+                    .setBooleanLeaf1(true)
+                    .setBooleanLeaf2(true)
+                    .build()))
                 .build();
 
-        final BooleanContainer booleanContainerInt = new BooleanContainerBuilder().setBooleanListInt(Collections
-                .singletonList(new BooleanListIntBuilder()
-                        .withKey(new BooleanListIntKey((byte) 1))
+        final BooleanListIntKey blik = new BooleanListIntKey((byte) 1);
+        final BooleanContainer booleanContainerInt = new BooleanContainerBuilder()
+                .setBooleanListInt(Collections.singletonMap(blik, new BooleanListIntBuilder()
+                        .withKey(blik)
                         .setBooleanLeafInt((byte) 1)
                         .build()))
                 .build();
index 6768bef5b70a6862e7139cc294c15806772c98c6..7c8fa7a72950b820315c06dc8de13df81f6b90f7 100644 (file)
@@ -14,10 +14,10 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
-import java.util.List;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
@@ -47,8 +47,8 @@ public class CachingCodecTest extends AbstractBindingCodecTest {
 
     private static final NodeIdentifier TOP_LEVEL_LIST_ARG = new NodeIdentifier(TopLevelList.QNAME);
     private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
-    private static final List<TopLevelList> TWO_LIST = createList(2);
-    private static final List<TopLevelList> THREE_LIST = createList(3);
+    private static final Map<TopLevelListKey, TopLevelList> TWO_LIST = createList(2);
+    private static final Map<TopLevelListKey, TopLevelList> THREE_LIST = createList(3);
 
     private static final Top TOP_TWO_LIST_DATA = new TopBuilder().setTopLevelList(TWO_LIST).build();
     private static final Top TOP_THREE_LIST_DATA = new TopBuilder().setTopLevelList(THREE_LIST).build();
@@ -77,12 +77,11 @@ public class CachingCodecTest extends AbstractBindingCodecTest {
         contNode = registry.getCodecContext().getSubtreeCodec(CONT_PATH);
     }
 
-    private static List<TopLevelList> createList(final int num) {
-
-        final ImmutableList.Builder<TopLevelList> builder = ImmutableList.builder();
+    private static Map<TopLevelListKey, TopLevelList> createList(final int num) {
+        final ImmutableMap.Builder<TopLevelListKey, TopLevelList> builder = ImmutableMap.builder();
         for (int i = 0; i < num; i++) {
             final TopLevelListKey key = new TopLevelListKey("test-" + i);
-            builder.add(new TopLevelListBuilder().withKey(key).build());
+            builder.put(key, new TopLevelListBuilder().withKey(key).build());
         }
         return builder.build();
     }
@@ -134,7 +133,7 @@ public class CachingCodecTest extends AbstractBindingCodecTest {
 
         final Top input = new TopBuilder().build();
         assertNull(input.getTopLevelList());
-        assertEquals(ImmutableList.of(), input.nonnullTopLevelList());
+        assertEquals(ImmutableMap.of(), input.nonnullTopLevelList());
 
         final NormalizedNode<?, ?> dom = cachingCodec.serialize(input);
         final Top output = cachingCodec.deserialize(dom);
@@ -142,7 +141,7 @@ public class CachingCodecTest extends AbstractBindingCodecTest {
         assertTrue(output.equals(input));
 
         assertNull(output.getTopLevelList());
-        assertEquals(ImmutableList.of(), output.nonnullTopLevelList());
+        assertEquals(ImmutableMap.of(), output.nonnullTopLevelList());
     }
 
     @SafeVarargs
index d379eb109035bbe88eee654a15ccd9648f24fb5c..c7ed608f8b2072fd9d928692c4cecbd25e2b31ee 100644 (file)
@@ -60,11 +60,8 @@ public class CaseSubstitutionTest extends AbstractBindingCodecTest {
 
     private static RpcComplexUsesAugment createComplexData() {
         return new RpcComplexUsesAugmentBuilder()
-        .setContainerWithUses(new ContainerWithUsesBuilder()
-            .setLeafFromGrouping("foo")
-        .build())
-        .setListViaUses(Collections.emptyList())
-        .build();
+                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
+                .setListViaUses(Collections.emptyMap())
+                .build();
     }
-
 }
index 6450a29c9f1d05382fc5720f74c53457ce9b9f64..c44141791dad03531dc36a5fa4c5b093eca8a6da 100644 (file)
@@ -58,13 +58,11 @@ public class EmptyLeafTest extends AbstractBindingCodecTest {
         assertNotNull(((EmptyLeaf) list).getEmptyType());
     }
 
+    // FIXME: either remove this method or take advantage of it
     private static RpcComplexUsesAugment createComplexData() {
         return new RpcComplexUsesAugmentBuilder()
-        .setContainerWithUses(new ContainerWithUsesBuilder()
-            .setLeafFromGrouping("foo")
-        .build())
-        .setListViaUses(Collections.emptyList())
-        .build();
+                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
+                .setListViaUses(Collections.emptyMap())
+                .build();
     }
-
 }
index 542d79b567c49ff693796e3eefefa37cd10c9323..f9e50ef824c67f8ad23a001386ab423110c3faae 100644 (file)
@@ -9,7 +9,7 @@ package org.opendaylight.mdsal.binding.dom.codec.test;
 
 import static org.junit.Assert.assertEquals;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.util.Map.Entry;
 import org.junit.Test;
 import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.Def;
@@ -29,10 +29,10 @@ public class KeyInheritenceTest extends AbstractBindingCodecTest {
     private static final InstanceIdentifier<Use> USE_IID = InstanceIdentifier.create(Use.class);
 
     private static final Def DEF = new DefBuilder()
-            .setLst(ImmutableList.of(new LstBuilder().setFoo("foo").withKey(KEY).build()))
+            .setLst(ImmutableMap.of(KEY, new LstBuilder().setFoo("foo").withKey(KEY).build()))
             .build();
     private static final Use USE = new UseBuilder()
-            .setLst(ImmutableList.of(new LstBuilder().setFoo("foo").withKey(KEY).build()))
+            .setLst(ImmutableMap.of(KEY, new LstBuilder().setFoo("foo").withKey(KEY).build()))
             .build();
 
     @Test
index 731ce67c7037123786c6cd326532848b7c77c727..81a54d15ce5443805c6d9af38c4aa22c1c703f40 100644 (file)
@@ -230,7 +230,7 @@ public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingCodec
         for (Entry<Class<? extends Augmentation<Top>>, ? extends Augmentation<Top>> augment : augments.entrySet()) {
             topBuilder.addAugmentation(augment.getKey(), augment.getValue());
         }
-        return topBuilder.setTopLevelList(Collections.emptyList()).build();
+        return topBuilder.setTopLevelList(Collections.emptyMap()).build();
     }
 
     @Test
index 5a8e14daf9d89313cef4b8d7f33d7f75512c4c7d..ce2cad96978ea16035789e2aed747152e5b8d961 100644 (file)
@@ -10,7 +10,7 @@ package org.opendaylight.mdsal.binding.dom.codec.test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.time.Instant;
 import org.junit.Test;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TwoLevelListChanged;
@@ -31,9 +31,9 @@ public class NotificationProcessingTest extends AbstractBindingCodecTest {
     private static final QName NAME = QName.create(TopLevelList.QNAME, "name");
 
     private static TwoLevelListChanged createTestBindingData() {
+        final TopLevelListKey key = new TopLevelListKey("test");
         return new TwoLevelListChangedBuilder()
-                .setTopLevelList(ImmutableList.of(
-                    new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build()))
+                .setTopLevelList(ImmutableMap.of(key, new TopLevelListBuilder().withKey(key).build()))
                 .build();
     }
 
index 79103528ce40174d8e99131556a6c1579f188479..ef777b521ed3e78317505459ebeb3b7f93b3ff5d 100644 (file)
@@ -10,7 +10,7 @@ package org.opendaylight.mdsal.binding.dom.codec.test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import org.junit.Test;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.GetTopOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.GetTopOutputBuilder;
@@ -31,11 +31,13 @@ public class RpcDataSerializationTest extends AbstractBindingCodecTest {
     private static final SchemaPath PUT_TOP_INPUT = SchemaPath.create(true, PUT_TOP, PutTopInput.QNAME);
     private static final SchemaPath GET_TOP_OUTPUT = SchemaPath.create(true, GET_TOP, GetTopOutput.QNAME);
 
+    private static final TopLevelListKey LIST_KEY = new TopLevelListKey("test");
+
     @Test
     public void testRpcInputToNormalized() {
-        final PutTopInputBuilder tb = new PutTopInputBuilder();
-        tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build()));
-        final PutTopInput bindingOriginal = tb.build();
+        final PutTopInput bindingOriginal = new PutTopInputBuilder()
+                .setTopLevelList(ImmutableMap.of(LIST_KEY, new TopLevelListBuilder().withKey(LIST_KEY).build()))
+                .build();
         final ContainerNode dom = registry.toNormalizedNodeRpcData(bindingOriginal);
         assertNotNull(dom);
         assertEquals(PutTopInput.QNAME, dom.getIdentifier().getNodeType());
@@ -46,16 +48,14 @@ public class RpcDataSerializationTest extends AbstractBindingCodecTest {
 
     @Test
     public void testRpcOutputToNormalized() {
-        final GetTopOutputBuilder tb = new GetTopOutputBuilder();
-        tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().withKey(new TopLevelListKey("test")).build()));
-        final GetTopOutput bindingOriginal = tb.build();
+        final GetTopOutput bindingOriginal = new GetTopOutputBuilder()
+                .setTopLevelList(ImmutableMap.of(LIST_KEY, new TopLevelListBuilder().withKey(LIST_KEY).build()))
+                .build();
         final ContainerNode dom = registry.toNormalizedNodeRpcData(bindingOriginal);
         assertNotNull(dom);
         assertEquals(GetTopOutput.QNAME, dom.getIdentifier().getNodeType());
 
         final DataObject bindingDeserialized = registry.fromNormalizedNodeRpcData(GET_TOP_OUTPUT, dom);
         assertEquals(bindingOriginal, bindingDeserialized);
-
     }
-
 }
index 1f3af42563d7293aa2c6f737728bbfc340b6d2b1..f5806f6e0f7e6fca10dc485dec9f028b212c48cc 100644 (file)
@@ -40,6 +40,7 @@ import static org.opendaylight.mdsal.binding.model.util.Types.augmentationTypeFo
 import static org.opendaylight.mdsal.binding.model.util.Types.classType;
 import static org.opendaylight.mdsal.binding.model.util.Types.listTypeFor;
 import static org.opendaylight.mdsal.binding.model.util.Types.listenableFutureTypeFor;
+import static org.opendaylight.mdsal.binding.model.util.Types.mapTypeFor;
 import static org.opendaylight.mdsal.binding.model.util.Types.primitiveVoidType;
 import static org.opendaylight.mdsal.binding.model.util.Types.typeForClass;
 import static org.opendaylight.mdsal.binding.model.util.Types.wildcardTypeFor;
@@ -325,7 +326,14 @@ abstract class AbstractTypeGenerator {
                 keyTypeBuilder = null;
             }
 
-            final ParameterizedType listType = listTypeFor(genType);
+            // Decide whether to generate a List or a Map
+            final ParameterizedType listType;
+            if (keyTypeBuilder != null && !node.isUserOrdered()) {
+                listType = mapTypeFor(keyTypeBuilder, genType);
+            } else {
+                listType = listTypeFor(genType);
+            }
+
             constructGetter(parent, listType, node);
             constructNonnull(parent, listType, node);
 
index 93a7e1dc5c74bce30715cbee097a26500d3df3cb..935b8d339ab7737f345d0c7bfd5e4d6e489ba9b0 100644 (file)
@@ -76,7 +76,7 @@ public class ChoiceCaseGenTypesTest extends AbstractTypesTest {
         containsInterface("LockType", genType);
 
         genType = checkGeneratedType(genTypes, "PartialLock", pcgPref + ".lock.type"); // case
-        containsMethods(genType, new NameTypePattern("getPartialLock", "List<PartialLock>"));
+        containsMethods(genType, new NameTypePattern("getPartialLock", "Map<PartialLockKey,PartialLock>"));
         containsInterface("LockType", genType);
 
         genType = checkGeneratedType(genTypes, "Fingerprint", pcgPref + ".lock.type"); // case
@@ -159,7 +159,7 @@ public class ChoiceCaseGenTypesTest extends AbstractTypesTest {
 
         genType = checkGeneratedType(genTypes, "UnknownFiles", pcgPref
                 + ".netconf.state.datastores.datastore.storage.format"); // case
-        containsMethods(genType, new NameTypePattern("getFiles", "List<Files>"));
+        containsMethods(genType, new NameTypePattern("getFiles", "Map<FilesKey,Files>"));
         containsInterface("StorageFormat", genType);
 
         // case
index 2dec3dae45f8b07373058d1d9cd6cfc4eeb953b3..9f7351e9d0cdd6a77e3adfac6094b525d8c10def 100644 (file)
@@ -156,6 +156,14 @@ public final class Types {
         return parameterizedTypeFor(MAP_TYPE, keyType, valueType);
     }
 
+    public static boolean isMapType(final ParameterizedType type) {
+        return MAP_TYPE.equals(type.getRawType());
+    }
+
+    public static boolean isMapType(final Type type) {
+        return type instanceof ParameterizedType && isMapType((ParameterizedType) type);
+    }
+
     /**
      * Returns an instance of {@link ParameterizedType} describing the typed {@link Set}&lt;V&gt; with concrete type
      * of value.
index 6a2d78888972dd67c7ed67cdcc46602cad8f4add..0194697f1451e56fa2814f533572540d93b7e557 100644 (file)
@@ -275,13 +275,15 @@ class BuilderTemplate extends AbstractBuilderTemplate {
         val returnType = field.returnType
         if (returnType instanceof ParameterizedType) {
             if (Types.isListType(returnType)) {
-                return generateListSetter(field, returnType.actualTypeArguments.get(0))
+                return generateListSetter(field, returnType.actualTypeArguments.get(0), "")
+            } else if (Types.isMapType(returnType)) {
+                return generateListSetter(field, returnType.actualTypeArguments.get(1), ".values()")
             }
         }
         return generateSimpleSetter(field, returnType)
     }
 
-    def private generateListSetter(GeneratedProperty field, Type actualType) '''
+    def private generateListSetter(GeneratedProperty field, Type actualType, String extractor) '''
         «val restrictions = restrictionsForSetter(actualType)»
         «IF restrictions !== null»
             «generateCheckers(field, restrictions, actualType)»
@@ -289,7 +291,7 @@ class BuilderTemplate extends AbstractBuilderTemplate {
         public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
         «IF restrictions !== null»
             if (values != null) {
-               for («actualType.importedName» value : values) {
+               for («actualType.importedName» value : values«extractor») {
                    «checkArgument(field, restrictions, actualType, "value")»
                }
             }
index 7ac0e9cccfffafedac15c8f2d0f550c08a05e264..25796a586190a2bcfb7e9d29b35185cc572a2b7b 100644 (file)
@@ -7,7 +7,8 @@
  */
 package org.opendaylight.mdsal.binding.test.model.util;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 import java.util.Arrays;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
@@ -59,7 +60,7 @@ public final class ListsBindingUtils {
     }
 
     public static Top top(final TopLevelList... listItems) {
-        return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build();
+        return new TopBuilder().setTopLevelList(Maps.uniqueIndex(Arrays.asList(listItems), TopLevelList::key)).build();
     }
 
     public static TopLevelList topLevelList(final TopLevelListKey key) {
@@ -71,9 +72,9 @@ public final class ListsBindingUtils {
     }
 
     public static TreeComplexUsesAugment complexUsesAugment(final ListViaUsesKey... keys) {
-        final ImmutableList.Builder<ListViaUses> listViaUses = ImmutableList.builder();
+        final ImmutableMap.Builder<ListViaUsesKey, ListViaUses> listViaUses = ImmutableMap.builder();
         for (final ListViaUsesKey key : keys) {
-            listViaUses.add(new ListViaUsesBuilder().withKey(key).build());
+            listViaUses.put(key, new ListViaUsesBuilder().withKey(key).build());
         }
         return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build();
     }
index 40fd5c217e661910fea72ee34105b0222f77a755..bb8be6862630a8dc5f6ea97f30779b0f49727b09 100644 (file)
@@ -9,7 +9,7 @@ package org.opendaylight.mdsal.binding.testutils;
 
 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
 
-import java.util.Map;
+import java.util.Map.Entry;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
@@ -43,8 +43,8 @@ public class AssertDataObjectsTest extends AbstractDataBrokerTest {
     @Test
     public void testAssertDataObjectsWithComplexTopWithKey() {
         AssertDataObjects.assertEqualByText(HEADER + "new TopBuilder >> [\n"
-                + "    topLevelList = #[\n"
-                + "        new TopLevelListBuilder >> [\n"
+                + "    topLevelList = #{\n"
+                + "        new TopLevelListKey(\"foo\") -> new TopLevelListBuilder >> [\n"
                 + "            name = \"foo\"\n"
                 + "            addAugmentation(TreeComplexUsesAugment, new TreeComplexUsesAugmentBuilder >> [\n"
                 + "                containerWithUses = new ContainerWithUsesBuilder >> [\n"
@@ -52,7 +52,7 @@ public class AssertDataObjectsTest extends AbstractDataBrokerTest {
                 + "                ]\n"
                 + "            ])\n"
                 + "        ]\n"
-                + "    ]\n"
+                + "    }\n"
                 + "]", ExpectedObjects.top());
     }
 
@@ -86,8 +86,8 @@ public class AssertDataObjectsTest extends AbstractDataBrokerTest {
         String expectedTopText = "import static extension org.opendaylight.mdsal.binding.testutils."
                 + "XtendBuilderExtensions.operator_doubleGreaterThan\n\n"
                 + "new TopBuilder >> [\n"
-                + "    topLevelList = #[\n"
-                + "        new TopLevelListBuilder >> [\n"
+                + "    topLevelList = #{\n"
+                + "        new TopLevelListKey(\"foo\") -> new TopLevelListBuilder >> [\n"
                 + "            name = \"foo\"\n"
                 + "            addAugmentation(TreeComplexUsesAugment, new TreeComplexUsesAugmentBuilder >> [\n"
                 + "                containerWithUses = new ContainerWithUsesBuilder >> [\n"
@@ -95,13 +95,13 @@ public class AssertDataObjectsTest extends AbstractDataBrokerTest {
                 + "                ]\n"
                 + "            ])\n"
                 + "        ]\n"
-                + "    ]\n"
+                + "    }\n"
                 + "]";
         AssertDataObjects.assertEqualByText(expectedTopText, actualTop);
     }
 
     <T extends DataObject> void put(WriteTransaction tx, LogicalDatastoreType store,
-            Map.Entry<InstanceIdentifier<T>, T> obj) {
+            Entry<InstanceIdentifier<T>, T> obj) {
         tx.put(OPERATIONAL, obj.getKey(), obj.getValue());
     }
 
index e2ed4a4d84b34d6906f0c78d38d132a904d0cbe5..cd3e3991042d61673f2c73ed46dd4ad3842332db 100644 (file)
@@ -57,7 +57,7 @@ public class AugmentableExtensionTest extends AbstractDataBrokerTest {
         Top actualTop = readTx.read(OPERATIONAL, id).get().get();
         AssertBeans.assertEqualByText("#{\n}", augmentableExtension.getAugmentations(actualTop));
 
-        TopLevelList topLevelList = actualTop.getTopLevelList().get(0);
+        TopLevelList topLevelList = actualTop.getTopLevelList().values().iterator().next();
         AssertDataObjects.assertEqualByText("#{\n"
                 + "    TreeComplexUsesAugment -> new TreeComplexUsesAugmentBuilder >> [\n"
                 + "        containerWithUses = new ContainerWithUsesBuilder >> [\n"
index be384e90e3abc4147bfaa645ba60d90fc7d46e2b..f673303b04e42fa1e5166017013d1832a60c32e6 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.mdsal.binding.testutils;
 
+import com.google.common.collect.Maps;
 import java.util.Collections;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
@@ -47,12 +48,12 @@ public final class ExpectedObjects {
 
     public static Top top() {
         return new TopBuilder()
-                .setTopLevelList(Collections.singletonList(new TopLevelListBuilder()
+                .setTopLevelList(Maps.uniqueIndex(Collections.singletonList(new TopLevelListBuilder()
                     .setName("foo")
                     .addAugmentation(TreeComplexUsesAugment.class, new TreeComplexUsesAugmentBuilder()
                         .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
                         .build())
-                    .build()))
+                    .build()), TopLevelList::key))
                 .build();
     }
 }
index a0262b557e580d5c0726cf5510441854eab9fc89..b03f237f91c62f8840f70caebc4d733c06cdec79 100644 (file)
@@ -86,7 +86,7 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
         final List<SourceReference> requiredSources = new ArrayList<>();
         final List<SourceReference> librarySources = new ArrayList<>();
 
-        for (Module module : modState.nonnullModule()) {
+        for (Module module : modState.nonnullModule().values()) {
             final SourceReference modRef = sourceRefFor(module, module.getSchema());
 
             // TODO: take deviations/features into account
@@ -97,7 +97,7 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
                 requiredSources.add(modRef);
             }
 
-            for (Submodule submodule : module.nonnullSubmodule()) {
+            for (Submodule submodule : module.nonnullSubmodule().values()) {
                 // Submodules go to library, as they are pulled in as needed
                 librarySources.add(sourceRefFor(submodule, submodule.getSchema()));
             }
index 3c1cd113ee37a3fa714cb20f8745ce07cbdc8893..d293baa69d13ee379192805b4a5f19fc43ea1ba9 100644 (file)
@@ -17,6 +17,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
@@ -36,8 +37,10 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.librar
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModuleRevisionBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.Module;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Datastore;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.DatastoreKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSet;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.SchemaKey;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
 import org.opendaylight.yangtools.rcf8528.data.util.AbstractMountPointContextFactory;
 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
@@ -109,13 +112,13 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
     }
 
     private @NonNull SchemaContext bindLibrary(final @NonNull YangLibrary yangLib) throws YangParserException {
-        final List<Datastore> datastores = yangLib.nonnullDatastore();
+        final Map<DatastoreKey, Datastore> datastores = yangLib.nonnullDatastore();
         checkArgument(!datastores.isEmpty(), "No datastore defined");
 
         final List<SourceReference> requiredSources = new ArrayList<>();
         final List<SourceReference> librarySources = new ArrayList<>();
         final HashSet<String> moduleSet = findModuleSet(yangLib, findSchemaName(datastores, Operational.QNAME));
-        for (ModuleSet modSet : yangLib.nonnullModuleSet()) {
+        for (ModuleSet modSet : yangLib.nonnullModuleSet().values()) {
             if (moduleSet.remove(modSet.getName())) {
                 fillModules(librarySources, requiredSources, modSet);
             }
@@ -131,7 +134,7 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
         final List<SourceReference> librarySources = new ArrayList<>();
 
         for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
-                .Module module : modState.nonnullModule()) {
+                .Module module : modState.nonnullModule().values()) {
             final SourceReference modRef = sourceRefFor(module, module.getSchema());
 
             // TODO: take deviations/features into account
@@ -143,7 +146,7 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
             }
 
             for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
-                    .module.Submodule submodule : module.nonnullSubmodule()) {
+                    .module.Submodule submodule : module.nonnullSubmodule().values()) {
                 // Submodules go to library, as they are pulled in as needed
                 librarySources.add(sourceRefFor(submodule, submodule.getSchema()));
             }
@@ -152,8 +155,8 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
         return resolver.resolveSchemaContext(librarySources, requiredSources);
     }
 
-    private String findSchemaName(final List<Datastore> datastores, final QName qname) {
-        final Iterator<Datastore> it = datastores.iterator();
+    private String findSchemaName(final Map<DatastoreKey, Datastore> datastores, final QName qname) {
+        final Iterator<Datastore> it = datastores.values().iterator();
         final Datastore ds = it.next();
 
         // FIXME: This is ugly, but it is the most compatible thing we can do without knowing the exact requested
@@ -186,28 +189,27 @@ final class MountPointContextFactoryImpl extends AbstractMountPointContextFactor
     }
 
     private static HashSet<String> findModuleSet(final YangLibrary yangLib, final String schemaName) {
-        for (Schema schema : yangLib.nonnullSchema()) {
-            if (schemaName.equals(schema.getName())) {
-                return new HashSet<>(schema.getModuleSet());
-            }
+        final Schema schema = yangLib.nonnullSchema().get(new SchemaKey(schemaName));
+        if (schema == null) {
+            throw new IllegalArgumentException("Failed to find moduleSet for " + schemaName);
         }
-        throw new IllegalArgumentException("Failed to find moduleSet for " + schemaName);
+        return new HashSet<>(schema.getModuleSet());
     }
 
     private static void fillModules(final List<SourceReference> librarySources,
             final List<SourceReference> requiredSources, final ModuleSet modSet) {
         // TODO: take deviations/features into account
 
-        for (ImportOnlyModule mod : modSet.nonnullImportOnlyModule()) {
+        for (ImportOnlyModule mod : modSet.nonnullImportOnlyModule().values()) {
             fillSource(librarySources, mod.getName(), ImportOnlyModuleRevisionBuilder.toYangCommon(mod.getRevision()),
                 mod.getLocation());
-            mod.nonnullSubmodule().forEach(sub -> {
+            mod.nonnullSubmodule().values().forEach(sub -> {
                 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
             });
         }
-        for (Module mod : modSet.nonnullModule()) {
+        for (Module mod : modSet.nonnullModule().values()) {
             fillSource(requiredSources, mod.getName(), toYangCommon(mod.getRevision()), mod.getLocation());
-            mod.nonnullSubmodule().forEach(sub -> {
+            mod.nonnullSubmodule().values().forEach(sub -> {
                 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
             });
         }