blob: 1864caebd357c269c94f180897948161cde9d5e1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2015 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.utility.internal.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.RandomAccess;
import org.eclipse.jpt.common.utility.internal.Range;
import org.eclipse.jpt.common.utility.internal.iterable.IterableTools;
import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools;
import org.eclipse.jpt.common.utility.iterable.ListIterable;
import org.eclipse.jpt.common.utility.predicate.Predicate;
import org.eclipse.jpt.common.utility.transformer.Transformer;
/**
* {@link List} utility methods.
*/
public final class ListTools {
// ********** add all **********
/**
* Add all the elements returned by the specified iterable
* to the specified list at the specified index.
* Return whether the list changed as a result.
*/
public static <E> boolean addAll(List<? super E> list, int index, Iterable<? extends E> iterable) {
return addAll(list, index, iterable.iterator());
}
/**
* Add all the elements returned by the specified iterable
* to the specified list at the specified index.
* Return whether the list changed as a result.
* The specified iterable size is a performance hint.
*/
public static <E> boolean addAll(List<? super E> list, int index, Iterable<? extends E> iterable, int iterableSize) {
return addAll(list, index, iterable.iterator(), iterableSize);
}
/**
* Add all the elements returned by the specified iterator
* to the specified list at the specified index.
* Return whether the list changed as a result.
*/
public static <E> boolean addAll(List<? super E> list, int index, Iterator<? extends E> iterator) {
return iterator.hasNext() && addAll_(list, index, iterator);
}
/**
* assume the iterator is not empty
*/
private static <E> boolean addAll_(List<? super E> list, int index, Iterator<? extends E> iterator) {
return (index == list.size()) ? CollectionTools.addAll_(list, iterator) : list.addAll(index, arrayList(iterator));
}
/**
* Add all the elements returned by the specified iterator
* to the specified list at the specified index.
* Return whether the list changed as a result.
* The specified iterator size is a performance hint.
*/
public static <E> boolean addAll(List<? super E> list, int index, Iterator<? extends E> iterator, int iteratorSize) {
return iterator.hasNext() && list.addAll(index, arrayList(iterator, iteratorSize));
}
/**
* Add all the elements in the specified array
* to the specified list at the specified index.
* Return whether the list changed as a result.
*/
public static <E> boolean addAll(List<? super E> list, int index, E... array) {
return (array.length != 0) && list.addAll(index, Arrays.asList(array));
}
// ********** diff **********
/**
* Return the range of elements in the specified
* arrays that are different.
* Return <code>null</code> if the arrays are identical.
* Use the elements' {@link Object#equals(Object)} method to compare the
* elements.
* @see #indexOfDifference(List, List)
* @see #lastIndexOfDifference(List, List)
*/
public static Range differenceRange(List<?> list1, List<?> list2) {
int end = lastIndexOfDifference(list1, list2);
return (end == -1) ? null : new Range(indexOfDifference(list1, list2), end);
}
/**
* Return the index of the first elements in the specified
* lists that are different. If the lists are identical, return
* the size of the two lists (i.e. one past the last index).
* If the lists are different sizes and all the elements in
* the shorter list match their corresponding elements in
* the longer list, return the size of the shorter list
* (i.e. one past the last index of the shorter list).
* Use the elements' {@link Object#equals(Object)} method to compare the
* elements.
*/
public static int indexOfDifference(List<?> list1, List<?> list2) {
int end = Math.min(list1.size(), list2.size());
for (int i = 0; i < end; i++) {
Object o = list1.get(i);
if (o == null) {
if (list2.get(i) != null) {
return i;
}
} else {
if ( ! o.equals(list2.get(i))) {
return i;
}
}
}
return end;
}
/**
* Return the index of the first elements in the specified
* lists that are different, beginning at the end.
* If the lists are identical, return -1.
* If the lists are different sizes, return the index of the
* last element in the longer list.
* Use the elements' {@link Object#equals(Object)} method to compare the
* elements.
*/
public static int lastIndexOfDifference(List<?> list1, List<?> list2) {
int len1 = list1.size();
int len2 = list2.size();
if (len1 != len2) {
return Math.max(len1, len2) - 1;
}
for (int i = len1 - 1; i > -1; i--) {
Object o = list1.get(i);
if (o == null) {
if (list2.get(i) != null) {
return i;
}
} else {
if ( ! o.equals(list2.get(i))) {
return i;
}
}
}
return -1;
}
// ********** filter **********
/**
* Return a new list with the filtered
* elements of the specified list.
*/
public static <E> ArrayList<E> filter(Collection<? extends E> list, Predicate<? super E> predicate) {
ArrayList<E> result = new ArrayList<E>(list.size());
for (E each : list) {
if (predicate.evaluate(each)) {
result.add(each);
}
}
return result;
}
// ********** identity diff **********
/**
* Return the range of elements in the specified
* arrays that are different.
* Return <code>null</code> if the arrays are identical.
* Use object identity to compare the elements.
* @see #indexOfIdentityDifference(List, List)
* @see #lastIndexOfIdentityDifference(List, List)
*/
public static Range identityDifferenceRange(List<?> list1, List<?> list2) {
int end = lastIndexOfIdentityDifference(list1, list2);
return (end == -1) ? null : new Range(indexOfIdentityDifference(list1, list2), end);
}
/**
* Return the index of the first elements in the specified
* lists that are different. If the lists are identical, return
* the size of the two lists (i.e. one past the last index).
* If the lists are different sizes and all the elements in
* the shorter list match their corresponding elements in
* the longer list, return the size of the shorter list
* (i.e. one past the last index of the shorter list).
* Use object identity to compare the elements.
*/
public static int indexOfIdentityDifference(List<?> list1, List<?> list2) {
int end = Math.min(list1.size(), list2.size());
for (int i = 0; i < end; i++) {
if (list1.get(i) != list2.get(i)) {
return i;
}
}
return end;
}
/**
* Return the index of the first elements in the specified
* lists that are different, beginning at the end.
* If the lists are identical, return -1.
* If the lists are different sizes, return the index of the
* last element in the longer list.
* Use object identity to compare the elements.
*/
public static int lastIndexOfIdentityDifference(List<?> list1, List<?> list2) {
int len1 = list1.size();
int len2 = list2.size();
if (len1 != len2) {
return Math.max(len1, len2) - 1;
}
for (int i = len1 - 1; i > -1; i--) {
if (list1.get(i) != list2.get(i)) {
return i;
}
}
return -1;
}
// ********** index of **********
/**
* Return the index of the first occurrence of the
* specified element in the specified list, starting at the specified index;
* or return -1 if there is no such element.
*/
public static <E> int indexOf(List<E> list, Object value, int startIndex) {
int size = list.size();
return (size == 0) ? -1 : (startIndex >= size) ? -1 : indexOf(list, value, (startIndex < 0) ? 0 : startIndex, size);
}
/**
* assume 0 <= start index < list size
*/
private static <E> int indexOf(List<E> list, Object value, int startIndex, int listSize) {
if (value == null) {
for (int i = startIndex; i < listSize; i++) {
if (list.get(i) == null) {
return i;
}
}
} else {
for (int i = startIndex; i < listSize; i++) {
if (value.equals(list.get(i))) {
return i;
}
}
}
return -1;
}
/**
* Return the index of the first occurrence of the
* specified element in the specified list, starting at the specified index;
* or return -1 if there is no such element.
*/
public static <E> int identityIndexOf(List<E> list, Object value, int startIndex) {
int size = list.size();
return (size == 0) ? -1 : (startIndex >= size) ? -1 : identityIndexOf(list, value, (startIndex < 0) ? 0 : startIndex, size);
}
/**
* assume 0 <= start index < list size
*/
private static <E> int identityIndexOf(List<E> list, Object value, int startIndex, int listSize) {
for (int i = startIndex; i < listSize; i++) {
if (list.get(i) == value) {
return i;
}
}
return -1;
}
// ********** insertion index **********
/**
* Return an index of where the specified comparable object
* can be inserted into the specified sorted list and still keep
* the list sorted. If the specified sorted list is an instance of
* {@link RandomAccess} return the <em>maximum</em> insertion index;
* otherwise return the <em>minimum</em> insertion index.
*/
public static <E extends Comparable<? super E>> int insertionIndexOf(List<E> sortedList, Comparable<E> value) {
if (sortedList instanceof RandomAccess) {
for (int i = sortedList.size(); i-- > 0; ) {
if (value.compareTo(sortedList.get(i)) >= 0) {
return i + 1;
}
}
return 0;
}
int i = 0;
for (E element : sortedList) {
if (value.compareTo(element) <= 0) {
return i;
}
i++;
}
return i;
}
/**
* Return an index of where the specified comparable object
* can be inserted into the specified sorted list and still keep
* the list sorted. If the specified sorted list is an instance of
* {@link RandomAccess} return the <em>maximum</em> insertion index;
* otherwise return the <em>minimum</em> insertion index.
*/
public static <E> int insertionIndexOf(List<E> sortedList, E value, Comparator<? super E> comparator) {
if (sortedList instanceof RandomAccess) {
for (int i = sortedList.size(); i-- > 0; ) {
if (comparator.compare(value, sortedList.get(i)) >= 0) {
return i + 1;
}
}
return 0;
}
int i = 0;
for (E element : sortedList) {
if (comparator.compare(value, element) <= 0) {
return i;
}
i++;
}
return i;
}
// ********** last index of **********
/**
* Return the index of the last occurrence of the
* specified element in the specified array, starting at the specified index;
* or return -1 if there is no such element.
*/
public static <E> int lastIndexOf(List<E> list, Object value, int startIndex) {
if (startIndex < 0) {
return -1;
}
int size = list.size();
return (size == 0) ? -1 : lastIndexOf_(list, value, (startIndex >= size) ? size - 1 : startIndex);
}
/**
* assume 0 <= start index < list size
*/
private static <E> int lastIndexOf_(List<E> list, Object value, int startIndex) {
if (value == null) {
for (int i = startIndex; i >= 0; i--) {
if (list.get(i) == null) {
return i;
}
}
} else {
for (int i = startIndex; i >= 0; i--) {
if (value.equals(list.get(i))) {
return i;
}
}
}
return -1;
}
/**
* Return the index of the last occurrence of the
* specified element in the specified array, starting at the specified index;
* or return -1 if there is no such element.
*/
public static <E> int lastIdentityIndexOf(List<E> list, Object value, int startIndex) {
if (startIndex < 0) {
return -1;
}
int size = list.size();
return (size == 0) ? -1 : lastIdentityIndexOf_(list, value, (startIndex >= size) ? size - 1 : startIndex);
}
/**
* assume 0 <= start index < list size
*/
private static <E> int lastIdentityIndexOf_(List<E> list, Object value, int startIndex) {
for (int i = startIndex; i >= 0; i--) {
if (list.get(i) == value) {
return i;
}
}
return -1;
}
// ********** move **********
/**
* Move the specified element from its current position to the specified
* index. Return the altered list.
*/
public static <E, L extends List<E>> L move(L list, int index, E element) {
return move(list, index, list.indexOf(element));
}
/**
* Move an element from the specified source index to the specified target
* index. Return the altered list.
*/
public static <E, L extends List<E>> L move(L list, int targetIndex, int sourceIndex) {
return (targetIndex == sourceIndex) ? list : move_(list, targetIndex, sourceIndex);
}
/**
* assume targetIndex != sourceIndex
*/
private static <E, L extends List<E>> L move_(L list, int targetIndex, int sourceIndex) {
if (list instanceof RandomAccess) {
// move elements, leaving the list in place
E temp = list.get(sourceIndex);
if (targetIndex < sourceIndex) {
for (int i = sourceIndex; i-- > targetIndex; ) {
list.set(i + 1, list.get(i));
}
} else {
for (int i = sourceIndex; i < targetIndex; i++) {
list.set(i, list.get(i + 1));
}
}
list.set(targetIndex, temp);
} else {
// remove the element and re-add it at the target index
list.add(targetIndex, list.remove(sourceIndex));
}
return list;
}
/**
* Move elements from the specified source index to the specified target
* index. Return the altered list.
*/
public static <E, L extends List<E>> L move(L list, int targetIndex, int sourceIndex, int length) {
if ((targetIndex == sourceIndex) || (length == 0)) {
return list;
}
if (length == 1) {
return move_(list, targetIndex, sourceIndex);
}
if (list instanceof RandomAccess) {
// move elements, leaving the list in place
ArrayList<E> temp = new ArrayList<E>(list.subList(sourceIndex, sourceIndex + length));
if (targetIndex < sourceIndex) {
for (int i = sourceIndex; i-- > targetIndex; ) {
list.set(i + length, list.get(i));
}
} else {
for (int i = sourceIndex; i < targetIndex; i++) {
list.set(i, list.get(i + length));
}
}
for (int i = 0; i < length; i++) {
list.set(targetIndex + i, temp.get(i));
}
} else {
// remove the elements and re-add them at the target index
list.addAll(targetIndex, removeElementsAtIndex(list, sourceIndex, length));
}
return list;
}
// ********** remove **********
/**
* Remove the elements at the specified index.
* Return the removed elements.
*/
public static <E> ArrayList<E> removeElementsAtIndex(List<? extends E> list, int index, int length) {
List<? extends E> subList = list.subList(index, index + length);
ArrayList<E> removed = new ArrayList<E>(subList);
subList.clear();
return removed;
}
/**
* Remove any duplicate elements from the specified list,
* while maintaining the order.
* Return whether the list changed as a result.
*/
public static <E> boolean removeDuplicateElements(List<E> list) {
int listSize = list.size();
if ((listSize == 0) || (listSize == 1)) {
return false;
}
LinkedHashSet<E> temp = new LinkedHashSet<E>(listSize); // take advantage of hashed look-up
boolean modified = false;
for (E item : list) {
if ( ! temp.add(item)) {
modified = true; // duplicate item
}
}
if (modified) {
int i = 0;
for (E e : temp) {
list.set(i, e);
i++;
}
int tempSize = temp.size();
for (i = list.size(); i-- > tempSize; ) {
list.remove(i); // pull off the end
}
}
return modified;
}
// ********** rotate **********
/**
* Return the list after it has been "rotated" by one position.
*/
public static <E> List<E> rotate(List<E> list) {
return rotate(list, 1);
}
// ********** transform **********
/**
* Return a new list with transformations of the
* elements in the specified list.
*/
public static <I, O> ArrayList<O> transform(Collection<I> list, Transformer<? super I, ? extends O> transformer) {
ArrayList<O> result = new ArrayList<O>(list.size());
for (I each : list) {
result.add(transformer.transform(each));
}
return result;
}
// ********** java.util.Collections enhancements **********
/**
* Return the destination list after the source list has been copied into it.
* @see Collections#copy(List, List)
*/
public static <E, L extends List<E>> L copy(L dest, List<? extends E> src) {
Collections.copy(dest, src);
return dest;
}
/**
* Return the list after it has been "filled".
* @see Collections#fill(List, Object)
*/
public static <E, L extends List<E>> L fill(L list, E value) {
Collections.fill(list, value);
return list;
}
/**
* Return the list after it has been "reversed".
* @see Collections#reverse(List)
*/
public static <E, L extends List<E>> L reverse(L list) {
Collections.reverse(list);
return list;
}
/**
* Return the list after it has been "rotated".
* @see Collections#rotate(List, int)
*/
public static <E, L extends List<E>> L rotate(L list, int distance) {
Collections.rotate(list, distance);
return list;
}
/**
* Return the list after it has been "shuffled".
* @see Collections#shuffle(List)
*/
public static <E, L extends List<E>> L shuffle(L list) {
Collections.shuffle(list);
return list;
}
/**
* Return the list after it has been "shuffled".
* @see Collections#shuffle(List, Random)
*/
public static <E, L extends List<E>> L shuffle(L list, Random random) {
Collections.shuffle(list, random);
return list;
}
/**
* Return the list after it has been "sorted".
* NB: The list is sorted in place as a side-effect.
* @see Collections#sort(List)
*/
public static <E extends Comparable<? super E>, L extends List<E>> L sort(L list) {
Collections.sort(list);
return list;
}
/**
* Return the list after it has been "sorted".
* NB: The list is sorted in place as a side-effect.
* @see Collections#sort(List, Comparator)
*/
public static <E, L extends List<E>> L sort(L list, Comparator<? super E> comparator) {
Collections.sort(list, comparator);
return list;
}
/**
* Return the list after the specified elements have been "swapped".
* @see Collections#swap(List, int, int)
*/
public static <E, L extends List<E>> L swap(L list, int i, int j) {
Collections.swap(list, i, j);
return list;
}
// ********** factory methods **********
/**
* Return an array list corresponding to the specified iterable.
*/
public static <E> ArrayList<E> arrayList(Iterable<? extends E> iterable) {
return arrayList(iterable.iterator());
}
/**
* Return an array list corresponding to the specified iterable.
* The specified iterable size is a performance hint.
*/
public static <E> ArrayList<E> arrayList(Iterable<? extends E> iterable, int iterableSize) {
return arrayList(iterable.iterator(), iterableSize);
}
/**
* Return an array list corresponding to the specified iterator.
*/
public static <E> ArrayList<E> arrayList(Iterator<? extends E> iterator) {
return arrayList(iterator, new ArrayList<E>());
}
/**
* Return an array list corresponding to the specified iterator.
* The specified iterator size is a performance hint.
*/
public static <E> ArrayList<E> arrayList(Iterator<? extends E> iterator, int iteratorSize) {
return arrayList(iterator, new ArrayList<E>(iteratorSize));
}
private static <E> ArrayList<E> arrayList(Iterator<? extends E> iterator, ArrayList<E> list) {
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
/**
* Return an array list corresponding to the specified array.
* Unlike {@link Arrays#asList(Object[])}, the list
* is modifiable and is not backed by the array.
*/
public static <E> ArrayList<E> arrayList(E... array) {
ArrayList<E> list = new ArrayList<E>(array.length);
for (E e : array) {
list.add(e);
}
return list;
}
// ********** transformers **********
/**
* Return a transformer that transforms a {@link List} into a
* {@link ListIterator}.
*/
@SuppressWarnings("unchecked")
public static <E> Transformer<List<E>, ListIterator<E>> listIteratorTransformer() {
return LIST_ITERATOR_TRANSFORMER;
}
/**
* A transformer that transforms a {@link List} into a
* {@link ListIterator}.
*/
@SuppressWarnings("rawtypes")
public static final Transformer LIST_ITERATOR_TRANSFORMER = new ListIteratorTransformer();
/**
* A transformer that transforms a {@link List} into a
* {@link ListIterator}.
*/
public static class ListIteratorTransformer<E>
implements Transformer<List<E>, ListIterator<E>>
{
public ListIterator<E> transform(List<E> list) {
return list.listIterator();
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
/**
* Return a transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterator}.
*/
@SuppressWarnings("unchecked")
public static <E> Transformer<List<? extends E>, ListIterator<E>> readOnlyListIteratorTransformer() {
return READ_ONLY_LIST_ITERATOR_TRANSFORMER;
}
/**
* A transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterator}.
*/
@SuppressWarnings("rawtypes")
public static final Transformer READ_ONLY_LIST_ITERATOR_TRANSFORMER = new ReadOnlyListIteratorTransformer();
/**
* A transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterator}.
*/
public static class ReadOnlyListIteratorTransformer<E>
implements Transformer<List<? extends E>, ListIterator<E>>
{
public ListIterator<E> transform(List<? extends E> list) {
return IteratorTools.<E>readOnly(list.listIterator());
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
/**
* Return a transformer that transforms a {@link List} into a
* {@link ListIterable}.
*/
@SuppressWarnings("unchecked")
public static <E> Transformer<List<E>, ListIterable<E>> listIterableTransformer() {
return LIST_ITERABLE_TRANSFORMER;
}
/**
* A transformer that transforms a {@link List} into a
* {@link ListIterable}.
*/
@SuppressWarnings("rawtypes")
public static final Transformer LIST_ITERABLE_TRANSFORMER = new ListIterableTransformer();
/**
* A transformer that transforms a {@link List} into a
* {@link ListIterable}.
*/
public static class ListIterableTransformer<E>
implements Transformer<List<E>, ListIterable<E>>
{
public ListIterable<E> transform(List<E> list) {
return IterableTools.listIterable(list);
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
/**
* Return a transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterable}.
*/
@SuppressWarnings("unchecked")
public static <E> Transformer<List<? extends E>, ListIterable<E>> readOnlyListIterableTransformer() {
return READ_ONLY_LIST_ITERABLE_TRANSFORMER;
}
/**
* A transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterable}.
*/
@SuppressWarnings("rawtypes")
public static final Transformer READ_ONLY_LIST_ITERABLE_TRANSFORMER = new ReadOnlyListIterableTransformer();
/**
* A transformer that transforms a {@link List} into a
* <em>read-only</em> {@link ListIterable}.
*/
public static class ReadOnlyListIterableTransformer<E>
implements Transformer<List<? extends E>, ListIterable<E>>
{
public ListIterable<E> transform(List<? extends E> list) {
return IterableTools.<E>readOnly(IterableTools.listIterable(list));
}
@Override
public String toString() {
return this.getClass().getSimpleName();
}
}
// ********** constructor **********
/**
* Suppress default constructor, ensuring non-instantiability.
*/
private ListTools() {
super();
throw new UnsupportedOperationException();
}
}