/* * Copyright 2016 requery.io * * 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 io.requery.sql; import io.requery.query.Expression; import io.requery.util.CloseableIterator; import io.requery.util.IndexAccessible; import io.requery.util.Objects; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.Statement; import java.sql.Wrapper; import java.util.NoSuchElementException; import java.util.Set; /** * {@link java.util.Iterator} implementation that operates over an {@link java.sql.ResultSet} * providing an interface to convert the current result set row into a concrete Java object. * * @param <E> type of element returned by this iterator * * @author Nikhil Purushe */ public class ResultSetIterator<E> implements CloseableIterator<E>, IndexAccessible<E>, Wrapper { private final Set<? extends Expression<?>> selection; private final ResultSet results; private final ResultReader<E> reader; private final boolean closeStatement; private final boolean closeConnection; private int position; private boolean closed; private boolean advanced; ResultSetIterator(ResultReader<E> reader, ResultSet results, Set<? extends Expression<?>> selection, boolean closeStatement, boolean closeConnection) { this.reader = Objects.requireNotNull(reader); this.results = Objects.requireNotNull(results); this.selection = selection; this.closeStatement = closeStatement; this.closeConnection = closeConnection; } @Override public boolean hasNext() { if (closed) { return false; } // result set advanced but next() not called yet if (advanced) { return true; } else { try { if (results.next()) { return advanced = true; } else { // close ourselves close(); return false; } } catch (SQLException e) { return false; } } } @Override public E next() { if (closed) { throw new IllegalStateException(); } try { if (!advanced) { // move forward if (!results.next()) { advanced = false; close(); throw new NoSuchElementException(); } } // else already ready to read E value = reader.read(results, selection); position++; advanced = false; return value; } catch (SQLException e) { NoSuchElementException exception = new NoSuchElementException(); exception.initCause(e); throw exception; } } @Override public E get(int index) { if (index < 0) { throw new IndexOutOfBoundsException(); } try { // if the result set type is TYPE_FORWARD_ONLY, this will throw if (results.absolute(index + 1)) { position = index; if (results.rowDeleted()) { return null; } return reader.read(results, selection); } else { throw new UnsupportedOperationException(); } } catch (SQLException e) { throw new RuntimeException(e); } } @Override public int position() { return position; } @Override public void remove() { try { if (results.isBeforeFirst()) { throw new IllegalStateException(); } if (results.getConcurrency() == ResultSet.CONCUR_UPDATABLE) { results.deleteRow(); } else { throw new UnsupportedOperationException(); } } catch (SQLFeatureNotSupportedException e) { throw new UnsupportedOperationException(e); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public void close() { synchronized (results) { if (!closed) { Statement statement = null; if (closeStatement) { try { statement = results.getStatement(); } catch (SQLException ignored) { } } closeSuppressed(results); if (statement != null) { Connection connection = null; try { connection = statement.getConnection(); } catch (SQLException ignored) { } closeSuppressed(statement); if (closeConnection) { closeSuppressed(connection); } } closed = true; } } } private static void closeSuppressed(AutoCloseable closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception ignored) { } } } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return results.unwrap(iface); } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return results.isWrapperFor(iface); } @Override public boolean equals(Object obj) { if (obj instanceof ResultSetIterator) { ResultSetIterator iterator = (ResultSetIterator) obj; return iterator.results == results; } return false; } @Override public int hashCode() { return Objects.hash(results); } }