/*
 * Copyright (c) 2016 Claudiu Soroiu
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ro.derbederos.hamcrest;

import org.hamcrest.Matcher;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.BaseStream;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.hamcrest.Matchers.emptyIterable;
import static ro.derbederos.hamcrest.LambdaMatchers.map;

/**
 * This class provides a set of mapping matchers for java 8 streams.
 * Basically it contains matchers that convert streams to {@link Iterable} and allow {@link Iterable}
 * matchers to be used.
 * <p>
 * Examples:
 * <pre>
 * assertThat(stream, mapStream(Person::getName, hasItem(startsWith("Ana"))));
 *
 * assertThat(stream, toIterable(hasItem("Ana Pop"));
 *
 * assertThat(Stream.empty(), emptyStream());
 * </pre>
 *
 * @since 0.1
 */
public final class StreamMatchers {

    private StreamMatchers() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    /**
     * Utility method that creates a matcher that converts a stream of <code>&lt;T&gt;</code> to an iterable of
     * <code>&lt;U&gt;</code> allowing us to use an iterable matcher on the result of the mapping function.
     * <p>
     * Example:
     * <pre>
     * assertThat(stream, mapStream(Person::getName, hasItem(startsWith("Ana"))));
     * </pre>
     *
     * @param mapper  The function that transforms every element of the input stream.
     * @param matcher The matcher to be applied on the resulting iterable.
     * @param <T>     The type of the elements in the input stream.
     * @param <U>     The type of the result of the <code>mapper</code> function.
     * @since 0.1
     */
    public static <T, U> Matcher<Stream<? extends T>> mapStream(Function<T, U> mapper,
            Matcher<Iterable<? super U>> matcher) {
        return map(stream -> stream.map(mapper::apply).collect(Collectors.toList()), matcher);
    }

    /**
     * Creates a {@link Matcher} that applies an iterable <code>matcher</code> on the input stream. It is an adapter method.
     * <p>
     * Example:
     * <pre>
     * assertThat(stream, toIterable(hasItem("Ana Pop"));
     * </pre>
     *
     * @param matcher The matcher to be applied on the resulting iterable.
     * @param <T>     The type of the elements in the input stream.
     * @since 0.1
     */
    public static <T> Matcher<Stream<? extends T>> toIterable(Matcher<Iterable<? super T>> matcher) {
        return mapStream(a -> a, matcher);
    }

    /**
     * Creates a {@link Matcher} that checks if the given {@link BaseStream} is empty.
     * <p>
     * Examples:
     * <pre>
     * assertThat(Stream.empty(), emptyStream());
     * </pre>
     *
     * @param <T> The type of the stream elements.
     * @param <S> The type of the stream implementing {@code BaseStream}.
     * @since 0.1
     */
    public static <T, S extends BaseStream<T, S>> Matcher<BaseStream<T, S>> emptyStream() {
        return map(baseStreamToIterable(), emptyIterable());
    }

    private static <S extends BaseStream<T, S>, T> Function<BaseStream<T, S>, Iterable<T>> baseStreamToIterable() {
        return stream -> {
            List<T> target = new ArrayList<>();
            stream.iterator().forEachRemaining(target::add);
            return target;
        };
    }
}

@@ -1394,17 +1394,17 @@
 since 0.
-1
+6
 %0A */%0Apub
@@ -2476,15 +2476,8 @@
 pper
-::apply
 ).co
@@ -3073,14 +3073,27 @@
 eam(
-a -%3E a
+Function.identity()
 , ma
@@ -3576,16 +3576,32 @@
 urn map(
+StreamMatchers::
 baseStre
@@ -3612,18 +3612,16 @@
 Iterable
-()
 , emptyI
@@ -3693,35 +3693,8 @@
 , T%3E
- Function%3CBaseStream%3CT, S%3E,
  Ite
@@ -3701,17 +3701,16 @@
 rable%3CT%3E
-%3E
  baseStr
@@ -3727,43 +3727,35 @@
 ble(
-) %7B%0A        return stream -%3E %7B%0A    
+BaseStream%3CT, S%3E stream) %7B%0A
     
@@ -3802,20 +3802,16 @@
         
- 
-   
 stream.i
@@ -3855,28 +3855,24 @@
 d);%0A        
-   
- 
 return targe
@@ -3878,19 +3878,8 @@
 et;%0A
-        %7D;%0A
     

use method references instead of lambdas