/*
 * Copyright (c) 2013-2014 the original author or authors
 *
 * 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 io.werval.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * Utilities to work with Iterables.
 */
public final class Iterables
{
    private static final class EmptyIterable
        implements Iterable
    {
        @Override
        public Iterator iterator()
        {
            return new EmptyIterator();
        }
    }

    private static final class EmptyIterator
        implements Iterator
    {
        @Override
        public boolean hasNext()
        {
            return false;
        }

        @Override
        public Object next()
        {
            throw new NoSuchElementException();
        }
    }

    private static final class SkipIterable<T>
        implements Iterable<T>
    {
        private final int skip;
        private final Iterable iterable;

        SkipIterable( int skip, Iterable iterable )
        {
            this.skip = skip;
            this.iterable = iterable;
        }

        @Override
        public Iterator iterator()
        {
            Iterator<T> iterator = iterable.iterator();
            for( int i = 0; i < skip; i++ )
            {
                if( iterator.hasNext() )
                {
                    iterator.next();
                }
                else
                {
                    return Iterables.<T>empty().iterator();
                }
            }
            return iterator;
        }
    }

    private static final Iterable EMPTY = new EmptyIterable();

    /**
     * Empty Iterable.
     *
     * @param <T> Parameterized item type
     *
     * @return An empty Iterable
     */
    @SuppressWarnings( "unchecked" )
    public static <T> Iterable<T> empty()
    {
        return EMPTY;
    }

    /**
     * First item or null.
     *
     * @param <T>      Parameterized item type
     * @param iterable Iterable
     *
     * @return First item or null
     */
    public static <T> T first( Iterable<T> iterable )
    {
        Iterator<T> it = iterable.iterator();
        return it.hasNext() ? it.next() : null;
    }

    /**
     * Count the number of items.
     *
     * @param iterable Iterable
     *
     * @return Number of items in the Iterable
     */
    public static long count( Iterable<?> iterable )
    {
        long count = 0;
        for( Object item : iterable )
        {
            count++;
        }
        return count;
    }

    /**
     * Skip some items.
     *
     * @param <T>      Parameterized item type
     * @param skip     How many items to skip
     * @param iterable Iterable
     *
     * @return An Iterable starting after skipped items
     */
    public static <T> Iterable<T> skip( final int skip, final Iterable<T> iterable )
    {
        return new SkipIterable<>( skip, iterable );
    }

    /**
     * Add all items of an Iterable into a Collection.
     *
     * @param <T>        Parameterized item type
     * @param <C>        Parameterized collection type
     * @param collection Collection to add to
     * @param iterable   Items to add to the given collection
     *
     * @return Given collection
     */
    public static <T, C extends Collection<T>> C addAll( C collection, Iterable<? extends T> iterable )
    {
        for( T item : iterable )
        {
            collection.add( item );
        }
        return collection;
    }

    /**
     * Create an Iterable of given items.
     *
     * @param <T>   Parameterized item type
     * @param items Items to turn in an Iterable
     *
     * @return An Iterable of given items
     */
    @SuppressWarnings( "unchecked" )
    public static <T> Iterable<T> asIterable( T... items )
    {
        return Arrays.asList( items );
    }

    /**
     * Create a new List with all items of an Iterable.
     *
     * @param <T>      Parameterized item type
     * @param iterable Iterable
     *
     * @return A new List with all items from the given Iterable
     */
    public static <T> List<T> toList( Iterable<T> iterable )
    {
        return addAll( new ArrayList<T>(), iterable );
    }

    private Iterables()
    {
    }
}

@@ -813,16 +813,52 @@
 ception;
+%0Aimport java.util.function.Function;
 %0A%0A/**%0A *
@@ -2184,32 +2184,1091 @@
        %7D%0A    %7D%0A%0A
+    private static final class MapIterable%3CI, O%3E%0A        implements Iterable%3CO%3E%0A    %7B%0A        private final Iterable%3CI%3E input;%0A        private final Function%3CI, O%3E function;%0A%0A        private MapIterable( Iterable%3CI%3E input, Function%3CI, O%3E function )%0A        %7B%0A            this.input = input;%0A            this.function = function;%0A        %7D%0A%0A        @Override%0A        public Iterator%3CO%3E iterator()%0A        %7B%0A            return new MapIterator%3C%3E( input.iterator(), function );%0A        %7D%0A    %7D%0A%0A    private static final class MapIterator%3CI, O%3E%0A        implements Iterator%3CO%3E%0A    %7B%0A        private final Iterator%3CI%3E input;%0A        private final Function%3CI, O%3E function;%0A%0A        private MapIterator( Iterator%3CI%3E input, Function%3CI, O%3E function )%0A        %7B%0A            this.input = input;%0A            this.function = function;%0A        %7D%0A%0A        @Override%0A        public boolean hasNext()%0A        %7B%0A            return input.hasNext();%0A        %7D%0A%0A        @Override%0A        public O next()%0A        %7B%0A            return function.apply( input.next() );%0A        %7D%0A    %7D%0A%0A
     private stat
@@ -4540,32 +4540,32 @@
 terable )%0A    %7B%0A
-
         return n
@@ -4600,32 +4600,185 @@
 rable );%0A    %7D%0A%0A
+    public static %3CI, O%3E Iterable%3CO%3E map( Iterable%3CI%3E input, Function%3CI, O%3E function )%0A    %7B%0A        return new MapIterable%3C%3E( input, function );%0A    %7D%0A%0A
     /**%0A     * A

Add util.Iterables::map