blob: 1d02e42def0ec3fa66bd31e16c54d1fb9de6b4ef [file] [log] [blame]
/*
* Copyright 2003,2004 The Apache Software Foundation.
*
* 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.
*/
package org.apache.commons.functor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.apache.commons.functor.core.NoOp;
import org.apache.commons.functor.core.collection.FilteredIterator;
import org.apache.commons.functor.core.collection.TransformedIterator;
import org.apache.commons.functor.core.composite.ConditionalUnaryProcedure;
import org.apache.commons.functor.core.composite.Not;
import org.apache.commons.functor.core.composite.UnaryNot;
import org.apache.commons.functor.generator.BaseGenerator;
import org.apache.commons.functor.generator.Generator;
import org.apache.commons.functor.generator.IteratorToGeneratorAdapter;
/**
* Utility methods and algorithms for applying functors to {@link Generator}s
* and {@link Iterator}s.
* {@link Generator}s also many of these utility methods as instance methods.
* The {@link #select select} and {@link #reject reject} methods
* return new Generators. This becomes useful for constructing nested expressions.
* For example:
*
* <pre>
* Algorithms.apply(new EachElement(list), func1)
* .filter(pred)
* .apply(func2)
* .reject(pred2)
* .to();
* </pre>
*
* @since 1.0
* @version $Revision$ $Date$
* @author Jason Horman (jason@jhorman.org)
* @author Rodney Waldhoff
*/
public final class Algorithms {
/** Public constructor for bean-ish APIs */
public Algorithms() {
}
public static Collection collect(Iterator iter) {
return collect(iter,new ArrayList());
}
public static Collection collect(Iterator iter, Collection col) {
while(iter.hasNext()) {
col.add(iter.next());
}
return col;
}
/**
* {@link ListIterator#set Set} each element of the
* given {@link ListIterator ListIterator} to
* the result of applying the
* given {@link UnaryFunction UnaryFunction} to
* its original value.
*/
public static void transform(ListIterator iter, UnaryFunction func) {
while(iter.hasNext()) {
iter.set(func.evaluate(iter.next()));
}
}
/**
* {@link Iterator#remove Remove} from the
* given {@link Iterator Iterator} all elements
* that fail to match the
* given {@link UnaryPredicate UnaryPredicate}.
*
* @see #remove(Iterator,UnaryPredicate)
*/
public static void retain(Iterator iter, UnaryPredicate pred) {
while(iter.hasNext()) {
if(!(pred.test(iter.next()))) {
iter.remove();
}
}
}
/**
* {@link Iterator#remove Renmove} from the
* given {@link Iterator Iterator} all elements
* that match the
* given {@link UnaryPredicate UnaryPredicate}.
*
* @see #retain(Iterator,UnaryPredicate)
*/
public static void remove(Iterator iter, UnaryPredicate pred) {
while(iter.hasNext()) {
if(pred.test(iter.next())) {
iter.remove();
}
}
}
/**
* Returns an {@link Iterator} that will apply the given {@link UnaryFunction} to each
* element when accessed.
*/
public static final Iterator apply(Iterator iter, UnaryFunction func) {
return TransformedIterator.transform(iter,func);
}
/**
* Returns a {@link Generator} that will apply the given {@link UnaryFunction} to each
* generated element.
*/
public static final Generator apply(final Generator gen, final UnaryFunction func) {
return new BaseGenerator(gen) {
public void run(final UnaryProcedure proc) {
gen.run(
new UnaryProcedure() {
public void run(Object obj) {
proc.run(func.evaluate(obj));
}
});
}
};
}
/**
* Equivalent to
* <code>{@link #contains(Generator,UnaryPredicate) contains}(new {@link org.apache.commons.functor.generator.IteratorToGeneratorAdapter IteratorToGeneratorAdapter}(iter),pred)</code>.
*/
public static final boolean contains(Iterator iter, UnaryPredicate pred) {
return contains(new IteratorToGeneratorAdapter(iter),pred);
}
/**
* Return <code>true</code> if some element in the given {@link Generator}
* that matches the given {@link UnaryPredicate UnaryPredicate}.
*
* @see #detect(Generator,UnaryPredicate)
*/
public static final boolean contains(Generator gen, UnaryPredicate pred) {
FindWithinGenerator finder = new FindWithinGenerator(gen,pred);
gen.run(finder);
return finder.wasFound();
}
/**
* Equivalent to <code>{@link #detect(Generator,UnaryPredicate) detect}(new {@link org.apache.commons.functor.generator.IteratorToGeneratorAdapter IteratorToGeneratorAdapter}(iter),pred)</code>.
*/
public static final Object detect(Iterator iter, UnaryPredicate pred) {
return detect(new IteratorToGeneratorAdapter(iter), pred);
}
/**
* Equivalent to <code>{@link #detect(Generator,UnaryPredicate,Object) detect}(new {@link org.apache.commons.functor.generator.IteratorToGeneratorAdapter IteratorToGeneratorAdapter}(iter),pred,ifNone)</code>.
*/
public static final Object detect(Iterator iter, UnaryPredicate pred, Object ifNone) {
return detect(new IteratorToGeneratorAdapter(iter), pred, ifNone);
}
/**
* Return the first element within the given {@link Generator} that matches
* the given {@link UnaryPredicate UnaryPredicate}, or throw {@link
* java.util.NoSuchElementException NoSuchElementException} if no
* matching element can be found.
*
* @see #detect(Generator,UnaryPredicate,Object)
* @throws NoSuchElementException If no element could be found.
*/
public static final Object detect(final Generator gen, final UnaryPredicate pred) {
FindWithinGenerator finder = new FindWithinGenerator(gen,pred);
gen.run(finder);
if(finder.wasFound()) {
return finder.getFoundObject();
} else {
throw new NoSuchElementException("No element matching " + pred + " was found.");
}
}
/**
* Return the first element within the given {@link Generator} that matches
* the given {@link UnaryPredicate UnaryPredicate}, or return the given
* (possibly <code>null</code>) <code>Object</code> if no matching element
* can be found.
*
* @see #detect(Generator,UnaryPredicate)
*/
public static final Object detect(final Generator gen, final UnaryPredicate pred, Object ifNone) {
FindWithinGenerator finder = new FindWithinGenerator(gen,pred);
gen.run(finder);
return finder.wasFound() ? finder.getFoundObject() : ifNone;
}
/**
* Equivalent to <code>{@link #foreach(Generator,UnaryProcedure) foreach}(new {@link org.apache.commons.functor.generator.IteratorToGeneratorAdapter IteratorToGeneratorAdapter}(iter),proc)</code>.
*/
public static final void foreach(Iterator iter, UnaryProcedure proc) {
foreach(new IteratorToGeneratorAdapter(iter), proc);
}
/**
* {@link UnaryProcedure#run Apply} the given {@link UnaryProcedure
* UnaryProcedure} to each element in the given {@link Generator}.
*/
public static final void foreach(Generator gen, UnaryProcedure proc) {
gen.run(proc);
}
/**
* {@link BinaryFunction#evaluate Evaluate} the pair <i>( previousResult,
* element )</i> for each element in the given {@link Iterator} where
* previousResult is initially <i>seed</i>, and thereafter the result of the
* evaluation of the previous element in the iterator. Returns the result
* of the final evaluation.
*
* <p>
* In code:
* <pre>
* while(iter.hasNext()) {
* seed = func.evaluate(seed,iter.next());
* }
* return seed;
* </pre>
*/
public static final Object inject(Iterator iter, Object seed, BinaryFunction func) {
while(iter.hasNext()) {
seed = func.evaluate(seed,iter.next());
}
return seed;
}
/**
* {@link BinaryFunction#evaluate Evaluate} the pair <i>( previousResult,
* element )</i> for each element in the given {@link Generator} where
* previousResult is initially <i>seed</i>, and thereafter the result of the
* evaluation of the previous element in the iterator. Returns the result
* of the final evaluation.
*
* <p>
* In code:
* <pre>
* while(iter.hasNext()) {
* seed = func.evaluate(seed,iter.next());
* }
* return seed;
* </pre>
*/
public static final Object inject(Generator gen, final Object seed, final BinaryFunction func) {
Injector injector = new Injector(seed,func);
gen.run(injector);
return injector.getResult();
}
/**
* Returns an {@link Iterator} that will only return elements that DO
* NOT match the given predicate.
*/
public static Iterator reject(Iterator iter, UnaryPredicate pred) {
return FilteredIterator.filter(iter,UnaryNot.not(pred));
}
/**
* Returns a {@link Generator} that will only "generate" elements that DO
* NOT match the given predicate.
*/
public static Generator reject(final Generator gen, final UnaryPredicate pred) {
return new BaseGenerator(gen) {
public void run(final UnaryProcedure proc) {
gen.run(new ConditionalUnaryProcedure(pred,NoOp.instance(),proc));
}
};
}
/**
* Returns an {@link Iterator} that will only return elements that DO
* match the given predicate.
*/
public static final Iterator select(Iterator iter, UnaryPredicate pred) {
return FilteredIterator.filter(iter,pred);
}
/**
* Returns a {@link Generator} that will only "generate" elements that DO
* match the given predicate.
*/
public static final Generator select(final Generator gen, final UnaryPredicate pred) {
return new BaseGenerator(gen) {
public void run(final UnaryProcedure proc) {
gen.run(new ConditionalUnaryProcedure(pred,proc,NoOp.instance()));
}
};
}
public static final void dountil(Procedure proc, Predicate pred) {
dowhile(proc,Not.not(pred));
}
public static final void dowhile(Procedure proc, Predicate pred) {
do { proc.run(); } while(pred.test());
}
public static final void untildo(Predicate pred, Procedure proc) {
whiledo(Not.not(pred),proc);
}
public static final void whiledo(Predicate pred, Procedure proc) {
while(pred.test()) { proc.run(); }
}
/**
* Equivalent to
* <code>{@link #reject(Iterator,UnaryPredicate) reject}(iter,pred)</code>.
*/
public static final Iterator until(final Iterator iter, final UnaryPredicate pred) {
return reject(iter,pred);
}
/**
* Equivalent to
* <code>{@link #reject(Generator,UnaryPredicate) reject}(gen,pred)</code>.
*/
public static final Generator until(final Generator gen, final UnaryPredicate pred) {
return reject(gen,pred);
}
/**
* Tail recursion for {@link Function functions}. If the {@link Function}
* returns another function of the same type as the original, that function
* is executed. Functions are executed until a non function value or a
* function of a different type is returned.
*/
public static final Object recurse(Function function) {
Object result = null;
Class recursiveFunctionClass = function.getClass();
// if the function returns another function, execute it. stop executing
// when the function doesn't return another function of the same type.
while(true) {
result = function.evaluate();
if(recursiveFunctionClass.isInstance(result)) {
function = (Function)result;
continue;
} else {
break;
}
}
return result;
}
// inner classes
//---------------------------------------------------------------
private static class FindWithinGenerator implements UnaryProcedure {
FindWithinGenerator(Generator gen, UnaryPredicate pred) {
this.generator = gen;
this.predicate = pred;
this.found = false;
this.foundObject = null;
}
public void run(Object obj) {
if(predicate.test(obj)) {
found = true;
foundObject = obj;
generator.stop();
}
}
boolean wasFound() {
return found;
}
Object getFoundObject() {
return foundObject;
}
private UnaryPredicate predicate = null;
private boolean found = false;
private Object foundObject = null;
private Generator generator = null;
}
private static class Injector implements UnaryProcedure {
Injector(Object seed, BinaryFunction function) {
this.seed = seed;
this.function = function;
}
public void run(Object obj) {
seed = function.evaluate(seed,obj);
}
Object getResult() {
return seed;
}
private Object seed = null;
private BinaryFunction function = null;
}
}