All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.helpers.collection.Iterables Maven / Gradle / Ivy

/**
 * Copyright (c) 2002-2013 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.helpers.collection;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import org.neo4j.helpers.Function;
import org.neo4j.helpers.Predicate;

/**
 * TODO
 */
public final class Iterables
{
    private static Iterable EMPTY = new Iterable()
    {
        Iterator iterator = new Iterator()
        {
            @Override
            public boolean hasNext()
            {
                return false;
            }

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

            @Override
            public void remove()
            {
            }
        };

        @Override
        public Iterator iterator()
        {
            return iterator;
        }
    };

    public static  Iterable empty()
    {
        return EMPTY;
    }

    public static  Iterable constant( final T item )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                return new Iterator()
                {
                    @Override
                    public boolean hasNext()
                    {
                        return true;
                    }

                    @Override
                    public T next()
                    {
                        return item;
                    }

                    @Override
                    public void remove()
                    {
                    }
                };
            }
        };
    }

    public static  Iterable limit( final int limitItems, final Iterable iterable )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                final Iterator iterator = iterable.iterator();

                return new Iterator()
                {
                    int count;

                    @Override
                    public boolean hasNext()
                    {
                        return count < limitItems && iterator.hasNext();
                    }

                    @Override
                    public T next()
                    {
                        count++;
                        return iterator.next();
                    }

                    @Override
                    public void remove()
                    {
                        iterator.remove();
                    }
                };
            }
        };
    }

    public static  Function, Iterable> limit( final int limitItems )
    {
        return new Function, Iterable>()
        {
            @Override
            public Iterable apply( Iterable ts )
            {
                return limit( limitItems, ts );
            }
        };
    }

    public static  Iterable unique( final Iterable iterable )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                final Iterator iterator = iterable.iterator();

                return new Iterator()
                {
                    Set items = new HashSet();
                    T nextItem;

                    @Override
                    public boolean hasNext()
                    {
                        while ( iterator.hasNext() )
                        {
                            nextItem = iterator.next();
                            if ( items.add( nextItem ) )
                            {
                                return true;
                            }
                        }

                        return false;
                    }

                    @Override
                    public T next()
                    {
                        if ( nextItem == null && !hasNext() )
                        {
                            throw new NoSuchElementException();
                        }

                        return nextItem;
                    }

                    @Override
                    public void remove()
                    {
                    }
                };
            }
        };
    }

    public static > C addAll( C collection, Iterable iterable )
    {
        for ( T item : iterable )
        {
            collection.add( item );
        }

        return collection;
    }

    public static long count( Iterable iterable )
    {
        long c = 0;
        Iterator iterator = iterable.iterator();
        while ( iterator.hasNext() )
        {
            iterator.next();
            c++;
        }
        return c;
    }

    public static  Iterable filter( Predicate specification, Iterable i )
    {
        return new FilterIterable( i, specification );
    }

    public static  X first( Iterable i )
    {
        Iterator iter = i.iterator();
        if ( iter.hasNext() )
        {
            return iter.next();
        }
        else
        {
            return null;
        }
    }

    public static  X single( Iterable i )
    {
        Iterator iter = i.iterator();
        if ( iter.hasNext() )
        {
            X result = iter.next();

            if ( iter.hasNext() )
            {
                throw new IllegalArgumentException( "More than one element in iterable" );
            }

            return result;
        }
        else
        {
            throw new IllegalArgumentException( "No elements in iterable" );
        }
    }

    public static  Iterable skip( final int skip, final Iterable iterable )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                Iterator iterator = iterable.iterator();

                for ( int i = 0; i < skip; i++ )
                {
                    if ( iterator.hasNext() )
                    {
                        iterator.next();
                    }
                    else
                    {
                        return Iterables.empty().iterator();
                    }
                }

                return iterator;
            }
        };
    }

    public static  X last( Iterable i )
    {
        Iterator iter = i.iterator();
        X item = null;
        while ( iter.hasNext() )
        {
            item = iter.next();
        }

        return item;
    }

    public static  Iterable reverse( Iterable iterable )
    {
        List list = toList( iterable );
        Collections.reverse( list );
        return list;
    }

    public static  boolean matchesAny( Predicate specification, Iterable iterable )
    {
        boolean result = false;

        for ( T item : iterable )
        {
            if ( specification.accept( item ) )
            {
                result = true;
                break;
            }
        }

        return result;
    }

    public static  boolean matchesAll( Predicate specification, Iterable iterable )
    {
        boolean result = true;
        for ( T item : iterable )
        {
            if ( !specification.accept( item ) )
            {
                result = false;
            }
        }

        return result;
    }

    public static > Iterable flatten( I... multiIterator )
    {
        return new FlattenIterable( Arrays.asList( multiIterator ) );
    }

    public static > Iterable flattenIterables( Iterable multiIterator )
    {
        return new FlattenIterable( multiIterator );
    }

    public static  Iterable mix( final Iterable... iterables )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                final Iterable> iterators = toList( map( new Function, Iterator>()
                {
                    @Override
                    public Iterator apply( Iterable iterable )
                    {
                        return iterable.iterator();
                    }
                }, Arrays.asList( iterables ) ) );

                return new Iterator()
                {
                    Iterator> iterator;

                    Iterator iter;

                    @Override
                    public boolean hasNext()
                    {
                        for ( Iterator iterator : iterators )
                        {
                            if ( iterator.hasNext() )
                            {
                                return true;
                            }
                        }

                        return false;
                    }

                    @Override
                    public T next()
                    {
                        if ( iterator == null )
                        {
                            iterator = iterators.iterator();
                        }

                        while ( iterator.hasNext() )
                        {
                            iter = iterator.next();

                            if ( iter.hasNext() )
                            {
                                return iter.next();
                            }
                        }

                        iterator = null;

                        return next();
                    }

                    @Override
                    public void remove()
                    {
                        if ( iter != null )
                        {
                            iter.remove();
                        }
                    }
                };
            }
        };
    }

    public static  Iterable map( Function function, Iterable from )
    {
        return new MapIterable( from, function );
    }

    public static  Iterable iterableEnumeration( Enumeration enumeration )
    {
        List list = new ArrayList();
        while ( enumeration.hasMoreElements() )
        {
            T item = enumeration.nextElement();
            list.add( item );
        }

        return list;
    }

    public static  Iterable iterable( C... items )
    {
        return (Iterable) Arrays.asList( items );
    }

    public static  Iterable cast( Iterable iterable )
    {
        Iterable iter = iterable;
        return iter;
    }

    public static  Iterable concat( Iterable... iterables )
    {
        return concat( Arrays.asList( (Iterable[]) iterables ) );
    }

    public static  Iterable concat( final Iterable> iterables )
    {
        return new CombiningIterable( iterables );
    }

    public static  Function cast()
    {
        return new Function()
        {
            @Override
            public TO apply( FROM from )
            {
                return (TO) from;
            }
        };
    }

    public static  TO fold( Function function, Iterable i )
    {
        return last( map( function, i ) );
    }

    public static  Iterable prepend( final C item, final Iterable iterable )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                return new Iterator()
                {
                    T first = item;
                    Iterator iterator;

                    @Override
                    public boolean hasNext()
                    {
                        if ( first != null )
                        {
                            return true;
                        }
                        else
                        {
                            if ( iterator == null )
                            {
                                iterator = iterable.iterator();
                            }
                        }

                        return iterator.hasNext();
                    }

                    @Override
                    public T next()
                    {
                        if ( first != null )
                        {
                            try
                            {
                                return first;
                            }
                            finally
                            {
                                first = null;
                            }
                        }
                        else
                        {
                            return iterator.next();
                        }
                    }

                    @Override
                    public void remove()
                    {
                    }
                };
            }
        };
    }

    public static  Iterable append( final C item, final Iterable iterable )
    {
        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                final Iterator iterator = iterable.iterator();

                return new Iterator()
                {
                    T last = item;

                    @Override
                    public boolean hasNext()
                    {
                        if ( iterator.hasNext() )
                        {
                            return true;
                        }
                        else
                        {
                            return last != null;
                        }
                    }

                    @Override
                    public T next()
                    {
                        if ( iterator.hasNext() )
                        {
                            return iterator.next();
                        }
                        else
                        {
                            try
                            {
                                return last;
                            }
                            finally
                            {
                                last = null;
                            }
                        }
                    }

                    @Override
                    public void remove()
                    {
                    }
                };
            }
        };
    }

    public static  Iterable cache( Iterable iterable )
    {
        return new CacheIterable( iterable );
    }

    public static  List toList( Iterable iterable )
    {
        return addAll( new ArrayList(), iterable );
    }

    public static Object[] toArray( Iterable iterable )
    {
        return toArray( Object.class, iterable );
    }

    public static  T[] toArray( Class componentType, Iterable iterable )
    {
        if ( iterable == null )
        {
            return null;
        }

        List list = toList( iterable );
        return list.toArray( (T[]) Array.newInstance( componentType, list.size() ) );
    }

    private static class MapIterable
            implements Iterable
    {
        private final Iterable from;
        private final Function function;

        public MapIterable( Iterable from, Function function )
        {
            this.from = from;
            this.function = function;
        }

        @Override
        public Iterator iterator()
        {
            return new MapIterator( from.iterator(), function );
        }

        static class MapIterator
                implements Iterator
        {
            private final Iterator fromIterator;
            private final Function function;

            public MapIterator( Iterator fromIterator, Function function )
            {
                this.fromIterator = fromIterator;
                this.function = function;
            }

            @Override
            public boolean hasNext()
            {
                return fromIterator.hasNext();
            }

            @Override
            public TO next()
            {
                FROM from = fromIterator.next();

                return function.apply( from );
            }

            @Override
            public void remove()
            {
                fromIterator.remove();
            }
        }
    }

    private static class FilterIterable
            implements Iterable
    {
        private final Iterable iterable;

        private final Predicate specification;

        public FilterIterable( Iterable iterable, Predicate specification )
        {
            this.iterable = iterable;
            this.specification = specification;
        }

        @Override
        public Iterator iterator()
        {
            return new FilterIterator( iterable.iterator(), specification );
        }

        static class FilterIterator
                implements Iterator
        {
            private final Iterator iterator;

            private final Predicate specification;

            private T currentValue;
            boolean finished = false;
            boolean nextConsumed = true;

            public FilterIterator( Iterator iterator, Predicate specification )
            {
                this.specification = specification;
                this.iterator = iterator;
            }

            public boolean moveToNextValid()
            {
                boolean found = false;
                while ( !found && iterator.hasNext() )
                {
                    T currentValue = iterator.next();
                    boolean satisfies = specification.accept( currentValue );

                    if ( satisfies )
                    {
                        found = true;
                        this.currentValue = currentValue;
                        nextConsumed = false;
                    }
                }
                if ( !found )
                {
                    finished = true;
                }
                return found;
            }

            @Override
            public T next()
            {
                if ( !nextConsumed )
                {
                    nextConsumed = true;
                    return currentValue;
                }
                else
                {
                    if ( !finished )
                    {
                        if ( moveToNextValid() )
                        {
                            nextConsumed = true;
                            return currentValue;
                        }
                    }
                }
                return null;
            }

            @Override
            public boolean hasNext()
            {
                return !finished &&
                        (!nextConsumed || moveToNextValid());
            }

            @Override
            public void remove()
            {
            }
        }
    }

    private static class FlattenIterable>
            implements Iterable
    {
        private final Iterable iterable;

        public FlattenIterable( Iterable iterable )
        {
            this.iterable = iterable;
        }

        @Override
        public Iterator iterator()
        {
            return new FlattenIterator( iterable.iterator() );
        }

        static class FlattenIterator>
                implements Iterator
        {
            private final Iterator iterator;
            private Iterator currentIterator;

            public FlattenIterator( Iterator iterator )
            {
                this.iterator = iterator;
                currentIterator = null;
            }

            @Override
            public boolean hasNext()
            {
                if ( currentIterator == null )
                {
                    if ( iterator.hasNext() )
                    {
                        I next = iterator.next();
                        currentIterator = next.iterator();
                    }
                    else
                    {
                        return false;
                    }
                }

                while ( !currentIterator.hasNext() &&
                        iterator.hasNext() )
                {
                    currentIterator = iterator.next().iterator();
                }

                return currentIterator.hasNext();
            }

            @Override
            public T next()
            {
                return currentIterator.next();
            }

            @Override
            public void remove()
            {
                if ( currentIterator == null )
                {
                    throw new IllegalStateException();
                }

                currentIterator.remove();
            }
        }
    }

    private static class CacheIterable
            implements Iterable
    {
        private final Iterable iterable;
        private Iterable cache;

        private CacheIterable( Iterable iterable )
        {
            this.iterable = iterable;
        }

        @Override
        public Iterator iterator()
        {
            if ( cache != null )
            {
                return cache.iterator();
            }

            final Iterator source = iterable.iterator();

            return new Iterator()
            {
                List iteratorCache = new ArrayList();

                @Override
                public boolean hasNext()
                {
                    boolean hasNext = source.hasNext();
                    if ( !hasNext )
                    {
                        cache = iteratorCache;
                    }
                    return hasNext;
                }

                @Override
                public T next()
                {
                    T next = source.next();
                    iteratorCache.add( next );
                    return next;
                }

                @Override
                public void remove()
                {

                }
            };
        }
    }
    
    /**
     * Returns the index of the first occurrence of the specified element
     * in this iterable, or -1 if this iterable does not contain the element.
     * More formally, returns the lowest index i such that
     * (o==null ? get(i)==null : o.equals(get(i))),
     * or -1 if there is no such index.
     */
    public static  int indexOf( T itemToFind, Iterable iterable )
    {
        if ( itemToFind == null )
        {
            int index = 0;
            for ( T item : iterable )
            {
                if ( item == null )
                    return index;
                index++;
            }
        }
        else
        {
            int index = 0;
            for ( T item : iterable )
            {
                if ( itemToFind.equals( item ) )
                    return index;
                index++;
            }
        }
        return -1;
    }

    public static  Iterable option( final T item )
    {
        if ( item == null )
            return empty();

        return new Iterable()
        {
            @Override
            public Iterator iterator()
            {
                return new PrefetchingIterator()
                {
                    private boolean returned;

                    @Override
                    protected T fetchNextOrNull()
                    {
                        try
                        {
                            return !returned ? item : null;
                        }
                        finally
                        {
                            returned = true;
                        }
                    }
                };
            }
        };
    }
}