/*******************************************************************************
* Copyright (c) 2007 DSource.org 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
*
* Contributors:
* Bruno Medeiros - initial implementation
*******************************************************************************/
package melnorme.utilbox.misc;
import static melnorme.utilbox.core.CoreUtil.areEqual;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import melnorme.utilbox.collections.ArrayList2;
import melnorme.utilbox.collections.HashSet2;
import melnorme.utilbox.collections.Indexable;
import melnorme.utilbox.core.fntypes.FunctionX;
/**
* Utils for creation, query, and modification of Collection classes.
*/
public class CollectionUtil {
@SuppressWarnings("rawtypes")
public static final Indexable EMPTY_INDEXABLE = new ArrayList2<>();
public static <T> Indexable<T> nullToEmpty(Indexable<T> indexable) {
return Indexable.nullToEmpty(indexable);
}
/** @return a new {@link ArrayList} from given collection
* (a null collection is considered as if it's an empty one). */
public static <E> ArrayList2<E> createArrayList(Iterable<? extends E> iterable) {
return new ArrayList2<>(nullToEmpty(iterable));
}
/** @return a new {@link LinkedList} from given collection
* (a null collection is considered as if it's an empty one). */
public static <E> LinkedList<E> createLinkedList(Iterable<? extends E> iterable) {
return addAll2(new LinkedList<E>(), nullToEmpty(iterable));
}
/** @return a new {@link HashSet} from given collection
* (a null collection is considered as if it's an empty one). */
public static <E> HashSet2<E> createHashSet(Iterable<? extends E> iterable) {
return new HashSet2<>(nullToEmpty(iterable));
}
/** @return a new {@link ArrayList2} from given array (a null array is considered like it's an empty one). */
@SafeVarargs
public static <E> ArrayList2<E> createArrayList(E... array) {
ArrayList2<E> newCollection = new ArrayList2<E>();
if(array != null) {
Collections.addAll(newCollection, array);
}
return newCollection;
}
/** @return a new {@link LinkedList} from given array (a null array is considered like it's an empty one). */
@SafeVarargs
public static <E> LinkedList<E> createLinkedList(E... array) {
LinkedList<E> newCollection = new LinkedList<E>();
if(array != null) {
Collections.addAll(newCollection, array);
}
return newCollection;
}
/** @return a new {@link HashSet} from given array (a null array is considered like it's an empty one). */
@SafeVarargs
public static <E> HashSet2<E> createHashSet(E... array) {
HashSet2<E> newCollection = new HashSet2<E>();
if(array != null) {
Collections.addAll(newCollection, array);
}
return newCollection;
}
/** @return given iterable if it's non-null, an empty immutable {@link Iterable} otherwise. */
public static <E> Iterable<E> nullToEmpty(Iterable<E> iterable) {
return iterable == null ? Collections.EMPTY_LIST : iterable;
}
/** @return given coll if it's non-null, an empty immutable {@link Collection} otherwise. */
public static <E> Collection<E> nullToEmpty(Collection<E> coll) {
return coll == null ? Collections.EMPTY_LIST : coll;
}
/** @return given list if it's non-null, an empty immutable {@link List} otherwise. */
public static <E> List<E> nullToEmpty(List<E> list) {
return list == null ? Collections.EMPTY_LIST : list;
}
/* ----------------- query ----------------- */
public static <T> int indexOfSame(Iterable<? extends T> iterable, T obj) {
return indexOfSame(iterable.iterator(), obj);
}
public static <T> int indexOfSame(Iterator<? extends T> iterator, T obj) {
int ix = 0;
while(iterator.hasNext()) {
T element = iterator.next();
if(element == obj)
return ix;
ix++;
}
return -1;
}
public static <T> int indexOf(Iterable<? extends T> iterable, T obj) {
return indexOf(iterable.iterator(), obj);
}
public static <T> int indexOf(Iterator<? extends T> iterator, T obj) {
int ix = 0;
while(iterator.hasNext()) {
T element = iterator.next();
if(areEqual(element, obj)) {
return ix;
}
ix++;
}
return -1;
}
/**
* @return the index in the iteration order of the first element that matches given predicate, or -1 otherwise.
*/
public static <T> int indexUntil(Iterator<? extends T> iterator, Predicate<T> predicate) {
int ix = 0;
while(iterator.hasNext()) {
T element = iterator.next();
if(predicate.test(element)) {
return ix;
}
ix++;
}
return -1;
}
// BM: is there no function in the JVM standard lib that does this??
/**
* @return the first element to match given predicate, null otherwise.
*/
public static <T> T findElement(Iterator<? extends T> iterator, Predicate<? super T> predicate) {
while(iterator.hasNext()) {
T element = iterator.next();
if(predicate.test(element)) {
return element;
}
}
return null;
}
/** @return whether given coll contains given obj
* (obj must be the same as the one contained, not just equal).
*/
public static boolean containsSame(Iterable<?> coll, Object obj) {
for(Iterator<?> iterator = coll.iterator(); iterator.hasNext();) {
Object element = iterator.next();
if(element == obj)
return true;
}
return false;
}
/** @return whether given coll contains all elements of other, using a naive/basic algorithm. */
public static boolean containsAll(Collection<?> coll, Collection<?> other) {
for(Object otherElement : other) {
if(!coll.contains(otherElement))
return false;
}
return true;
}
/**
* @return the first element of given coll, if collection is non-null and non-empty. Otherwise null.
*/
public static <T> T getSingleElementOrNull(Iterable<T> coll) {
if(coll == null)
return null;
Iterator<T> iterator = coll.iterator();
T firstElement = iterator.next();
return iterator.hasNext() ? null : firstElement;
}
/* ----------------- modifications ----------------- */
/** Creates a List copy of orig, with all elements except elements equal to excludedElem. */
public static <E> ArrayList2<E> copyExcept(E[] orig, E excludedElem) {
ArrayList2<E> rejectedElements = new ArrayList2<E>(orig.length);
for (int i= 0; i < orig.length; i++) {
if (!orig[i].equals(excludedElem)) {
rejectedElements.add(orig[i]);
}
}
return rejectedElements;
}
public static <ELEM, COLL extends Collection<? super ELEM>> COLL addAll2(COLL dest, Iterable<? extends ELEM> source) {
return addAllFromIterator(dest, source.iterator());
}
public static <ELEM, COLL extends Collection<? super ELEM>> COLL addAllFromIterator(COLL dest,
Iterator<? extends ELEM> source) {
while(source.hasNext()) {
dest.add(source.next());
}
return dest;
}
public static <ELEM, COLL extends Collection<? super ELEM>, SourceELEM> COLL addAll(COLL dest,
Iterable<? extends SourceELEM> source, Function<SourceELEM, ELEM> mapper) {
for(SourceELEM elem : source) {
dest.add(mapper.apply(elem));
}
return dest;
}
/** Remove from given list all elements that match given predicate. */
public static <E, L extends Iterable<E>> L filter(L list, Predicate<E> predicate) {
for (Iterator<? extends E> iter = list.iterator(); iter.hasNext(); ) {
E obj = iter.next();
if(predicate.test(obj)) {
iter.remove();
}
}
return list;
}
public static <ELEM, INTO extends Collection<ELEM>> INTO filter2(
INTO into, Iterable<? extends ELEM> from, Predicate<ELEM> predicate
) {
for (ELEM elem : from) {
if(predicate.test(elem)) {
into.add(elem);
}
}
return into;
}
public static <ELEM, INTO extends Collection<ELEM>, EXC extends Exception> INTO filter2x(
INTO into, Iterable<? extends ELEM> from, FunctionX<ELEM, Boolean, EXC> predicate
) throws EXC {
for (ELEM elem : from) {
if(predicate.apply(elem) == Boolean.TRUE) {
into.add(elem);
}
}
return into;
}
/** Removes from given list the first element that matches given predicate.
* @return true if an element was removed, false otherwise. */
public static <E> boolean removeElement(List<? extends E> list, Predicate<E> predicate) {
for (Iterator<? extends E> iter = list.iterator(); iter.hasNext(); ) {
E obj = iter.next();
if(predicate.test(obj)) {
iter.remove();
return true;
}
}
return false;
}
/** Sorts given list and returns it. */
public static <E extends Comparable<? super E>, L extends List<E>> L sort(L list) {
Collections.sort(list);
return list;
}
@SuppressWarnings("unused")
private static void sort__genericsTestCompile() {
List<? extends Integer> list1 = null;
ArrayList<? extends Integer> list2 = null;
list1 = sort(list1);
list2 = sort(list2);
}
/* ----------------- functional API ----------------- */
// We might want to modify this to use Java 8 Streams
public static <E, R> ArrayList2<R> map(
Iterable<E> coll, Function<? super E, ? extends R> evalFunction) {
return mapTo(coll, evalFunction, new ArrayList2<R>());
}
public static <E, R, DEST extends Collection<R>> DEST mapTo(
Iterable<E> coll, Function<? super E, ? extends R> evalFunction, DEST destCollection) {
for(E collElement : coll) {
R mappeElem = evalFunction.apply(collElement);
destCollection.add(mappeElem);
}
return destCollection;
}
public static <E, R, EXC extends Exception> ArrayList2<R> mapx(
Iterable<E> coll, FunctionX<? super E, ? extends R, EXC> evalFunction) throws EXC {
return mapxTo(coll, evalFunction, new ArrayList2<R>());
}
public static <E, R, EXC extends Exception, DEST extends Collection<R>> DEST mapxTo(
Iterable<E> coll, FunctionX<? super E, ? extends R, EXC> evalFunction, DEST destCollection) throws EXC {
for(E collElement : coll) {
R mappeElem = evalFunction.apply(collElement);
destCollection.add(mappeElem);
}
return destCollection;
}
public static boolean listEquals(Indexable<?> coll1, Indexable<?> coll2) {
return Indexable.indexableEquals(coll1, coll2);
}
public static boolean listEquals(List<?> coll1, List<?> coll2) {
if(coll1.size() != coll2.size()) {
return false;
}
return iterationEquals(coll1.iterator(), coll2.iterator());
}
public static boolean iterationEquals(Iterator<?> iter1, Iterator<?> iter2) {
while(iter1.hasNext() && iter2.hasNext()) {
Object o1 = iter1.next();
Object o2 = iter2.next();
if(!areEqual(o1, o2)) {
return false;
}
}
return !(iter1.hasNext() || iter2.hasNext());
}
}