Saturday, August 30, 2003

Java filter iterator

Several separate Java projects have led to using the same simple Iterator implementation, AbstractFilterIterator. The idea is very simple: use a single method to filter an underlying Iterator and decide which subset of the data to reveal. Perhaps it is more easily shown than explained, at least for me:

/*
 * AbstractFilterIterator.java
 * Copyright 2003 (C) B. K. Oxley (binkley)
 * <binkley@alumni.rice.edu>
 *
 * 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
 *
 * Created on July 15, 2003.
 *
 * $Id: AbstractFilterIterator.java,v 1.1 2003/08/28 19:07:52 binkley Exp $
 */

package hm.binkley.util;

import java.util.Iterator;
import java.util.ListIterator;

/**
 * <code>AbstractFilterIterator</code> is a partial list iterator.
 *
 * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
 * @version 1.0
 */
public abstract class AbstractFilterIterator
    implements Iterator {
    /** <code>ListIterator</code> through the elements. */
    protected ListIterator it;

    /** The current element after {@link #next()}. */
    protected Object cursor = null;

    /**
     * Construct an <code>AbstractFilterIterator</code> from an
     * underlying iterator over a range.
     *
     * @param it the underlying iterator
     *
     */
    protected AbstractFilterIterator(ListIterator it) {
	this.it = it;
    }

    /**
     * Match <var>element</var>.  If the match is
     * <code>true</code>, <code>AbstractFilterIterator</code> will
     * present <var>element</var> in the view of the underlying
     * iterator.
     *
     * @param element the element to match
     * @return <code>true</code> if the element matches
     */
    protected abstract boolean match(Object element);

    /**
     * Scroll the underlying iterator forward to find the next match.
     * Afterwards, use {@link #rewindNext(int)} to return the
     * underlying iterator to its original position.
     *
     * @return boolean <code>true</code> if there is a next match
     */
    protected boolean findNext() {
	while (it.hasNext())
	    if (match(it.next()))
		return true;

	return false;
    }

    /**
     * Return the underlying iterator to previous position after an
     * earlier call to {@link #findNext()}.
     *
     * @param current the previous position
     */
    protected void rewindNext(int current) {
	for (int next = it.nextIndex(); next > current; --next)
	    it.previous();
    }

    /** {@inheritDoc} */
    public boolean hasNext() {
	// We have do it this way so that you can use next()
	// independently of hasNext().
	int current = it.nextIndex();
	boolean hasNext = findNext();

	rewindNext(current);

	return hasNext;
    }

    /** {@inheritDoc} */
    public Object next() {
	while (!match(cursor = it.next()))
	    ; // nothing

	return cursor;
    }

    /** {@inheritDoc} */
    public void remove() {
	it.remove();
    }
}

As you might suspect, AbstractFilterListIterator is similar and extends AbstractFilterIterator to include methods from ListIterator. This is very straight-forward as AbstractFilterIterator already has a protected ListIterator member, it, to work with.

No comments: