package edu.stanford.nlp.util;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.Predicate;
/**
* <p>Static class with some useful {@link java.util.function.Predicate} implementations and utility methods for working
* with {@code Predicates}.</p>
* <p>
* <p></p>Contains filters that always accept or reject, filters that accept or reject an Object if it's in a given
* Collection, as well as several composite filters. Contains methods for creating a new Filter that is the AND/OR of
* two Filters, or the NOT of a Filter. Finally, you can filter an Object[] through a Filter to return a new
* <code>Object[]</code> with only the accepted values, or {@link #retainAll(java.util.Collection,
* java.util.function.Predicate)} elements in a Collection that pass a filter.</p>
*
* @author Christopher Manning
* @version 1.0
*/
public class Filters {
/**
* Nothing to instantiate
*/
private Filters() {
}
/**
* The acceptFilter accepts everything.
*/
public static <T> Predicate<T> acceptFilter() {
return new CategoricalFilter<>(true);
}
/**
* The rejectFilter accepts nothing.
*/
public static <T> Predicate<T> rejectFilter() {
return new CategoricalFilter<>(false);
}
private static final class CategoricalFilter<T> implements Predicate<T>, Serializable {
private final boolean judgment;
protected CategoricalFilter(boolean judgment) {
this.judgment = judgment;
}
/**
* Checks if the given object passes the filter.
*
* @param obj an object to test
*/
@Override
public boolean test(T obj) {
return judgment;
}
@Override
public String toString() {
return "CategoricalFilter(" + judgment + ')';
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (!(other instanceof CategoricalFilter)) {
return false;
}
return ((CategoricalFilter) other).judgment == this.judgment;
}
// Automatically generated by Eclipse
private static final long serialVersionUID = 7501774666726883656L;
}
/**
* The collectionAcceptFilter accepts a certain collection.
*/
public static <E> Predicate<E> collectionAcceptFilter(E[] objs) {
return new CollectionAcceptFilter<>(Arrays.asList(objs), true);
}
/**
* The collectionAcceptFilter accepts a certain collection.
*/
public static <E> Predicate<E> collectionAcceptFilter(Collection<E> objs) {
return new CollectionAcceptFilter<>(objs, true);
}
/**
* The collectionRejectFilter rejects a certain collection.
*/
public static <E> Predicate<E> collectionRejectFilter(E[] objs) {
return new CollectionAcceptFilter<>(Arrays.asList(objs), false);
}
/**
* The collectionRejectFilter rejects a certain collection.
*/
public static <E> Predicate<E> collectionRejectFilter(Collection<E> objs) {
return new CollectionAcceptFilter<>(objs, false);
}
private static final class CollectionAcceptFilter<E> implements Predicate<E>, Serializable {
private final Collection<E> args;
private final boolean judgment;
protected CollectionAcceptFilter(Collection<E> c, boolean judgment) {
this.args = Generics.newHashSet(c);
this.judgment = judgment;
}
/**
* Checks if the given object passes the filter.
*
* @param obj an object to test
*/
@Override
public boolean test(E obj) {
if (args.contains(obj)) {
return judgment;
}
return !judgment;
}
@Override
public String toString() {
return "(" + judgment + ':' + args + ')';
}
private static final long serialVersionUID = -8870550963937943540L;
} // end class CollectionAcceptFilter
/**
* Filter that accepts only when both filters accept (AND).
*/
public static <E> Predicate<E> andFilter(Predicate<E> f1, Predicate<E> f2) {
return (new CombinedFilter<>(f1, f2, true));
}
/**
* Filter that accepts when either filter accepts (OR).
*/
public static <E> Predicate<E> orFilter(Predicate<E> f1, Predicate<E> f2) {
return (new CombinedFilter<>(f1, f2, false));
}
/**
* Conjunction or disjunction of two filters.
*/
private static class CombinedFilter<E> implements Predicate<E>, Serializable {
private final Predicate<E> f1, f2;
private final boolean conjunction; // and vs. or
public CombinedFilter(Predicate<E> f1, Predicate<E> f2, boolean conjunction) {
this.f1 = f1;
this.f2 = f2;
this.conjunction = conjunction;
}
@Override
public boolean test(E o) {
if (conjunction) {
return (f1.test(o) && f2.test(o));
}
return (f1.test(o) || f2.test(o));
}
// Automatically generated by Eclipse
private static final long serialVersionUID = -2988241258905198687L;
}
/**
* Disjunction of a list of filters.
*/
public static class DisjFilter<T> implements Predicate<T>, Serializable {
private final List<Predicate<T>> filters;
public DisjFilter(List<Predicate<T>> filters) {
this.filters = filters;
}
@SafeVarargs
public DisjFilter(Predicate<T>... filters) {
this.filters = new ArrayList<>();
this.filters.addAll(Arrays.asList(filters));
}
public void addFilter(Predicate<T> filter) {
filters.add(filter);
}
@Override
public boolean test(T obj) {
for (Predicate<T> f:filters) {
if (f.test(obj)) return true;
}
return false;
}
private static final long serialVersionUID = 1L;
}
/**
* Conjunction of a list of filters.
*/
public static class ConjFilter<T> implements Predicate<T>, Serializable {
private final List<Predicate<T>> filters;
public ConjFilter(List<Predicate<T>> filters) {
this.filters = filters;
}
@SafeVarargs
public ConjFilter(Predicate<T>... filters) {
this.filters = new ArrayList<>();
this.filters.addAll(Arrays.asList(filters));
}
public void addFilter(Predicate<T> filter) {
filters.add(filter);
}
@Override
public boolean test(T obj) {
for (Predicate<T> f:filters) {
if (!f.test(obj)) return false;
}
return true;
}
private static final long serialVersionUID = 1L;
}
/**
* Filter that does the opposite of given filter (NOT).
*/
public static <E> Predicate<E> notFilter(Predicate<E> filter) {
return (new NegatedFilter<>(filter));
}
/**
* Filter that's either negated or normal as specified.
*/
public static <E> Predicate<E> switchedFilter(Predicate<E> filter, boolean negated) {
return new NegatedFilter<>(filter, negated);
}
/**
* Negation of a filter.
*/
private static class NegatedFilter<E> implements Predicate<E>, Serializable {
private final Predicate<E> filter;
private final boolean negated;
public NegatedFilter(Predicate<E> filter, boolean negated) {
this.filter = filter;
this.negated = negated;
}
public NegatedFilter(Predicate<E> filter) {
this(filter, true);
}
@Override
public boolean test(E o) {
return (negated ^ filter.test(o)); // xor
}
public String toString() {
return "NOT(" + filter.toString() + ')';
}
// Automatically generated by Eclipse
private static final long serialVersionUID = -1599556783677718177L;
}
/**
* A filter that accepts a random fraction of the input it sees.
*/
public static class RandomFilter<E> implements Predicate<E>, Serializable {
private static final long serialVersionUID = -4885773582960355425L;
private final Random random;
private final double fraction;
public RandomFilter() {
this(0.1, new Random());
}
public RandomFilter(double fraction) {
this(fraction, new Random());
}
public RandomFilter(double fraction, Random random) {
this.fraction = fraction;
this.random = random;
}
@Override
public boolean test(E o) {
return (random.nextDouble() < fraction);
}
}
/**
* Applies the given filter to each of the given elements, and returns the
* array of elements that were accepted. The runtime type of the returned
* array is the same as the passed in array.
*/
@SuppressWarnings("unchecked")
public static <E> E[] filter(E[] elems, Predicate<E> filter) {
List<E> filtered = new ArrayList<>();
for (E elem: elems) {
if (filter.test(elem)) {
filtered.add(elem);
}
}
return (filtered.toArray((E[]) Array.newInstance(elems.getClass().getComponentType(), filtered.size())));
}
/**
* Removes all elements in the given Collection that aren't accepted by the given Filter.
*/
public static <E> void retainAll(Collection<E> elems, Predicate<? super E> filter) {
for (Iterator<E> iter = elems.iterator(); iter.hasNext();) {
E elem = iter.next();
if ( ! filter.test(elem)) {
iter.remove();
}
}
}
}