/*
* 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;
import java.util.function.Function;
/**
* 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<T> iterable;
SkipIterable( int skip, Iterable<T> iterable )
{
this.skip = skip;
this.iterable = iterable;
}
@Override
public Iterator<T> 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 class MapIterable<I, O>
implements Iterable<O>
{
private final Iterable<I> input;
private final Function<I, O> function;
private MapIterable( Iterable<I> input, Function<I, O> function )
{
this.input = input;
this.function = function;
}
@Override
public Iterator<O> iterator()
{
return new MapIterator<>( input.iterator(), function );
}
}
private static final class MapIterator<I, O>
implements Iterator<O>
{
private final Iterator<I> input;
private final Function<I, O> function;
private MapIterator( Iterator<I> input, Function<I, O> function )
{
this.input = input;
this.function = function;
}
@Override
public boolean hasNext()
{
return input.hasNext();
}
@Override
public O next()
{
return function.apply( input.next() );
}
}
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 );
}
public static <I, O> Iterable<O> map( Iterable<I> input, Function<I, O> function )
{
return new MapIterable<>( input, function );
}
/**
* 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 );
}
public static <T, I extends Iterable<T>> I notEmptyOrNull( I iterable )
{
return iterable != null && count( iterable ) > 0 ? iterable : null;
}
private Iterables()
{
}
}