A standard generic Predicate.

Sometimes we used the apache predicate, sometimes we rolled our own.
I'm rolling our own now because I want generic support.

Implementation details are subject to change, but two that we may want
to discuss sooner than later:

- I called the arbitration method "isMatch()", but almost went with
  "evaluate()" and considered "is()".
- I made Predicate an abstract class rather than an interface because I felt
  it was cool to be able to create filtered Iterators or views of collections
  using the Predicate.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@1893 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
ray
2006-08-28 18:36:18 +00:00
parent 308f771857
commit b053107fd8
+300
View File
@@ -0,0 +1,300 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2004 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Arbitrates membership.
*
* Example usage:
* Find the best kind of peanut butter:
* public Iterator<PeanutButter> findBestKinds (Collection<PeanutButter> pbs)
* {
* return new Predicate<PeanutButter>() {
* public boolean isMatch (PeanutButter pb)
* {
* return pb.isCreamy() && !pb.containsPartiallyHydrogenatedOils();
* }
* }.filter(pbs.iterator());
* }
*
* An interface like this may someday be in the Java library, but until then...
*/
public abstract class Predicate<T>
{
//--------------------------------------------------------------------
// But first, some handy utility classes:
/**
* A simple predicate that includes an object if it is an instance
* of the specified class.
*/
public static class InstanceOf<T> extends Predicate<T>
{
public InstanceOf (Class clazz)
{
_class = clazz;
}
// from Predicate
public boolean isMatch (T obj)
{
return _class.isInstance(obj);
}
/** The class that must be implemented by applicants. */
protected Class _class;
}
/**
* Includes an object if and only if all the predicates with which it
* was constructed also include the object. The predicates
* are tested in the order specified and testing is halted as soon
* as one does not include the object. If the Predicate is constructed
* with no arguments, it will behave like TRUE.
*/
public static class And<T> extends Predicate<T>
{
public And (Predicate<T> ... preds)
{
_preds = preds;
}
// from Predicate
public boolean isMatch (T obj)
{
for (Predicate<T> pred : _preds) {
if (!pred.isMatch(obj)) {
return false;
}
}
return true;
}
/** The predicates that all must be satisfied. */
protected Predicate<T>[] _preds;
}
/**
* Includes an object if at least one of the predicates specified in the
* constructor include the object. The predicates are tested in the
* order specified and testing is halted as soon as one includes
* the object. If the predicate is constructed with no arguments,
* it will behave like FALSE.
*/
public static class Or<T> extends Predicate<T>
{
public Or (Predicate<T> ... preds)
{
_preds = preds;
}
// from Predicate
public boolean isMatch (T obj)
{
for (Predicate<T> pred : _preds) {
if (pred.isMatch(obj)) {
return true;
}
}
return false;
}
protected Predicate<T>[] _preds;
}
/**
* Negates any other predicate.
*/
public static class Not<T> extends Predicate<T>
{
public Not (Predicate<T> negated)
{
_pred = negated;
}
// from Predicate
public boolean isMatch (T obj)
{
return !_pred.isMatch(obj);
}
protected Predicate<T> _pred;
}
//--------------------------------------------------------------------
// Here's the sole abstract method in the Predicate class:
/**
* Does the specified object belong to the special set that we test for?
*/
public abstract boolean isMatch (T obj);
//--------------------------------------------------------------------
// And finally, some handy utility methods:
/**
* Return a new iterator that contains only matching elements from
* the input iterator.
*/
public <E extends T> Iterator<E> filter (final Iterator<E> input)
{
return new Iterator<E>() {
// from Iterator
public boolean hasNext ()
{
return _found || findNext();
}
// from Iterator
public E next ()
{
if (_found || findNext()) {
_found = false;
return _last;
} else {
throw new NoSuchElementException();
}
}
// from Iterator
public void remove ()
{
if (_removeable) {
_removeable = false;
input.remove();
} else {
throw new IllegalStateException();
}
}
private boolean findNext ()
{
boolean result = false;
while (input.hasNext()) {
E candidate = input.next();
if (isMatch(candidate)) {
_last = candidate;
result = true;
break;
}
}
_found = result;
_removeable = result;
return result;
}
private E _last;
private boolean _found; // because _last == null is a valid element
private boolean _removeable;
};
}
/**
* Remove non-matching elements from the specified collection.
*/
public <E extends T> void filter (Collection<E> coll)
{
for (Iterator<E> itr = coll.iterator(); itr.hasNext(); ) {
if (!isMatch(itr.next())) {
itr.remove();
}
}
}
/**
* Create an Iterable view of the specified Iterable that only contains
* elements that match the predicate.
* This Iterable can be iterated over at any time in the future to
* view the current predicate-matching elements of the input Iterable.
*/
public <E extends T> Iterable<E> createView (final Iterable<E> input)
{
return new Iterable<E>() {
public Iterator<E> iterator() {
return filter(input.iterator());
}
};
}
/**
* Create a view of the specified collection that only contains elements
* that match the predicate.
* This collection can be examined at any time in the future to view
* the current predicate-matching elements of the input collection.
*
* Note that the view is not modifiable and currently has poor
* implementations of some methods.
*/
public <E extends T> Collection<E> createView (final Collection<E> input)
{
// TODO: create a collection of the same type?
return new AbstractCollection<E>() {
// from abstract AbstractCollection
public int size ()
{
// oh god, oh god: we iterate and count
int size = 0;
for (E elem : this) {
size++;
}
return size;
}
@Override
public boolean add (E element)
{
return input.add(element);
}
@Override
public boolean remove (Object element)
{
return input.remove(element);
}
@Override
public boolean contains (Object element)
{
try {
@SuppressWarnings("unchecked")
E elem = (E) element;
return isMatch(elem) && input.contains(elem);
} catch (ClassCastException cce) {
// since it's not an E, it can't be a member of our view
return false;
}
}
// from abstract AbstractCollection
public Iterator<E> iterator ()
{
return filter(input.iterator());
}
};
}
}