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

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 a, Iterable 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 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 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 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 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 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 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> 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 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 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 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 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 iterable, Function2 function) { if (function == null) throw new NullPointerException("function"); Iterator 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 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 keys, Function1 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 values, Function1 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 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 key) { return ListExtensions.sortInplaceBy(Lists.newArrayList(iterable), key); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy