// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.stream;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * The purpose of this class is to fill gaps in the Java {@link Collectors} api
 * by offering convenient ways to retrieve implementations of {@link Collector}.
 *
 * <p>For example, to get a collector that accumulates elements into an insertion
 * order map:
 * <pre>{@code
 *
 *     Map<String, Person> idToPerson =
 *         persons.stream().collect(toLinkedMap(Person::id, Functions.identity());
 * }</pre>
 *
 * @author gjoranv
 */
public class CustomCollectors {

    private CustomCollectors() { }

    /**
     * Returns a {@code Collector} that accumulates elements into a {@code Map}
     * with predictable iteration order. This a convenience that can be used
     * instead of calling {@link java.util.stream.Collectors#toMap(Function, Function, BinaryOperator, Supplier)}.
     * with a merger that throws upon duplicate keys.
     *
     * @param keyMapper Mapping function to produce keys.
     * @param valueMapper Mapping function to produce values.
     * @param <T> Type of the input elements.
     * @param <K> Output type of the key mapping function.
     * @param <U> Output type of the value mapping function.
     * @return A collector which collects elements into a map with predictable iteration order.
     * @throws DuplicateKeyException If two elements map to the same key.
     */
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toLinkedMap(Function<? super T, ? extends K> keyMapper,
                                          Function<? super T, ? extends U> valueMapper) {
        return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), LinkedHashMap::new);
    }

    /**
     * Returns a {@code Collector} that accumulates elements into a {@code Map}
     * created by the given supplier. This a convenience that can be used
     * instead of calling {@link java.util.stream.Collectors#toMap(Function, Function, BinaryOperator, Supplier)}.
     * with a merger that throws upon duplicate keys.
     *
     * @param keyMapper Mapping function to produce keys.
     * @param valueMapper Mapping function to produce values.
     * @param mapSupplier Supplier of a new map.
     * @param <T> Type of the input elements.
     * @param <K> Output type of the key mapping function.
     * @param <U> Output type of the value mapping function.
     * @param <M> Type of the resulting map.
     * @return A collector which collects elements into a map created by the given supplier.
     * @throws DuplicateKeyException If two elements map to the same key.
     */
    public static <T, K, U, M extends Map<K,U>>
    Collector<T, ?, M> toCustomMap(Function<? super T, ? extends K> keyMapper,
                                   Function<? super T, ? extends U> valueMapper,
                                   Supplier<M> mapSupplier) {
        return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), mapSupplier);
    }


    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new DuplicateKeyException(u); };
    }

    public static class DuplicateKeyException extends IllegalStateException {
        private static final long serialVersionUID = 1L;

        DuplicateKeyException(Object key) {
            super(String.format("Duplicate keys: %s", key));
        }
    }
}

@@ -616,32 +616,48 @@
 to a
-n ins
+ map with%0A * predictable it
 er
+a
 tion
-%0A *
  order
- map
 :%0A *
@@ -996,38 +996,35 @@
 %0A     * 
-with predictable it
+that provides ins
 er
-a
 tion ord
@@ -1025,16 +1025,26 @@
 on order
+ iteration
 . This a
@@ -1602,33 +1602,21 @@
 ap with 
-predictable it
+ins
 er
-a
 tion ord
@@ -1617,16 +1617,26 @@
 on order
+ iteration
 .%0A     *

rephrase comments