001/*
002 * #%L
003 * Netarchivesuite - common
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.common.utils;
025
026import java.sql.ResultSet;
027import java.sql.SQLException;
028import java.sql.Statement;
029import java.util.Iterator;
030import java.util.NoSuchElementException;
031
032import dk.netarkivet.common.exceptions.ArgumentNotValid;
033import dk.netarkivet.common.exceptions.IOFailure;
034
035/**
036 * Similar to a FilterIterator, but takes a java.sql.ResultSet (which is neither Iterable, Iterator nor Enumeration).
037 *
038 * @param <T> The type returned by the ResultSetIterator
039 */
040public abstract class ResultSetIterator<T> implements Iterator<T> {
041
042    /** The current Statement that this Result originates from. */
043    private final Statement stm;
044
045    /** The current ResultSet that this Iterator operates upon. */
046    private final ResultSet res;
047
048    /** Temporary storage to hold the object that the Iterator returns. */
049    private T objectCache;
050
051    /** Tells us whether the resultset is closed yet. */
052    private boolean isClosed = false;
053
054    /**
055     * Constructor for this class.
056     *
057     * @param res a ResultSet for this Iterator to operate on.
058     */
059    public ResultSetIterator(Statement stm, ResultSet res) {
060        ArgumentNotValid.checkNotNull(stm, "Statement");
061        ArgumentNotValid.checkNotNull(res, "ResultSet");
062        this.stm = stm;
063        this.res = res;
064    }
065
066    /**
067     * Returns <tt>true</tt> if the iteration has more elements. (In other words, returns <tt>true</tt> if <tt>next</tt>
068     * would return an element rather than throwing an exception.)
069     *
070     * @return <tt>true</tt> if the iterator has more elements.
071     */
072    public boolean hasNext() {
073        if (objectCache == null) {
074            try {
075                if (!isClosed && res.next()) {
076                    objectCache = filter(res);
077                } else {
078                    isClosed = true;
079                    res.close();
080                    stm.close();
081                }
082            } catch (SQLException e) {
083                throw new IOFailure("SQL error getting next element from " + res + "\n"
084                        + ExceptionUtils.getSQLExceptionCause(e), e);
085            }
086        }
087        return objectCache != null;
088    }
089
090    /**
091     * Returns the object corresponding to the given object, or null if that object is to be skipped.
092     *
093     * @param result An object in the source iterator domain
094     * @return An object in this iterators domain, or null
095     */
096    public abstract T filter(ResultSet result);
097
098    /**
099     * Returns the next element in the iteration. Calling this method repeatedly until the {@link #hasNext()} method
100     * returns false will return each element in the underlying collection exactly once.
101     *
102     * @return the next element in the iteration.
103     * @throws NoSuchElementException iteration has no more elements.
104     */
105    public T next() {
106        if (objectCache != null) {
107            T obj = objectCache;
108            objectCache = null;
109            return obj;
110        }
111        throw new NoSuchElementException();
112    }
113
114    /**
115     * Removes from the underlying collection the last element returned by the iterator (optional operation). This
116     * method can be called only once per call to <tt>next</tt>. The behavior of an iterator is unspecified if the
117     * underlying collection is modified while the iteration is in progress in any way other than by calling this
118     * method.
119     *
120     * @throws UnsupportedOperationException if the <tt>remove</tt> operation is not supported by this Iterator.
121     * @throws IllegalStateException if the <tt>next</tt> method has not yet been called, or the <tt>remove</tt> method
122     * has already been called after the last call to the <tt>next</tt> method.
123     */
124    public void remove() {
125        throw new UnsupportedOperationException("This class does not support remove()");
126    }
127
128}