Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.semanticweb.elk.util.collections.Operations Maven / Gradle / Ivy
/*
* #%L
* ELK Reasoner
*
* $Id$
* $HeadURL$
* %%
* Copyright (C) 2011 Department of Computer Science, University of Oxford
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.semanticweb.elk.util.collections;
import java.io.IOException;
import java.io.Writer;
import java.util.AbstractCollection;
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.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Some useful static methods for collections
*
* @author "Yevgeny Kazakov"
* @author Peter Skocovsky
*/
public class Operations {
public static final Multimap, ?> EMPTY_MULTIMAP = new Multimap() {
@Override
public boolean contains(Object key, Object value) {
return false;
}
@Override
public boolean add(Object key, Object value) {
throw new UnsupportedOperationException(
"The Empty multimap cannot be modified!");
}
@Override
public Collection get(Object key) {
return Collections.emptySet();
}
@Override
public boolean remove(Object key, Object value) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Set keySet() {
return Collections.emptySet();
}
@Override
public void clear() {
}
@Override
public Collection remove(Object key) {
return Collections.emptySet();
}
};
@SuppressWarnings("unchecked")
public static Multimap emptyMultimap() {
return (Multimap) EMPTY_MULTIMAP;
}
@SafeVarargs
public static Iterable concat(
final Iterable extends T>... inputs) {
return concat(Arrays.asList(inputs));
}
public static Iterator singletonIterator(final T element) {
return new Iterator() {
boolean hasNext = true;
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
hasNext = false;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public static Iterable singleton(final T element) {
return new Iterable() {
@Override
public Iterator iterator() {
return singletonIterator(element);
}
};
}
/**
* Concatenates several {@link Iterable}s into one
*
* @param inputs
* the {@link Iterable} of {@link Iterable}s to be concatenated
* @param
* the type of elements of {@link Iterable}s
* @return {@link Iterable} consisting of all elements found in input
* {@link Iterable}s
*/
public static Iterable concat(
final Iterable extends Iterable extends T>> inputs) {
assert inputs != null;
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
Iterator extends Iterable extends T>> outer = inputs
.iterator();
Iterator extends T> inner;
boolean hasNext = advance();
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if (hasNext) {
T result = inner.next();
hasNext = advance();
return result;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
boolean advance() {
while (true) {
if (inner != null && inner.hasNext())
return true;
if (outer.hasNext())
inner = outer.next().iterator();
else
return false;
}
}
};
}
};
}
/**
* @param
* the type of elements of iterators
* @param first
* the elements that should come first
* @param second
* the elements that should come next
* @return an iterator that iterates that advances the two given iterators
* at the same time and returns the values of the second iterator
*/
public static Iterator synchronize(Iterator> first,
Iterator extends T> second) {
return new Iterator() {
@Override
public boolean hasNext() {
return first.hasNext() && second.hasNext();
}
@Override
public T next() {
first.next();
return second.next();
}
};
}
/**
* Splits the input {@link Iterable} on batches with at most given number of
* elements.
*
* @param elements
* the {@link Iterable} to be split
* @param
* the type of elements in the {@link Iterable}
* @param batchSize
* the maximal number of elements in batches
* @return a {@link Iterable} of batches containing elements from the input
* collection
* @see #concat(Iterable)
*/
public static Iterable> split(
final Iterable extends T> elements, final int batchSize) {
return new Iterable>() {
@Override
public Iterator> iterator() {
return new Iterator>() {
final Iterator extends T> elementsIterator = elements
.iterator();
@Override
public boolean hasNext() {
return elementsIterator.hasNext();
}
@Override
public ArrayList next() {
final ArrayList nextBatch = new ArrayList(
batchSize);
int count = 0;
while (count++ < batchSize
&& elementsIterator.hasNext()) {
nextBatch.add(elementsIterator.next());
}
return nextBatch;
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Deletion is not supported");
}
};
}
};
}
/**
* Splits the input {@link Collection} on batches with at most given number
* of elements.
*
* @param elements
* the {@link Collection} to be split
* @param
* the type of elements in the {@link Collection}
* @param batchSize
* the maximal number of elements in batches
* @return a {@link Collection} of batches containing elements from the
* input collection
*/
public static Collection> split(
final Collection extends T> elements, final int batchSize) {
return new AbstractCollection>() {
@Override
public Iterator> iterator() {
Iterable> iterable = split(
(Iterable extends T>) elements, batchSize);
return iterable.iterator();
}
@Override
public int size() {
// rounding up
return (elements.size() + batchSize - 1) / batchSize;
}
};
}
public static Collection getCollection(final Iterable iterable,
final int size) {
return new AbstractCollection() {
@Override
public Iterator iterator() {
return iterable.iterator();
}
@Override
public int size() {
return size;
}
};
}
/**
* @param
* the type of elements
* @param input
* the input iterator over these elements
* @param condition
* the condition used for filtering
* @return the filtered iterator
*
*/
public static Iterable filter(final Iterable extends T> input,
final Condition super T> condition) {
assert input != null;
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
Iterator extends T> i = input.iterator();
T next;
boolean hasNext = advance();
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if (hasNext) {
T result = next;
hasNext = advance();
return result;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
i.remove();
}
boolean advance() {
while (i.hasNext()) {
next = i.next();
if (condition.holds(next))
return true;
}
return false;
}
};
}
};
}
@SuppressWarnings("unchecked")
public static Iterable filter(final Iterable input,
final Class type) {
return (Iterable) filter(input, new Condition() {
@Override
public boolean holds(S element) {
return type.isInstance(element);
}
});
}
/**
* Returns read-only view of the given set consisting of the elements
* satisfying a given condition, if the number of such elements is known
*
* @param
* the type of elements
* @param input
* the given set to be filtered
* @param condition
* the condition used for filtering the set. Must be consistent
* with equals() for T, that is: a.equals(b) must imply that
* holds(a) == holds(b)
* @param size
* the number of elements in the filtered set
* @return the set consisting of the elements of the input set satisfying
* the given condition
*/
public static Set filter(final Set extends T> input,
final Condition super T> condition, final int size) {
return new Set() {
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
@SuppressWarnings("unchecked")
public boolean contains(Object o) {
if (!input.contains(o))
return false;
T elem = null;
try {
elem = (T) o;
} catch (ClassCastException cce) {
return false;
}
/*
* here's why the condition must be consistent with equals(): we
* check it on the passed element while we really need to check
* it on the element which is in the underlying set (and is
* equal to o according to equals()). However, as long as the
* condition is consistent, the result will be the same.
*/
return condition.holds(elem);
}
@Override
public Iterator iterator() {
Iterable iterable = filter(input, condition);
return iterable.iterator();
}
@Override
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Object o : filter(input, condition)) {
result[i++] = o;
}
return result;
}
@Override
public S[] toArray(S[] a) {
throw new UnsupportedOperationException();
}
@Override
public boolean add(T e) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection> c) {
for (Object o : c) {
if (contains(o))
return false;
}
return true;
}
@Override
public boolean addAll(Collection extends T> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
};
}
/**
* Transformations of input values to output values
*
* @param
* the type of the input of the transformation
* @param
* the type of the output of the transformation
*/
public interface Transformation {
/**
* Transforms the input element
*
* @param element
* the element to be transformed
* @return the result of the transformation
*/
public O transform(I element);
}
// TODO: get rid of Conditions in favour of transformations
/**
* Transforms elements using a given {@link Transformation} the output
* elements consist of the result of the transformation in the same order;
* if the transformation returns {@code null}, it is not included in the
* output
*
* @param
* the type of input of elements
* @param
* the type of output elements
* @param input
* the input elements
* @param transformation
* the transformation for elements
* @return the transformed output elements
*
*/
public static Iterable map(final Iterable input,
final Transformation super I, O> transformation) {
assert input != null;
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
Iterator i = input.iterator();
O next;
boolean hasNext = advance();
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public O next() {
if (hasNext) {
O result = next;
hasNext = advance();
return result;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
i.remove();
}
boolean advance() {
while (i.hasNext()) {
next = transformation.transform(i.next());
if (next != null)
return true;
}
return false;
}
};
}
};
}
public static Iterable mapConcat(final Iterable input,
final Transformation super I, Iterable> transformation) {
assert input != null;
return new Iterable() {
@Override
public Iterator iterator() {
return mapConcat(input.iterator(), transformation);
}
};
}
public static Collection map(final Collection input,
final Transformation super I, O> transformation) {
assert input != null;
return new AbstractCollection() {
@Override
public Iterator iterator() {
return map(input.iterator(), transformation);
}
@Override
public int size() {
// this is an upper bound
return input.size();
}
};
}
public static Iterator map(final Iterator input,
final Transformation super I, O> transformation) {
return new Iterator() {
O next;
boolean hasNext = advance();
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public O next() {
if (hasNext) {
O result = next;
hasNext = advance();
return result;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
input.remove();
}
boolean advance() {
while (input.hasNext()) {
next = transformation.transform(input.next());
if (next != null)
return true;
}
return false;
}
};
}
public static Iterator mapConcat(final Iterator input,
final Transformation super I, Iterable> transformation) {
return new Iterator() {
Iterator nextIter = Collections. emptyList().iterator();
O next;
@Override
public boolean hasNext() {
for (;;) {
if (next != null) {
return true;
}
if (nextIter.hasNext()) {
next = nextIter.next();
return true;
}
if (input.hasNext()) {
nextIter = transformation.transform(input.next())
.iterator();
} else {
return false;
}
}
}
@Override
public O next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
O result = next;
next = null;
return result;
}
@Override
public void remove() {
input.remove();
}
};
}
/**
* Prints key-value entries present in the first {@link Multimap} but not in
* the second {@link Multimap} using the given {@link Writer} and prefixing
* all messages with a given prefix.
*
* @param
* the type of the keys of the {@link Multimap}
* @param
* the type of the values of the {@link Multimap}
* @param first
* the entries that should be printed
* @param second
* the entries that should be skipped
* @param writer
* the writer using which the entries should be printed
* @param prefix
* the string to be appended to every line
* @throws IOException
* if any I/O error occurs
*/
public static void dumpDiff(Multimap first,
Multimap second, Writer writer, String prefix)
throws IOException {
for (K key : first.keySet()) {
Collection firstValues = first.get(key);
Collection secondValues = second.get(key);
dumpDiff(firstValues, secondValues, writer, prefix + key + "->");
}
}
/**
* Prints the elements present in the first {@link Collection} but not in
* the second {@link Collection} using the given {@link Writer} and
* prefixing all messages with a given prefix.
*
* @param
* the type of elements
* @param first
* the elements to be printed
* @param second
* the elements to be skipped
* @param writer
* the writer using which the elements should be printed
* @param prefix
* the string to be appended to every line
* @throws IOException
* if any I/O error occurs
*/
public static void dumpDiff(Collection first, Collection second,
Writer writer, String prefix) throws IOException {
for (T element : first)
if (!second.contains(element))
writer.append(prefix + element + "\n");
}
/**
* A simple object that transforms objects of type I to objects of type O
*
* @author Pavel Klinov
*
* [email protected]
*
* @param
* the type of input objects
* @param
* the type of the output objects
*/
public interface Functor {
public O apply(I element);
}
/**
* An extension of {@link Functor} which can do the reverse transformation
*
* @author Pavel Klinov
*
* [email protected]
* @param
* the type of input objects
* @param
* the type of output objects
*/
public interface FunctorEx extends Functor {
/**
* The reason this method takes Objects rather than instances of O is
* because it's primarily used for an efficient implementation of
* {@link Set#contains(Object)}, which takes an Object
*
* @param element
* the output element to be converted back to input
*
* @return Can return null if the transformation is not possible
*/
public I deapply(Object element);
}
/**
* An {@link Iterator} whose elements are obtained from another
* {@link Iterator} by applying a {@link Functor}
*
* @author Pavel Klinov
*
* [email protected]
*
* @param
* the type of elements of the input {@link Iterator}
* @param
* the type of elements of this {@link Iterator}
*/
private static class MapIterator implements Iterator {
private final Iterator extends I> iter_;
private final Functor functor_;
MapIterator(Iterator extends I> iter, Functor functor) {
iter_ = iter;
functor_ = functor;
}
@Override
public boolean hasNext() {
return iter_.hasNext();
}
@Override
public O next() {
return functor_.apply(iter_.next());
}
@Override
public void remove() {
iter_.remove();
}
}
/**
* A simple second-order map function for {@link Set}s
*
* @param
* the type of elements of the input {@link Set}
* @param
* the type of elements of this {@link Set}
* @param input
* the input {@link Set} to be converted
* @param functor
* the {@link Functor} that applies to the input elements
* @return a set which contains the results of applying the {@link Functor} to the
* elements in the input {@link Set}
*/
public static Set map(final Set extends I> input,
final FunctorEx functor) {
return new AbstractSet() {
@Override
public Iterator iterator() {
return new MapIterator(input.iterator(), functor);
}
@Override
public boolean contains(Object o) {
I element = functor.deapply(o);
return element == null ? false : input.contains(element);
}
@Override
public int size() {
return input.size();
}
};
}
public static String toString(Iterable iterable) {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (T elem : iterable) {
String elemStr = elem.toString();
if (!first && !elemStr.isEmpty()) {
builder.append(' ');
}
if (!elemStr.isEmpty()) {
builder.append(elemStr);
}
first = false;
}
return builder.toString();
}
/**
* @param
* the type of elements
* @param elementComparator
* a {@link Comparator} on these elements
* @return {@link Comparator} that compares {@link Iterable} according to
* lexical order w.r.t. element ordering defined by the provided
* element {@link Comparator}.
*/
public static final Comparator super Iterable> lexicalOrder(
final Comparator super T> elementComparator) {
return new Comparator>() {
@Override
public int compare(final Iterable o1, final Iterable o2) {
final Iterator list1 = o1 == null
? Collections. emptyList().iterator()
: o1.iterator();
final Iterator list2 = o2 == null
? Collections. emptyList().iterator()
: o2.iterator();
while (list1.hasNext() || list2.hasNext()) {
// index did not exceed the size of at least one list.
if (!list1.hasNext()) {
// list1 is shorter and coincides on its elements.
return -1;
}
if (!list2.hasNext()) {
// list2 is shorter and coincides on its elements.
return 1;
}
// index exceeded the size of none of the lists.
final int cmp = elementComparator.compare(list1.next(),
list2.next());
if (cmp != 0) {
return cmp;
}
}
/*
* index exceeded size of both lists and no difference was
* found, so the lists are the same.
*/
return 0;
}
};
}
}