
org.eclipse.xtext.xbase.lib.IterableExtensions Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.xbase.lib;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Functions.Function2;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure2;
import org.eclipse.xtext.xbase.lib.internal.BooleanFunctionDelegate;
import org.eclipse.xtext.xbase.lib.internal.FunctionDelegate;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* This is an extension library for {@link Iterable iterables}.
*
* @author Sven Efftinge - Initial contribution and API
* @author Sebastian Zarnekow
*/
@GwtCompatible(emulated = true)
public class IterableExtensions {
/**
*
* Concatenates two iterables into a single iterable. The returned iterable has an iterator that traverses the
* elements in {@code a}, followed by the elements in {@code b}. The resulting iterable is effectivly a view on the
* source iterables. That is, the source iterators are not polled until necessary and the result will reflect
* changes in the sources.
*
*
* The returned iterable's iterator supports {@code remove()} when the corresponding input iterator supports it.
*
*
* @param a
* the first iterable. May not be null
.
* @param b
* the second iterable. May not be null
.
* @return a combined iterable. Never null
.
*/
@Pure
@Inline(value="$3.$4concat($1, $2)", imported=Iterables.class)
public static Iterable operator_plus(Iterable extends T> a, Iterable extends T> b) {
return Iterables.concat(a, b);
}
/**
* Finds the first element in the given iterable that fulfills the predicate. If none is found or the iterable is
* empty, null
is returned.
*
* @param iterable
* the iterable. May not be null
.
* @param predicate
* the predicate. May not be null
.
* @return the first element in the iterable for which the given predicate returns true
, returns
* null
if no element matches the predicate or the iterable is empty.
*/
public static T findFirst(Iterable iterable, Function1 super T, Boolean> predicate) {
if (predicate == null)
throw new NullPointerException("predicate");
for (T t : iterable) {
if (predicate.apply(t))
return t;
}
return null;
}
/**
* Finds the last element in the given iterable that fulfills the predicate. If none is found or the iterable is
* empty, null
is returned.
*
* @param iterable
* the iterable. May not be null
.
* @param predicate
* the predicate. May not be null
.
* @return the last element in the iterable for which the given predicate returns true
, returns
* null
if no element matches the predicate or the iterable is empty.
*/
public static T findLast(Iterable iterable, Functions.Function1 super T, Boolean> predicate) {
if (predicate == null)
throw new NullPointerException("predicate");
if (iterable instanceof List>) {
List list = (List) iterable;
ListIterator iterator = list.listIterator(list.size());
while (iterator.hasPrevious()) {
T t = iterator.previous();
if (predicate.apply(t))
return t;
}
return null;
} else {
T result = null;
for (T t : iterable) {
if (predicate.apply(t))
result = t;
}
return result;
}
}
/**
* Returns the first element in the given iterable or null
if empty.
*
* @param iterable
* the iterable. May not be null
.
* @return the first element in the iterable or null
.
*/
public static T head(Iterable iterable) {
Iterator iterator = iterable.iterator();
if (iterator.hasNext())
return iterator.next();
return null;
}
/**
* Returns a view on this iterable that contains all the elements except the first.
*
* @param iterable
* the iterable. May not be null
.
* @return an iterable with all elements except the first. Never null
.
* @see #drop(Iterable, int)
*/
public static Iterable tail(final Iterable iterable) {
return drop(iterable, 1);
}
/**
* Returns the last element in the given iterable or null
if empty.
*
* @param iterable
* the iterable. May not be null
.
* @return the last element in the iterable or null
.
*/
public static T last(Iterable iterable) {
if (iterable instanceof List>) {
List list = (List) iterable;
if (list.isEmpty())
return null;
return list.get(list.size() - 1);
} else if (iterable instanceof SortedSet) {
SortedSet sortedSet = (SortedSet) iterable;
if (sortedSet.isEmpty())
return null;
return sortedSet.last();
} else {
T result = null;
for (T t : iterable) {
result = t;
}
return result;
}
}
/**
* Returns a view on this iterable that provides at most the first count
entries.
*
* @param iterable
* the iterable. May not be null
.
* @param count
* the number of elements that should be returned at most.
* @return an iterable with count
elements. Never null
.
* @throws IllegalArgumentException
* if count
is negative.
*/
public static Iterable take(final Iterable iterable, final int count) {
if (iterable == null)
throw new NullPointerException("iterable");
if (count < 0)
throw new IllegalArgumentException("Cannot take a negative number of elements. Argument 'count' was: "
+ count);
if (count == 0)
return Collections.emptyList();
return new Iterable() {
public Iterator iterator() {
return new AbstractIterator() {
private int remaining = count;
private Iterator delegate = iterable.iterator();
@Override
protected T computeNext() {
if (remaining <= 0)
return endOfData();
if (!delegate.hasNext())
return endOfData();
remaining--;
return delegate.next();
}
};
}
};
}
/**
* Returns a view on this iterable that provides all elements except the first count
entries.
*
* @param iterable
* the iterable. May not be null
.
* @param count
* the number of elements that should be dropped.
* @return an iterable without the first count
elements. Never null
.
* @throws IllegalArgumentException
* if count
is negative.
*/
@Pure
public static Iterable drop(final Iterable iterable, final int count) {
if (iterable == null)
throw new NullPointerException("iterable");
if (count == 0)
return iterable;
if (count < 0)
throw new IllegalArgumentException("Cannot drop a negative number of elements. Argument 'count' was: "
+ count);
return new Iterable() {
public Iterator iterator() {
return new AbstractIterator() {
private Iterator delegate = iterable.iterator();
{
int i = count;
while (i > 0 && delegate.hasNext()) {
delegate.next();
i--;
}
}
@Override
protected T computeNext() {
if (!delegate.hasNext())
return endOfData();
return delegate.next();
}
};
}
};
}
/**
* Returns {@code true} if one or more elements in {@code iterable} satisfy the predicate.
*
* @param iterable
* the iterable. May not be null
.
* @param predicate
* the predicate. May not be null
.
* @return true
if one or more elements in {@code iterable} satisfy the predicate.
*/
public static boolean exists(Iterable iterable, Function1 super T, Boolean> predicate) {
if (predicate == null)
throw new NullPointerException("predicate");
for (T t : iterable) {
if (predicate.apply(t))
return true;
}
return false;
}
/**
* Returns {@code true} if every element in {@code iterable} satisfies the predicate. If {@code iterable} is empty,
* {@code true} is returned. In other words, false
is returned if at least one element fails to fulfill
* the predicate.
*
* @param iterable
* the iterable. May not be null
.
* @param predicate
* the predicate. May not be null
.
* @return true
if one or more elements in {@code iterable} satisfy the predicate.
*/
public static boolean forall(Iterable iterable, Function1 super T, Boolean> predicate) {
if (predicate == null)
throw new NullPointerException("predicate");
for (T t : iterable) {
if (!predicate.apply(t))
return false;
}
return true;
}
/**
* Returns the elements of {@code unfiltered} that satisfy a predicate. The resulting iterable's iterator does not
* support {@code remove()}. The returned iterable is a view on the original elements. Changes in the unfiltered
* original are reflected in the view.
*
* @param unfiltered
* the unfiltered iterable. May not be null
.
* @param predicate
* the predicate. May not be null
.
* @return an iterable that contains only the elements that fulfill the predicate. Never null
.
*/
@Pure
public static Iterable filter(Iterable unfiltered, Function1 super T, Boolean> predicate) {
return Iterables.filter(unfiltered, new BooleanFunctionDelegate(predicate));
}
/**
* Returns all instances of class {@code type} in {@code unfiltered}. The returned iterable has elements whose class
* is {@code type} or a subclass of {@code type}. The returned iterable's iterator does not support {@code remove()}
* . The returned iterable is a view on the original elements. Changes in the unfiltered original are reflected in
* the view.
*
* @param unfiltered
* the unfiltered iterable. May not be null
.
* @param type
* the type of elements desired
* @return an unmodifiable iterable containing all elements of the original iterable that were of the requested
* type. Never null
.
*/
@GwtIncompatible("Class.isInstance")
@Pure
@Inline(value="$3.$4filter($1, $2)", imported=Iterables.class)
public static Iterable filter(Iterable> unfiltered, Class type) {
return Iterables.filter(unfiltered, type);
}
/**
* Returns a new iterable filtering any null references.
*
* @param unfiltered
* the unfiltered iterable. May not be null
.
* @return an unmodifiable iterable containing all elements of the original iterable without any null
references. Never null
.
*/
@Pure
public static Iterable filterNull(Iterable unfiltered) {
return Iterables.filter(unfiltered, Predicates.notNull());
}
/**
* Returns an iterable that performs the given {@code transformation} for each element of {@code original} when
* requested. The mapping is done lazily. That is, subsequent iterations of the elements in the iterable will
* repeatedly apply the transformation.
*
* The returned iterable's iterator supports {@code remove()} if the provided iterator does. After a successful
* {@code remove()} call, {@code original} no longer contains the corresponding element.
*
* @param original
* the original iterable. May not be null
.
* @param transformation
* the transformation. May not be null
.
* @return an iterable that provides the result of the transformation. Never null
.
*/
@Pure
public static Iterable map(Iterable original, Function1 super T, ? extends R> transformation) {
return Iterables.transform(original, new FunctionDelegate(transformation));
}
/**
* Combines multiple iterables into a single iterable. The returned iterable has an iterator that traverses the
* elements of each iterable in {@code inputs}. The input iterators are not polled until necessary.
*
*
* The returned iterable's iterator supports {@code remove()} when the corresponding input iterator supports it. The
* methods of the returned iterable may throw {@code NullPointerException} if any of the input iterators are null.
*
* @param inputs
* the to be flattened iterables. May not be null
.
* @return an iterable that provides the concatenated values of the input elements. Never null
.
*/
@Inline(value="$2.$3concat($1)", imported=Iterables.class)
public static Iterable flatten(Iterable extends Iterable extends T>> inputs) {
return Iterables.concat(inputs);
}
/**
* Applies {@code procedure} for each element of the given iterable.
*
* @param iterable
* the iterable. May not be null
.
* @param procedure
* the procedure. May not be null
.
*/
public static void forEach(Iterable iterable, Procedure1 super T> procedure) {
if (procedure == null)
throw new NullPointerException("procedure");
for (T t : iterable) {
procedure.apply(t);
}
}
/**
* Applies {@code procedure} for each element of the given iterable.
* The procedure takes the element and a loop counter. If the counter would overflow, {@link Integer#MAX_VALUE}
* is returned for all subsequent elements. The first element is at index zero.
*
* @param iterable
* the iterable. May not be null
.
* @param procedure
* the procedure. May not be null
.
* @since 2.3
*/
public static void forEach(Iterable iterable, Procedure2 super T, ? super Integer> procedure) {
if (procedure == null)
throw new NullPointerException("procedure");
int i = 0;
for (T t : iterable) {
procedure.apply(t, i);
if (i != Integer.MAX_VALUE)
i++;
}
}
/**
* Returns the concatenated string representation of the elements in the given iterable. No delimiter is used.
*
* @param iterable
* the iterable. May not be null
.
* @return the string representation of the iterable's elements. Never null
.
* @see #join(Iterable, CharSequence, Function1)
*/
public static String join(Iterable> iterable) {
return join(iterable, "");
}
/**
* Returns the concatenated string representation of the elements in the given iterable. The {@code separator} is
* used to between each pair of entries in the input. The string null
is used for null
* entries in the input.
*
* @param iterable
* the iterable. May not be null
.
* @param separator
* the separator. May not be null
.
* @return the string representation of the iterable's elements. Never null
.
* @see #join(Iterable, CharSequence, Function1)
*/
public static String join(Iterable> iterable, CharSequence separator) {
return Joiner.on(separator.toString()).useForNull("null").join(iterable);
}
/**
* Returns the concatenated string representation of the elements in the given iterable. The {@code function} is
* used to compute the string for each element. The {@code separator} is used to between each pair of entries in the
* input. The string null
is used if the function yields null
as the string representation
* for an entry.
*
* @param iterable
* the iterable. May not be null
.
* @param separator
* the separator. May not be null
.
* @param function
* the function that is used to compute the string representation of a single element. May not be
* null
.
* @return the string representation of the iterable's elements. Never null
.
*/
public static String join(Iterable iterable, CharSequence separator,
Functions.Function1 super T, ? extends CharSequence> function) {
if (separator == null)
throw new NullPointerException("separator");
if (function == null)
throw new NullPointerException("function");
StringBuilder result = new StringBuilder();
Iterator iterator = iterable.iterator();
while (iterator.hasNext()) {
T next = iterator.next();
CharSequence elementToString = function.apply(next);
result.append(elementToString);
if (iterator.hasNext())
result.append(separator);
}
return result.toString();
}
/**
* Returns the concatenated string representation of the elements in the given iterable. The {@code function} is
* used to compute the string for each element. The {@code separator} is used to between each pair of entries in the
* input. The string null
is used if the function yields null
as the string representation
* for an entry.
*
* @param iterable
* the iterable. May not be null
.
* @param before
* prepends the resulting string if the iterable contains at least one element. May be null
which is equivalent to passing an empty string.
* @param separator
* the separator. May be null
which is equivalent to passing an empty string.
* @param after
* appended to the resulting string if the iterable contain at least one element. May be null
which is equivalent to passing an empty string.
* @param function
* the function that is used to compute the string representation of a single element. May not be
* null
.
* @return the string representation of the iterable's elements. Never null
.
*/
public static String join(Iterable iterable, CharSequence before, CharSequence separator, CharSequence after,
Functions.Function1 super T, ? extends CharSequence> function) {
if (function == null)
throw new NullPointerException("function");
StringBuilder result = new StringBuilder();
Iterator iterator = iterable.iterator();
boolean notEmpty = iterator.hasNext();
if (notEmpty && before != null)
result.append(before);
while (iterator.hasNext()) {
T next = iterator.next();
CharSequence elementToString = function.apply(next);
result.append(elementToString);
if (iterator.hasNext() && separator != null)
result.append(separator);
}
if (notEmpty && after != null)
result.append(after);
return result.toString();
}
/**
* Determines whether two iterables contain equal elements in the same order. More specifically, this method returns
* {@code true} if {@code iterable} and {@code other} contain the same number of elements and every element of
* {@code iterable} is equal to the corresponding element of {@code other}.
*
* @param iterable
* an iterable. May not be null
.
* @param other
* an iterable. May not be null
.
* @return true
if the two iterables contain equal elements in the same order.
*/
public static boolean elementsEqual(Iterable> iterable, Iterable> other) {
return Iterables.elementsEqual(iterable, other);
}
/**
* Determines if the given iterable is null
or contains no elements.
*
* @param iterable
* the to-be-queried iterable. May be null
.
* @return {@code true} if the iterable is null
or contains no elements
*/
public static boolean isNullOrEmpty(Iterable> iterable) {
return iterable == null || isEmpty(iterable);
}
/**
* Determines if the given iterable contains no elements.
*
* @param iterable
* the to-be-queried iterable. May not be null
.
* @return {@code true} if the iterable contains no elements
* @see #isNullOrEmpty(Iterable)
*/
public static boolean isEmpty(Iterable> iterable) {
if (iterable instanceof Collection>)
return ((Collection>) iterable).isEmpty();
return !iterable.iterator().hasNext();
}
/**
* Returns the number of elements in {@code iterable}.
*
* @param iterable
* the iterable. May not be null
.
* @return the number of elements in {@code iterable}.
*/
public static int size(Iterable> iterable) {
return Iterables.size(iterable);
}
/**
*
* Applies the combinator {@code function} to all elements of the iterable in turn.
*
*
* One of the function parameters is an element of the iterable, and the other is the result of previous application
* of the function. The seed of the operation is the first element in the iterable. The second value is computed by
* applying the function to the seed together with the second element of the iterable. The third value is computed
* from the previous result together with the third element and so on. In other words, the previous result of each
* step is taken and passed together with the next element to the combinator function.
*
*
* If the iterable is empty, null
is returned.
*
*
* More formally, given an iterable {@code [a, b, c, d]} and a function {@code f}, the result of {@code reduce} is
* f(f(f(a, b), c), d)
*
*
* @param iterable
* the to-be-reduced iterable. May not be null
.
* @param function
* the combinator function. May not be null
.
* @return the last result of the applied combinator function or null
for the empty input.
*/
public static T reduce(Iterable extends T> iterable, Function2 super T, ? super T, ? extends T> function) {
if (function == null)
throw new NullPointerException("function");
Iterator extends T> iterator = iterable.iterator();
if (iterator.hasNext()) {
T result = iterator.next();
while (iterator.hasNext()) {
result = function.apply(result, iterator.next());
}
return result;
} else {
return null;
}
}
/**
*
* Applies the combinator {@code function} to all elements of the iterable in turn and uses {@code seed} as the
* start value.
*
*
* One of the function parameters is an element of the iterable, and the other is the result of previous application
* of the function. The seed of the operation is explicitly passed to {@link #fold(Iterable, Object, Function2)
* fold}. The first computed value is the result of the applied function for {@code seed} and the first element of
* the iterable. This intermediate result together with the second element of the iterable produced the next result
* and so on.
*
*
* {@link #fold(Iterable, Object, Function2) fold} is similar to {@link #reduce(Iterable, Function2) reduce} but
* allows a {@code seed} value and the combinator {@code function} may be asymmetric. It takes {@code T and R} and
* returns {@code R}.
*
* If the iterable is empty, seed
is returned.
*
*
* More formally, given an iterable {@code [a, b, c, d]}, a seed {@code initial} and a function {@code f}, the
* result of {@code fold} is f(f(f(f(initial, a), b), c), d)
*
*
* @param iterable
* the to-be-folded iterable. May not be null
.
* @param seed
* the initial value. May be null
.
* @param function
* the combinator function. May not be null
.
* @return the last result of the applied combinator function or seed
for the empty input.
*/
public static R fold(Iterable iterable, R seed, Function2 super R, ? super T, ? extends R> function) {
R result = seed;
Iterator iterator = iterable.iterator();
while (iterator.hasNext()) {
result = function.apply(result, iterator.next());
}
return result;
}
/**
* Returns a list that contains all the entries of the given iterable in the same order. If the iterable is of type
* {@link List}, itself is returned. Therefore an unchecked cast is performed.
* In all other cases, the result list is a copy of the iterable.
*
* @param iterable
* the iterable. May not be null
.
* @return a list with the same entries as the given iterable. May be the same as the given iterable iff it
* implements {@link List}, otherwise a copy is returned. Never null
.
*/
@Beta
public static List toList(Iterable iterable) {
if (iterable instanceof List>) {
List result = (List) iterable;
return result;
}
return Lists.newArrayList(iterable);
}
/**
* Returns a set that contains all the unique entries of the given iterable in the order of their appearance. If the
* iterable is of type {@link Set}, itself is returned. Therefore an unchecked cast is performed.
* In all other cases, the result set is a copy of the iterable with stable order.
*
* @param iterable
* the iterable. May not be null
.
* @return a set with the unique entries of the given iterable. May be the same as the given iterable iff it
* implements {@link Set}, otherwise a copy is returned. Never null
.
*/
@Beta
public static Set toSet(Iterable iterable) {
if (iterable instanceof Set>) {
Set result = (Set) iterable;
return result;
}
return Sets.newLinkedHashSet(iterable);
}
/**
* Returns a map for which the {@link Map#values} are computed by the given function, and each key is an element in
* the given {@code keys}. If the iterable contains equal keys more than once, the last one will be contained in the
* map. The map is computed eagerly. That is, subsequent changes in the keys are not reflected by the map.
*
* @param keys
* the keys to use when constructing the {@code Map}. May not be null
.
* @param computeValues
* the function used to produce the values for each key. May not be null
.
* @return a map mapping each entry in the given iterable to the corresponding result when evaluating the function
* {@code computeValues}.
*/
public static Map toInvertedMap(Iterable extends K> keys, Function1 super K, V> computeValues) {
Map result = Maps.newLinkedHashMap();
for (K k : keys) {
result.put(k, computeValues.apply(k));
}
return result;
}
/**
* Returns a map for which the {@link Map#values} are the given elements in the given order, and each key is the
* product of invoking a supplied function {@code computeKeys} on its corresponding value. If the function produces
* the same key for different values, the last one will be contained in the map.
*
* @param values
* the values to use when constructing the {@code Map}. May not be null
.
* @param computeKeys
* the function used to produce the key for each value. May not be null
.
* @return a map mapping the result of evaluating the function {@code keyFunction} on each value in the input
* collection to that value
*/
public static Map toMap(Iterable extends V> values, Function1 super V, K> computeKeys) {
if (computeKeys == null)
throw new NullPointerException("computeKeys");
Map result = Maps.newLinkedHashMap();
for (V v : values) {
result.put(computeKeys.apply(v), v);
}
return result;
}
/**
* Creates a sorted list that contains the items of the given iterable. The resulting list is in ascending order,
* according to the natural ordering of the elements in the iterable.
*
* @param iterable
* the items to be sorted. May not be null
.
* @return a sorted list as a shallow copy of the given iterable.
* @see Collections#sort(List)
* @see #sort(Iterable, Comparator)
* @see #sortBy(Iterable, Function1)
* @see ListExtensions#sortInplace(List)
*/
public static > List sort(Iterable iterable) {
return ListExtensions.sortInplace(Lists.newArrayList(iterable));
}
/**
* Creates a sorted list that contains the items of the given iterable. The resulting list is sorted according to
* the order induced by the specified comparator.
*
* @param iterable
* the items to be sorted. May not be null
.
* @param comparator
* the comparator to be used. May be null
to indicate that the natural ordering of the
* elements should be used.
* @return a sorted list as a shallow copy of the given iterable.
* @see Collections#sort(List, Comparator)
* @see #sort(Iterable)
* @see #sortBy(Iterable, Function1)
* @see ListExtensions#sortInplace(List, Comparator)
*/
public static List sort(Iterable iterable, Comparator super T> comparator) {
return ListExtensions.sortInplace(Lists.newArrayList(iterable), comparator);
}
/**
* Creates a sorted list that contains the items of the given iterable. The resulting list is sorted according to
* the order induced by applying a key function to each element which yields a comparable criteria.
*
* @param iterable
* the elements to be sorted. May not be null
.
* @param key
* the key function to-be-used. May not be null
.
* @return a sorted list as a shallow copy of the given iterable.
* @see #sort(Iterable)
* @see #sort(Iterable, Comparator)
* @see ListExtensions#sortInplaceBy(List, Function1)
*/
public static > List sortBy(Iterable iterable,
final Functions.Function1 super T, C> key) {
return ListExtensions.sortInplaceBy(Lists.newArrayList(iterable), key);
}
}