org.jopendocument.util.CollectionUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jOpenDocument Show documentation
Show all versions of jOpenDocument Show documentation
jOpenDocument is a free library for developers looking to use
Open Document files without OpenOffice.org.
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2013 jOpenDocument, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU
* General Public License Version 3 only ("GPL").
* You may not use this file except in compliance with the License.
* You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
* See the License for the specific language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*
*/
package org.jopendocument.util;
import org.jopendocument.util.cc.IClosure;
import org.jopendocument.util.cc.IPredicate;
import org.jopendocument.util.cc.ITransformer;
import org.jopendocument.util.cc.IdentityHashSet;
import org.jopendocument.util.cc.IdentitySet;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.TransformerUtils;
/**
* Une classe regroupant des méthodes utilitaires pour les collections.
*
* @author ILM Informatique 30 sept. 2004
*/
public class CollectionUtils {
/**
* Concatene une collection. Cette méthode va appliquer un transformation sur chaque élément
* avant d'appeler toString(). join([-1, 3, 0], " ,", doubleTransformer) == "-2, 6, 0"
*
* @param type of items
* @param c la collection a concaténer.
* @param sep le séparateur entre chaque élément.
* @param tf la transformation à appliquer à chaque élément.
* @return la chaine composée de chacun des éléments séparés par sep
.
*/
static public final String join(final Collection c, final String sep, final ITransformer tf) {
final int size = c.size();
if (size == 0)
return "";
final StringBuffer res = new StringBuffer(size * 4);
if (c instanceof RandomAccess && c instanceof List) {
final List list = (List) c;
for (int i = 0; i < size; i++) {
res.append(tf.transformChecked(list.get(i)));
if (i < size - 1)
res.append(sep);
}
} else {
final Iterator iter = c.iterator();
while (iter.hasNext()) {
final E elem = iter.next();
res.append(tf.transformChecked(elem));
if (iter.hasNext())
res.append(sep);
}
}
return res.toString();
}
/**
* Concatene une collection en appelant simplement toString() sur chaque élément.
*
* @param type of collection
* @param c la collection a concaténer.
* @param sep le séparateur entre chaque élément.
* @return la chaine composée de chacun des éléments séparés par sep
.
* @see #join(Collection, String, ITransformer)
*/
static public String join(Collection c, String sep) {
return join(c, sep, org.jopendocument.util.cc.Transformer. nopTransformer());
}
static public > C transform(final Collection c, final ITransformer transf, final C res) {
return transformAndFilter(c, transf, IPredicate.truePredicate(), res);
}
static public > C transformAndFilter(final Collection c, final ITransformer transf, final IPredicate filterAfter, final C res) {
return filterTransformAndFilter(c, IPredicate.truePredicate(), transf, filterAfter, res);
}
static public > C filterAndTransform(final Collection c, final IPredicate filterBefore, final ITransformer transf, final C res) {
return filterTransformAndFilter(c, filterBefore, transf, IPredicate.truePredicate(), res);
}
static public > C filterTransformAndFilter(final Collection c, final IPredicate filterBefore, final ITransformer transf,
final IPredicate filterAfter, final C res) {
iterate(c, filterBefore, new IClosure() {
@Override
public void executeChecked(T input) {
final U item = transf.transformChecked(input);
if (filterAfter.evaluateChecked(item))
res.add(item);
}
});
return res;
}
static public void iterate(final Collection c, final IClosure cl) {
iterate(c, IPredicate.truePredicate(), cl);
}
static public void iterate(final Collection c, final IPredicate filterBefore, final IClosure cl) {
if (c instanceof RandomAccess && c instanceof List) {
final List list = (List) c;
final int size = c.size();
for (int i = 0; i < size; i++) {
final T item = list.get(i);
if (filterBefore.evaluateChecked(item))
cl.executeChecked(item);
}
} else {
final Iterator iter = c.iterator();
while (iter.hasNext()) {
final T item = iter.next();
if (filterBefore.evaluateChecked(item))
cl.executeChecked(item);
}
}
}
private static final Pattern COMMA = Pattern.compile("\\p{Space}*,\\p{Space}*");
static public List split(String s) {
return split(s, COMMA);
}
static public List split(String s, String sep) {
return split(s, Pattern.compile(sep));
}
/**
* Split a string into a list based on a pattern.
*
* @param s the string to split.
* @param pattern the pattern where to cut the string.
* @return the splitted string, empty list if s
is "".
*/
static public List split(String s, Pattern pattern) {
return s.length() == 0 ? Collections. emptyList() : Arrays.asList(pattern.split(s));
}
/**
* Return an index between 0
and l.size()
inclusive. If i
* is negative, it is added to l.size()
(but bounded to 0), ie for a list of 3
* items, -1 is the index of the last item ; -3 and -4 are both the first. If i
is
* greater than l.size()
then l.size()
is returned.
*
*
* a b c a b c
* -3 -2 -1 0 1 2 3
*
*
* @param l the list, eg a list of 3 items.
* @param i the virtual index, eg -1.
* @return the real index, eg 2.
*/
static public int getValidIndex(final List l, final int i) {
return getValidIndex(l, i, false);
}
static public int getValidIndex(final List l, final int i, final boolean strict) {
final int size = l.size();
if (i > size) {
if (strict)
throw new IndexOutOfBoundsException("Too high : " + i + " > " + size);
return size;
} else if (i < -size) {
if (strict)
throw new IndexOutOfBoundsException("Too low : " + i + " < " + -size);
return 0;
} else if (i >= 0) {
return i;
} else {
return size + i;
}
}
/**
* Deletes a slice of a list. Pass indexes to {@link #getValidIndex(List, int)} to allow
* delete(l, 0, -1) to clear l or delete(l, -2, -2) to remove the penultimate item.
*
* @param l the list to delete from.
* @param from the first index to be removed (inclusive).
* @param to the last index to be removed (inclusive).
*/
static public void delete(List l, int from, int to) {
if (!l.isEmpty())
l.subList(getValidIndex(l, from), getValidIndex(l, to) + 1).clear();
}
/**
* Deletes the tail of a list. The resulting list will have a size of from
.
*
* @param l the list to delete from.
* @param from the first index to be removed (inclusive).
*/
static public void delete(List l, int from) {
delete(l, from, -1);
}
public static void filter(Collection collection, IPredicate predicate) {
org.apache.commons.collections.CollectionUtils.filter(collection, predicate);
}
public static boolean exists(Collection collection, IPredicate predicate) {
return org.apache.commons.collections.CollectionUtils.exists(collection, predicate);
}
/**
* Permet d'organiser une collection en une hiérarchie à l'aide de Map. Avec
* Col = [
* Obs1(bat=BAT A, local=A1, num=1),
* Obs2(bat=BAT B, local=B1, num=2),
* Obs3(bat=BAT B, local=B2, num=3),
* Obs4(bat=BAT B, local=B2, num=4)
* ]
*
* ainsi que deux extracteurs pour trouver le batiment et le local, et enfin itemOrdering suivant le numero, on a
* { BAT A => {A1 => {Obs1}}, {BAT B => {B1 => {Obs2}, B2 => {Obs3, Obs4}}}}.
*
* @param col la collection à organiser.
* @param propExtractors les extracteurs de propriétes.
* @param propComp les Comparator pour les propriétés renvoyées par les extracteurs, peut être
* null
si les propriétés sont des Comparable.
* @param itemOrdering comment ordonner les éléments dans la dernière tranche, peut être
* null
si les éléments sont des Comparable.
* @return une hiérarchie de SortedMap et en dernier un SortedSet.
*/
static public final SortedMap organize(Collection col, List propExtractors, List propComp, Comparator itemOrdering) {
if (propExtractors.size() == 0)
throw new IllegalArgumentException("Empty property extractors");
if (propComp == null)
propComp = Collections.nCopies(propExtractors.size(), null);
else if (propExtractors.size() != propComp.size())
throw new IllegalArgumentException("Size mismatch between " + propExtractors + " and " + propComp);
final SortedMap res = new TreeMap(propComp.get(0));
Iterator iter = col.iterator();
while (iter.hasNext()) {
final Object item = iter.next();
Map m = res;
for (int i = 0; i < propExtractors.size() - 1; i++) {
final Transformer extractor = propExtractors.get(i);
final Object property = extractor.transform(item);
Map newM = (Map) m.get(property);
if (newM == null) {
newM = new TreeMap(propComp.get(i + 1));
m.put(property, newM);
}
m = newM;
}
final Object property = propExtractors.get(propExtractors.size() - 1).transform(item);
SortedSet s = (SortedSet) m.get(property);
if (s == null) {
s = new TreeSet(itemOrdering);
m.put(property, s);
}
s.add(item);
}
return res;
}
/**
* Permet d'aplatir une hiérarchie. Exemple :
*
*
* A-
* A1
* A2
* B-
* B1
* B11
* B12
*
*
* devient [A, A1, A2, B, B1, B11, B12]
.
*
* @param hierarchy la hiérarchie à aplatir.
* @param itemTransf la transformation à faire sur les feuilles.
* @return la liste correspondante.
*/
static public final List flatten(Map hierarchy, Transformer itemTransf) {
final List res = new ArrayList();
final Iterator iter = hierarchy.keySet().iterator();
while (iter.hasNext()) {
final Object obj = iter.next();
res.add(obj);
final Object value = hierarchy.get(obj);
if (value instanceof Map)
res.addAll(flatten((Map) value, itemTransf));
else if (value instanceof Collection) {
final Collection items = (Collection) value;
final Iterator itemIter = items.iterator();
while (itemIter.hasNext()) {
final Object item = itemIter.next();
res.add(itemTransf.transform(item));
}
} else
throw new IllegalArgumentException("Illegal value: " + value);
}
return res;
}
/**
* Permet d'aplatir une hiérarchie.
*
* @param hierarchy la hiérarchie à aplatir.
* @return la liste correspondante.
*/
static public final List flatten(Map hierarchy) {
return flatten(hierarchy, TransformerUtils.nopTransformer());
}
/**
* Convertit une map en 2 listes, une pour les clefs, une pour les valeurs.
*
* @param map la Map à convertir.
* @return un tuple de 2 List, en 0 les clefs, en 1 les valeurs.
* @param type of key
* @param type of value
*/
static public Tuple2, List> mapToLists(Map map) {
final List keys = new ArrayList(map.size());
final List vals = new ArrayList(map.size());
for (final Map.Entry e : map.entrySet()) {
keys.add(e.getKey());
vals.add(e.getValue());
}
return Tuple2.create(keys, vals);
}
/**
* Add entries from toAdd
into map
only if the key is not already
* present.
*
* @param type of keys.
* @param type of values.
* @param map the map to fill.
* @param toAdd the entries to add.
* @return map
.
*/
static public Map addIfNotPresent(Map map, Map toAdd) {
for (final Map.Entry e : toAdd.entrySet()) {
if (!map.containsKey(e.getKey()))
map.put(e.getKey(), e.getValue());
}
return map;
}
/**
* Compute the index that have changed (added or removed) between 2 lists. One of the lists MUST
* be a sublist of the other, ie the to go from one to the other we just add or remove items but
* we don't do both.
*
* @param oldList the first list.
* @param newList the second list.
* @return a list of Integer.
* @param type of item
* @throws IllegalStateException if one list is not a sublist of the other.
*/
static public List getIndexesChanged(List oldList, List newList) {
final List longer;
final List shorter;
if (newList.size() > oldList.size()) {
longer = new ArrayList(newList);
shorter = new ArrayList(oldList);
} else {
longer = new ArrayList(oldList);
shorter = new ArrayList(newList);
}
final List res = new ArrayList();
int offset = 0;
while (shorter.size() > 0) {
if (longer.size() < shorter.size())
throw new IllegalStateException(shorter + " is not a sublist of " + longer);
// compare nulls
if (CompareUtils.equals(shorter.get(0), longer.get(0))) {
shorter.remove(0);
longer.remove(0);
} else {
longer.remove(0);
res.add(offset);
}
offset++;
}
for (int i = 0; i < longer.size(); i++) {
res.add(i + offset);
}
return res;
}
/**
* Aggregate a list of ints into a list of intervals. Eg aggregate([-1,0,1,2,5]) returns
* [[-1,2], [5,5]].
*
* @param ints a list of Integer strictly increasing.
* @return a list of int[2].
*/
static public List aggregate(Collection ints) {
final List res = new ArrayList();
int[] currentInterval = null;
for (final Number n : ints) {
final int index = n.intValue();
if (currentInterval == null || index != currentInterval[1] + 1) {
currentInterval = new int[2];
currentInterval[0] = index;
currentInterval[1] = currentInterval[0];
res.add(currentInterval);
} else {
currentInterval[1] = index;
}
}
return res;
}
/**
* Test whether col2 is contained in col1.
*
* @param type of collection
* @param col1 the first collection
* @param col2 the second collection
* @return null
if col1 contains all of col2, else return the extra items that col2
* have.
*/
static public Set contains(final Set col1, final Set col2) {
if (col1.containsAll(col2))
return null;
else {
final Set names = new HashSet(col2);
names.removeAll(col1);
return names;
}
}
static public > boolean containsAny(final C coll1, final C coll2) {
return org.apache.commons.collections.CollectionUtils.containsAny(coll1, coll2);
}
/**
* Convert an array to a list of a different type.
*
* @param type of array
* @param type of list
* @param array the array to convert, eg new Object[]{"a", "b"}.
* @param clazz the class of the list items, eg String.class.
* @return all items of array
into a list, eg ["a", "b"].
* @throws ClassCastException if some item of array
is not a T
.
*/
static public List castToList(U[] array, Class clazz) throws ClassCastException {
final List res = new ArrayList(array.length);
for (final U item : array) {
res.add(clazz.cast(item));
}
return res;
}
/**
* The number of equals item between a and b, starting from the end.
*
* @param type of items.
* @param a the first list, eg [a, b, c].
* @param b the second list, eg [a, null, z, c].
* @return the number of common items, eg 1.
*/
public static int equalsFromEnd(final List a, final List b) {
return equals(a, b, true, null);
}
public static int equalsFromStart(final List a, final List b) {
return equals(a, b, false, null);
}
/**
* The number of equals item between a and b, starting from the choosen end.
*
* @param type of the first list.
* @param type of the second list.
* @param a the first list, eg [a, b, c].
* @param b the second list, eg [a, null, z, c].
* @param fromEnd whether search from the start or the end, true
.
* @param transf how items of a
should be transformed before being compared, can be
* null
.
* @return the number of common items, eg 1.
*/
public final static int equals(final List a, final List b, boolean fromEnd, ITransformer transf) {
final int sizeA = a.size();
final int sizeB = b.size();
final int lastI = Math.min(sizeA, sizeB);
for (int i = 0; i < lastI; i++) {
final A itemA = a.get(fromEnd ? sizeA - 1 - i : i);
final B itemB = b.get(fromEnd ? sizeB - 1 - i : i);
if (!CompareUtils.equals(transf == null ? itemA : transf.transformChecked(itemA), itemB))
return i;
}
return lastI;
}
public static Collection select(final Collection a, final IPredicate pred) {
return select(a, pred, new ArrayList());
}
public static > C select(final Collection a, final IPredicate pred, final C b) {
for (final T item : a)
if (pred.evaluateChecked(item))
b.add(item);
return b;
}
// avoid name collision causing eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=319603
@SuppressWarnings("unchecked")
public static Collection intersection(final Collection a, final Collection b) {
return org.apache.commons.collections.CollectionUtils.intersection(a, b);
}
/**
* Compute the intersection of a and b. null
s are ignored : x ∩ null = x.
*
* @param type of collection.
* @param a the first set, can be null
.
* @param b the second set, can be null
.
* @return the intersection.
*/
@SuppressWarnings("unchecked")
public static Set inter(final Set a, final Set b) {
return (Set) interSubtype(a, b);
}
public static Set interSubtype(final Set a, final Set b) {
if (a == b)
return a;
else if (a == null)
return b;
else if (b == null)
return a;
else if (a.size() > b.size()) {
return interSubtype(b, a);
}
final Set res = new HashSet();
for (final T item : a) {
if (b.contains(item))
res.add(item);
}
return res;
}
public static Set inter(final Set... sets) {
return inter(Arrays.asList(sets));
}
public static Set inter(final List> sets) {
final List> mutable = new ArrayList>(sets.size());
for (final Set s : sets) {
// ignore nulls
if (s != null)
mutable.add(s);
}
if (mutable.isEmpty())
return null;
else if (mutable.size() == 1)
return mutable.get(0);
final int indexMin = indexOfMinSize(mutable);
if (indexMin != 0) {
mutable.add(0, mutable.remove(indexMin));
return inter(mutable);
}
if (mutable.get(0).isEmpty())
return Collections.emptySet();
// replace the first 2 by their intersection
// (inter will swap as appropriate if java doesn't evalute args in source order)
mutable.add(0, inter(mutable.remove(0), mutable.remove(0)));
return inter(mutable);
}
private static final int indexOfMinSize(final List> sets) {
if (sets.isEmpty())
throw new IllegalArgumentException("empty sets");
int res = 0;
for (int i = 1; i < sets.size(); i++) {
if (sets.get(i).size() < sets.get(res).size())
res = i;
}
return res;
}
/**
* Returns a {@link Set} containing the union of the given {@link Set}s.
*
* @param type of items.
* @param a the first set, must not be null
* @param b the second set, must not be null
* @return the union of the two.
*/
public static Set union(final Set a, final Set b) {
final Set res = new HashSet(a);
if (a != b)
res.addAll(b);
return res;
}
@SuppressWarnings("unchecked")
public static Collection subtract(final Collection a, final Collection b) {
return org.apache.commons.collections.CollectionUtils.subtract(a, b);
}
@SuppressWarnings("unchecked")
public static Collection substract(final Collection a, final Collection b) {
return org.apache.commons.collections.CollectionUtils.subtract(a, b);
}
/**
* Return the first item of l
if it's the only one, otherwise null
.
*
* @param type of list.
* @param l the list.
* @return the first item of l
or null
.
*/
public static T getSole(List l) {
return l.size() == 1 ? l.get(0) : null;
}
public static T getSole(Collection l) {
return l.size() == 1 ? l.iterator().next() : null;
}
public static T getFirst(Collection l) {
return l.size() > 0 ? l.iterator().next() : null;
}
/**
* Return the first item of l
if it isn't empty, otherwise null
.
*
* @param type of list.
* @param l the list.
* @return the first item of l
or null
.
*/
public static T getFirst(List l) {
return getNoExn(l, 0);
}
/**
* Return the last item of l
if it isn't empty, otherwise null
.
*
* @param type of list.
* @param l the list.
* @return the last item of l
or null
.
*/
public static T getLast(List l) {
return getNoExn(l, l.size() - 1);
}
/**
* Return the item no index
of l
if it exists, otherwise
* null
.
*
* @param type of list.
* @param l the list.
* @param index the wanted index.
* @return the corresponding item of l
or null
.
*/
public static T getNoExn(List l, int index) {
return index >= 0 && index < l.size() ? l.get(index) : null;
}
@SuppressWarnings("rawtypes")
private static final Iterator EMPTY_ITERATOR = new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
@SuppressWarnings("unchecked")
public static Iterator emptyIterator() {
return (Iterator) EMPTY_ITERATOR;
}
public static LinkedList toLinkedList(final Iterator iter) {
return addTo(iter, new LinkedList());
}
public static ArrayList toArrayList(final Iterator iter, final int estimatedSize) {
return addTo(iter, new ArrayList(estimatedSize));
}
public static > C addTo(final Iterator iter, final C c) {
while (iter.hasNext())
c.add(iter.next());
return c;
}
public static ListIterator getListIterator(final List l, final boolean reversed) {
if (!reversed)
return l.listIterator();
return reverseListIterator(l.listIterator(l.size()));
}
public static ListIterator reverseListIterator(final ListIterator listIter) {
if (listIter instanceof ReverseListIter)
return ((ReverseListIter) listIter).listIter;
else
return new ReverseListIter(listIter);
}
private static final class ReverseListIter implements ListIterator {
private final ListIterator listIter;
private ReverseListIter(ListIterator listIter) {
this.listIter = listIter;
}
@Override
public boolean hasNext() {
return this.listIter.hasPrevious();
}
@Override
public T next() {
return this.listIter.previous();
}
@Override
public boolean hasPrevious() {
return this.listIter.hasNext();
}
@Override
public T previous() {
return this.listIter.next();
}
@Override
public int nextIndex() {
return this.listIter.previousIndex();
}
@Override
public int previousIndex() {
return this.listIter.nextIndex();
}
@Override
public void remove() {
this.listIter.remove();
}
@Override
public void set(T e) {
this.listIter.set(e);
}
@Override
public void add(T e) {
throw new UnsupportedOperationException();
}
}
public static Set createSet(T... items) {
return new HashSet(Arrays.asList(items));
}
public static IdentitySet createIdentitySet(T... items) {
return new IdentityHashSet(Arrays.asList(items));
}
/**
* Return an {@link IdentitySet} consisting of items
.
*
* @param items the collection whose elements are to be in the result.
* @return a set, possibly items
if it's already an identity set.
*/
public static Set toIdentitySet(Collection items) {
if (items instanceof IdentitySet)
return (Set) items;
else
return new IdentityHashSet(items);
}
@SuppressWarnings("rawtypes")
private static final IdentitySet EMPTY_SET = new EmptyIdentitySet();
@SuppressWarnings("unchecked")
public static IdentitySet emptyIdentitySet() {
return (IdentitySet) EMPTY_SET;
}
private static final class EmptyIdentitySet extends AbstractSet