class RoutesAnswererUtil {

  public static <T extends AbstractRouteDecorator>
      Map<RouteRowKey, Map<RouteRowSecondaryKey, SortedSet<RouteRowAttribute>>> groupRoutes(
          SortedMap<String, SortedMap<String, GenericRib<T>>> ribs,
          Set<String> matchingNodes,
          @Nullable Prefix network,
          String vrfRegex,
          RoutingProtocolSpecifier protocolSpec,
          @Nullable Map<Ip, Set<String>> ipOwners) {
    Map<RouteRowKey, Map<RouteRowSecondaryKey, SortedSet<RouteRowAttribute>>> routesGroups =
        new HashMap<>();
    Pattern compiledVrfRegex = Pattern.compile(vrfRegex);
    ribs.forEach(
        (node, vrfMap) -> {
          if (matchingNodes.contains(node)) {
            vrfMap.forEach(
                (vrfName, rib) -> {
                  if (compiledVrfRegex.matcher(vrfName).matches()) {
                    rib.getRoutes().stream()
                        .filter(
                            route ->
                                (network == null || network.equals(route.getNetwork()))
                                    && protocolSpec.getProtocols().contains(route.getProtocol()))
                        .forEach(
                            route ->
                                routesGroups
                                    .computeIfAbsent(
                                        new RouteRowKey(node, vrfName, route.getNetwork()),
                                        k -> new HashMap<>())
                                    .computeIfAbsent(
                                        new RouteRowSecondaryKey(
                                            route.getNextHopIp(),
                                            route.getProtocol().protocolName()),
                                        k -> new TreeSet<>())
                                    .add(
                                        RouteRowAttribute.builder()
                                            .setNextHop(
                                                computeNextHopNode(route.getNextHopIp(), ipOwners))
                                            .setNextHopInterface(route.getNextHopInterface())
                                            .setAdminDistance(route.getAdministrativeCost())
                                            .setMetric(route.getMetric())
                                            .setTag(route.getTag())
                                            .build()));
                  }
                });
          }
        });
    return routesGroups;
  }

}