/*
 * Copyright 2017, TeamDev Ltd. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.storage.jdbc;

import io.spine.annotation.Internal;

import javax.annotation.Nullable;
import java.io.Closeable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * An iterator over a {@link ResultSet} of storage records.
 *
 * <p>Uses {@link Serializer} to deserialize records.
 *
 * <p><b>NOTE:</b> {@code remove} operation is not supported.
 *
 * @param <R> type of storage records
 * @author Alexander Litus
 */
@Internal
public abstract class DbIterator<R> implements Iterator<R>, Closeable {

    private final ResultSet resultSet;
    @Nullable
    private final PreparedStatement statement;
    private final String columnName;
    private boolean hasNextCalled = false;
    private boolean nextCalled = true;
    private boolean memoizedHasNext = false;

    /**
     * Creates a new iterator instance.
     *
     * @param statement  a statement used to retrieve a result set
     *                   (both statement and result set are closed in {@link #close()}).
     * @param columnName a name of a serialized storage record column
     * @throws DatabaseException if an error occurs during interaction with the DB
     */
    protected DbIterator(PreparedStatement statement, String columnName) throws DatabaseException {
        try {
            this.resultSet = statement.executeQuery();
            this.columnName = columnName;
            this.statement = statement;
        } catch (SQLException e) {
            throw new DatabaseException(e);
        }
    }

    /**
     * Creates a new iterator instance.
     *
     * @param resultSet  the results of a DB query to iterate over
     * @param columnName a name of a serialized storage record column
     * @throws DatabaseException if an error occurs during interaction with the DB
     */
    protected DbIterator(ResultSet resultSet, String columnName) {
        this.resultSet = resultSet;
        this.columnName = columnName;
        this.statement = null;
    }

    /**
     * {@inheritDoc}
     *
     * <p>Calls {@link #close()}, if {@link ResultSet#next()} returns {@code false}.
     *
     * @return {@inheritDoc}
     */
    @Override
    public boolean hasNext() {
        if (!nextCalled) {
            return memoizedHasNext;
        }
        try {
            final boolean hasNextElem = resultSet.next();
            if (!hasNextElem) {
                close();
            }

            // ResultSet.previous() is not used here because some JDBC drivers do not support it.
            memoizedHasNext = hasNextElem;

            hasNextCalled = true;
            nextCalled = false;

            return hasNextElem;
        } catch (SQLException e) {
            throw new DatabaseException(e);
        }
    }

    @Override
    public R next() {
        if (!memoizedHasNext && (hasNextCalled || !hasNext())) {
            throw noSuchElement();
        }

        hasNextCalled = false;
        memoizedHasNext = false;
        nextCalled = true;

        final R result;
        try {
            result = readResult();
        } catch (SQLException e) {
            throw new DatabaseException(e);
        }
        return result;
    }

    protected abstract R readResult() throws SQLException;

    protected ResultSet getResultSet() {
        return resultSet;
    }

    protected String getColumnName() {
        return columnName;
    }

    /**
     * Removal is unsupported.
     *
     * @throws UnsupportedOperationException always
     * @deprecated as unsupported
     */
    @Override
    @Deprecated
    public final void remove() throws UnsupportedOperationException {
        throw new UnsupportedOperationException("Removing is not supported.");
    }

    @Override
    public void close() throws DatabaseException {
        try {
            resultSet.close();
            if (statement != null) {
                statement.close();
                final Connection connection = statement.getConnection();
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException e) {
            throw new DatabaseException(e);
        }
    }

    private static NoSuchElementException noSuchElement() {
        throw new NoSuchElementException("No elements remained.");
    }
}

@@ -1091,42 +1091,8 @@
 l;%0A%0A
-import javax.annotation.Nullable;%0A
 impo
@@ -1229,24 +1229,51 @@
 LException;%0A
+import java.sql.Statement;%0A
 import java.
@@ -1392,24 +1392,158 @@
 records.%0A *%0A
+ * %3Cp%3EThis class internally uses %7B@link ResultSet%7D.%0A * See how to %7B@linkplain #close() finish%7D the usage of the iterator properly.%0A *%0A
  * %3Cp%3EUses %7B
@@ -1847,69 +1847,8 @@
 et;%0A
-    @Nullable%0A    private final PreparedStatement statement;%0A
     
@@ -2595,48 +2595,8 @@
 me;%0A
-            this.statement = statement;%0A
     
@@ -3115,39 +3115,8 @@
 me;%0A
-        this.statement = null;%0A
     
@@ -4838,32 +4838,310 @@
 rted.%22);%0A    %7D%0A%0A
+    /**%0A     * Closes %7B@link #resultSet%7D and the related %7B@link Statement%7D and %7B@link Connection%7D.%0A     *%0A     * %3Cp%3EThis method should be called either manually or called by %7B@link #hasNext()%7D.%0A     *%0A     * @throws DatabaseException if %7B@code SQLException%7D is occurred%0A     */%0A
     @Override%0A  
@@ -5226,32 +5226,98 @@
 ultSet.close();%0A
+            final Statement statement = resultSet.getStatement();%0A
             if (

Improve Javadocs of `DbIterator`