Intializers for the new vlan model
authorMarek Gradzki <[email protected]>
Thu, 2 Jun 2016 11:23:39 +0000 (13:23 +0200)
committerMarek Gradzki <[email protected]>
Mon, 6 Jun 2016 12:52:33 +0000 (12:52 +0000)
Change-Id: I513f0b190e9d9e669663a9a216e7c72b1ebeb10d
Signed-off-by: Marek Gradzki <[email protected]>
v3po/api/src/main/yang/v3po.yang
v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/initializers/InterfacesInitializer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/initializers/SubInterfaceInitializationUtils.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/interfacesstate/InterfaceUtils.java
v3po/vpp-translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/v3po/util/NamingContext.java

index 18b82a9..f60070a 100644 (file)
@@ -42,36 +42,6 @@ module v3po {
       "This type is used by to reference a bridged virtual interface";
   }
 
-  // todo remove from v3po
-  /*typedef vlan-type {
-    type enumeration {
-      enum 802dot1q;
-      enum 802dot1ad;
-    }
-  }*/
-
-  // todo remove from v3po
-  /*typedef tag-rewrite-operation {
-    type enumeration {
-      enum disabled;
-      enum push-1;
-      enum push-2;
-      enum pop-1;
-      enum pop-2;
-      enum translate-1-to-1;
-      enum translate-1-to-2;
-      enum translate-2-to-1;
-      enum translate-2-to-2;
-    }
-  }*/
-
-  // todo remove from v3po
-  //typedef vlan-tag {
-  //  type uint16 {
-  //    range "1..4094";
-  //  }
-  //}
-
   identity vxlan-tunnel {
     base if:interface-type;
   }
@@ -152,41 +122,6 @@ module v3po {
     }
   }
 
-  // todo remove from v3po
-  /*grouping sub-interface-base-attributes {
-    leaf identifier {
-      type uint32;
-    }
-    leaf vlan-type {
-      type vlan-type;
-      default '802dot1q';
-    }
-    leaf number-of-tags {
-      type uint8 {
-        range "0..2";
-      }
-      default 1;
-    }
-    leaf outer-id {
-      type vlan-tag;
-    }
-    leaf inner-id {
-      type vlan-tag;
-    }
-    leaf match-any-outer-id {
-      type empty;
-    }
-    leaf match-any-inner-id {
-      type empty;
-    }
-    leaf exact-match {
-      type empty;
-    }
-    leaf default-subif {
-      type empty;
-    }
-  }*/
-
   grouping tap-interface-base-attributes {
     leaf tap-name {
       type string;
@@ -309,24 +244,6 @@ module v3po {
     }
   }
 
-  // todo remove from v3po
-  /* grouping vlan-tag-rewrite-attributes {
-    leaf rewrite-operation {
-      type tag-rewrite-operation;
-      default 'disabled';
-    }
-    leaf first-pushed {
-      type vlan-type;
-      default '802dot1q';
-    }
-    leaf tag1 {
-      type vlan-tag;
-    }
-    leaf tag2 {
-      type vlan-tag;
-    }
-  }*/
-
   grouping l2-base-attributes {
       description
       "Parameters for configuring Layer2 features on interfaces.";
@@ -380,15 +297,6 @@ module v3po {
     // 2. Only this augmentation with combination of ifc type is trigger to do something for vpp, what if user only configures base interface stuff ? + We need to get leaves defined by ietf-interfaces when we are processing this augment
     // 3. The ietf-interfaces model does not define groupings which makes types reuse difficult
 
-    /*
-    container sub-interface {
-      when "../if:type = 'v3po:sub-interface'";
-      leaf super-interface {
-        type if:interface-ref;
-      }
-      uses sub-interface-base-attributes;
-    }*/
-
     container tap {
       when "../if:type = 'v3po:tap'";
       uses tap-interface-base-attributes;
@@ -422,10 +330,6 @@ module v3po {
       "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
 
       uses l2-base-attributes;
-
-      /*container vlan-tag-rewrite {
-        uses vlan-tag-rewrite-attributes;
-      }*/
     }
 
     container vxlan-gpe {
@@ -479,15 +383,6 @@ module v3po {
       type string;
     }
 
-    /*
-    container sub-interface {
-      when "../if:type = 'v3po:sub-interface'";
-      leaf super-interface {
-        type if:interface-state-ref;
-      }
-      uses sub-interface-base-attributes;
-    }*/
-
     container tap {
       when "../if:type = 'v3po:tap'";
       uses tap-interface-base-attributes;
@@ -520,10 +415,6 @@ module v3po {
       "not (../if:ipv6[if:enabled = 'true']/if:address/if:ip))";
 
       uses l2-base-attributes;
-
-      /* container vlan-tag-rewrite {
-        uses vlan-tag-rewrite-attributes;
-      }*/
     }
   }
 
index dbfed04..b712e15 100644 (file)
@@ -21,13 +21,15 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
 import io.fd.honeycomb.v3po.translate.read.ChildReader;
 import io.fd.honeycomb.v3po.translate.write.ChildWriter;
-import io.fd.honeycomb.v3po.translate.SubtreeManager;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
@@ -40,6 +42,21 @@ public final class RWUtils {
 
     private RWUtils() {}
 
+    /**
+     * Collector expecting only a single resulting item from a stream
+     */
+    public static<T> Collector<T,?,T> singleItemCollector() {
+        return Collectors.collectingAndThen(
+                Collectors.toList(),
+                list -> {
+                    if (list.size() != 1) {
+                        throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected");
+                    }
+                    return list.get(0);
+                }
+        );
+    }
+
     /**
      * Find next item in ID after provided type
      */
index a41a6e8..5a0e35d 100644 (file)
@@ -16,6 +16,8 @@
 
 package io.fd.honeycomb.v3po.translate.v3po.initializers;
 
+import static io.fd.honeycomb.v3po.translate.v3po.initializers.SubInterfaceInitializationUtils.initializeSubinterfaceStateAugmentation;
+
 import com.google.common.collect.Lists;
 import io.fd.honeycomb.v3po.vpp.data.init.AbstractDataTreeConverter;
 import javax.annotation.Nonnull;
@@ -34,20 +36,16 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanGpeVni;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.EthernetBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder;
-// import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.SubInterfaceBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.TapBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VhostUserBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.VxlanGpeBuilder;
-// import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.VlanTagRewriteBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Ethernet;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.L2;
-// import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.SubInterface;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Tap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VhostUser;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Vxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VxlanGpe;
-// import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.l2.VlanTagRewrite;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.Interconnection;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBased;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.l2.base.attributes.interconnection.BridgeBasedBuilder;
@@ -89,10 +87,19 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
         builder.setName(input.getName());
         builder.setType(input.getType());
         builder.setEnabled(AdminStatus.Up.equals(input.getAdminStatus()));
-        // builder.setLinkUpDownTrapEnable(); not present in interfaces-state
+        // builder.setLinkUpDownTrapEnable(); TODO not present in interfaces-state
+
+        initializeVppInterfaceStateAugmentation(input, builder);
+        initializeSubinterfaceStateAugmentation(input, builder);
+
+        return builder.build();
+    }
 
+    private static void initializeVppInterfaceStateAugmentation(
+            final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface input,
+            final InterfaceBuilder builder) {
         final VppInterfaceStateAugmentation vppIfcAugmentation =
-            input.getAugmentation(VppInterfaceStateAugmentation.class);
+                input.getAugmentation(VppInterfaceStateAugmentation.class);
         if(vppIfcAugmentation != null) {
             final VppInterfaceAugmentationBuilder augmentBuilder = new VppInterfaceAugmentationBuilder();
             builder.setDescription(vppIfcAugmentation.getDescription());
@@ -122,12 +129,6 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
                 setL2(augmentBuilder, l2);
             }
 
-            // FIXME new vlan model
-//            final SubInterface subInterface = vppIfcAugmentation.getSubInterface();
-//            if(subInterface != null) {
-//                setSubinterface(augmentBuilder, subInterface);
-//            }
-
             final Ethernet ethernet = vppIfcAugmentation.getEthernet();
             if(ethernet != null) {
                 setEthernet(augmentBuilder, ethernet);
@@ -137,38 +138,8 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
 
             builder.addAugmentation(VppInterfaceAugmentation.class, augmentBuilder.build());
         }
-
-        return builder.build();
     }
 
-    // FIXME vlan new modele
-//    private static void setSubinterface(final VppInterfaceAugmentationBuilder augmentBuilder,
-//                                        final SubInterface subInterface) {
-//        final SubInterfaceBuilder subIfcBuilder = new SubInterfaceBuilder();
-//
-//        if(subInterface.isDefaultSubif() != null) {
-//            subIfcBuilder.setDefaultSubif(subInterface.isDefaultSubif());
-//        }
-//        if(subInterface.isExactMatch() != null) {
-//            subIfcBuilder.setExactMatch(subInterface.isExactMatch());
-//        }
-//        if(subInterface.isMatchAnyInnerId() != null) {
-//            subIfcBuilder.setMatchAnyInnerId(subInterface.isMatchAnyInnerId());
-//        }
-//        if(subInterface.isMatchAnyOuterId() != null) {
-//            subIfcBuilder.setMatchAnyOuterId(subInterface.isMatchAnyOuterId());
-//        }
-//
-//        subIfcBuilder.setIdentifier(subInterface.getIdentifier());
-//        subIfcBuilder.setInnerId(subInterface.getInnerId());
-//        subIfcBuilder.setNumberOfTags(subInterface.getNumberOfTags());
-//        subIfcBuilder.setOuterId(subInterface.getOuterId());
-//        subIfcBuilder.setSuperInterface(subInterface.getSuperInterface());
-//        subIfcBuilder.setVlanType(subInterface.getVlanType());
-//
-//        augmentBuilder.setSubInterface(subIfcBuilder.build());
-//    }
-
     private static void setEthernet(final VppInterfaceAugmentationBuilder augmentBuilder, final Ethernet ethernet) {
         final EthernetBuilder ethernetBuilder = new EthernetBuilder();
         ethernetBuilder.setMtu(ethernet.getMtu());
@@ -195,17 +166,6 @@ public class InterfacesInitializer extends AbstractDataTreeConverter<InterfacesS
             }
         }
 
-        // FIXME new vlan model
-//        final VlanTagRewrite vlanTagRewrite = l2.getVlanTagRewrite();
-//        if(vlanTagRewrite != null) {
-//            final VlanTagRewriteBuilder vlanTagRewriteBuilder = new VlanTagRewriteBuilder();
-//            vlanTagRewriteBuilder.setFirstPushed(vlanTagRewrite.getFirstPushed());
-//            vlanTagRewriteBuilder.setRewriteOperation(vlanTagRewrite.getRewriteOperation());
-//            vlanTagRewriteBuilder.setTag1(vlanTagRewrite.getTag1());
-//            vlanTagRewriteBuilder.setTag2(vlanTagRewrite.getTag2());
-//            l2Builder.setVlanTagRewrite(vlanTagRewriteBuilder.build());
-//        }
-
         augmentBuilder.setL2(l2Builder.build());
     }
 
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/initializers/SubInterfaceInitializationUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/v3po/translate/v3po/initializers/SubInterfaceInitializationUtils.java
new file mode 100644 (file)
index 0000000..3975bf3
--- /dev/null
@@ -0,0 +1,66 @@
+package io.fd.honeycomb.v3po.translate.v3po.initializers;
+
+import com.google.common.collect.Lists;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.SubinterfaceAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.SubinterfaceAugmentationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.SubinterfaceStateAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces.state._interface.SubInterfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces.state._interface.sub.interfaces.SubInterface;
+
+/**
+ * Utility class for sub interface initialization
+ */
+final class SubInterfaceInitializationUtils {
+
+    private SubInterfaceInitializationUtils() {
+        throw new UnsupportedOperationException("Utility class cannot be instantiated");
+    }
+
+    static void initializeSubinterfaceStateAugmentation(
+            final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface input,
+            final InterfaceBuilder builder) {
+        final SubinterfaceStateAugmentation subIfcAugmentation =
+                input.getAugmentation(SubinterfaceStateAugmentation.class);
+        if (subIfcAugmentation != null) {
+            final SubinterfaceAugmentationBuilder augmentBuilder = new SubinterfaceAugmentationBuilder();
+
+            final SubInterfaces subInterfaces = subIfcAugmentation.getSubInterfaces();
+            if (subInterfaces != null) {
+                setSubInterfaces(augmentBuilder, subInterfaces);
+            }
+
+            builder.addAugmentation(SubinterfaceAugmentation.class, augmentBuilder.build());
+        }
+    }
+
+    private static void setSubInterfaces(final SubinterfaceAugmentationBuilder augmentBuilder,
+                                         final SubInterfaces operationalData) {
+
+        final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.SubInterfacesBuilder
+                subInterfacesCfgBuilder =
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.SubInterfacesBuilder();
+        subInterfacesCfgBuilder.setSubInterface(Lists.transform(operationalData.getSubInterface(),
+                SubInterfaceInitializationUtils::convertSubInterface));
+        augmentBuilder.setSubInterfaces(subInterfacesCfgBuilder.build());
+    }
+
+    private static org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterface convertSubInterface(
+            final SubInterface operationalData) {
+        org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterfaceBuilder subInterfaceCfgBuilder =
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterfaceBuilder();
+
+        subInterfaceCfgBuilder.setEnabled(Interface.AdminStatus.Up.equals(operationalData.getAdminStatus()));
+        subInterfaceCfgBuilder.setIdentifier(operationalData.getIdentifier());
+        subInterfaceCfgBuilder.setKey(new SubInterfaceKey(operationalData.getIdentifier()));
+        subInterfaceCfgBuilder.setL2(operationalData.getL2());
+        subInterfaceCfgBuilder.setMatch(operationalData.getMatch());
+        subInterfaceCfgBuilder.setTags(operationalData.getTags());
+        subInterfaceCfgBuilder.setVlanType(operationalData.getVlanType());
+
+        return subInterfaceCfgBuilder.build();
+    }
+
+}
index e0bae97..756b298 100644 (file)
@@ -19,14 +19,15 @@ package io.fd.honeycomb.v3po.translate.v3po.interfacesstate;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static io.fd.honeycomb.v3po.translate.v3po.interfacesstate.InterfaceCustomizer.getCachedInterfaceDump;
 import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.toList;
 
 import com.google.common.base.Preconditions;
 import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
 import io.fd.honeycomb.v3po.translate.v3po.util.TranslateUtils;
 import java.math.BigInteger;
 import java.util.Map;
 import java.util.concurrent.CompletionStage;
+import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd;
@@ -59,6 +60,9 @@ public final class InterfaceUtils {
 
     private static final int PHYSICAL_ADDRESS_LENGTH = 6;
 
+    private static final Collector<SwInterfaceDetails, ?, SwInterfaceDetails> SINGLE_ITEM_COLLECTOR =
+            RWUtils.singleItemCollector();
+
     private InterfaceUtils() {
         throw new UnsupportedOperationException("This utility class cannot be instantiated");
     }
@@ -147,11 +151,11 @@ public final class InterfaceUtils {
     /**
      * Queries VPP for interface description given interface key.
      *
-     * @param futureJvpp    VPP Java Future API
-     * @param name          interface name
-     * @param index         VPP index of the interface
-     * @param ctx           per-tx scope context containing cached dump with all the interfaces. If the cache is not
-     *                      available or outdated, another dump will be performed.
+     * @param futureJvpp VPP Java Future API
+     * @param name       interface name
+     * @param index      VPP index of the interface
+     * @param ctx        per-tx scope context containing cached dump with all the interfaces. If the cache is not
+     *                   available or outdated, another dump will be performed.
      * @return SwInterfaceDetails DTO or null if interface was not found
      * @throws IllegalArgumentException If interface cannot be found
      */
@@ -198,13 +202,8 @@ public final class InterfaceUtils {
         }
 
         // SwInterfaceDump's name filter does prefix match, so we need additional filtering:
-        final SwInterfaceDetails iface = ifaces.swInterfaceDetails.stream().filter(d -> d.swIfIndex == index).collect(
-                Collectors.collectingAndThen(toList(), l -> {
-                    if (l.size() == 1) {
-                        return l.get(0);
-                    }
-                    throw new RuntimeException();
-                }));
+        final SwInterfaceDetails iface =
+                ifaces.swInterfaceDetails.stream().filter(d -> d.swIfIndex == index).collect(SINGLE_ITEM_COLLECTOR);
         allInterfaces.put(index, iface); // update the cache
         return iface;
     }
index 34c8104..bcc980d 100644 (file)
@@ -21,8 +21,8 @@ import static com.google.common.base.Preconditions.checkState;
 
 import com.google.common.base.Optional;
 import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
 import java.util.stream.Collector;
-import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContextKey;
@@ -47,17 +47,7 @@ public final class NamingContext implements AutoCloseable {
     private final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.contexts.NamingContext, NamingContextKey>
         namingContextIid;
 
-    /**
-     * Collector expecting only a single resulting item from a stream
-     */
-    private static final Collector<Mapping, ?, Mapping> SINGLE_ITEM_COLLECTOR = Collectors.collectingAndThen(
-        Collectors.toList(),
-        list -> {
-            if (list.size() != 1) {
-                throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected");
-            }
-            return list.get(0);
-        });
+    private static final Collector<Mapping, ?, Mapping> SINGLE_ITEM_COLLECTOR = RWUtils.singleItemCollector();
 
     /**
      * Create new naming context