/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.jena.jdbc.results;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import org.apache.jena.jdbc.statements.JenaStatement;
import org.apache.jena.query.QueryExecution ;
/**
* Represents a set of streamed results backed by some {@link QueryExecution},
* streamed results are considered to be forward only
*
* @param <T>
* Type of the underlying result rows
*
*/
public abstract class StreamedResults<T> extends QueryExecutionResults {
private T currItem;
private boolean finished = false;
private int currRow = 0;
/**
* Creates new streamed results
*
* @param statement
* Statement that created the result set
* @param qe
* Query Execution
* @param commit
* Whether a commit is necessary when the results are closed
* @throws SQLException
* Thrown if the arguments are invalid
*/
public StreamedResults(JenaStatement statement, QueryExecution qe, boolean commit) throws SQLException {
super(statement, qe, commit);
}
/**
* Gets the current result row (if any)
*
* @return Result row, null if not at a row
* @throws SQLException
* Thrown if the result set is closed
*/
protected T getCurrentRow() throws SQLException {
if (this.isClosed())
throw new SQLException("Result set is closed");
return this.currItem;
}
/**
* Method which derived classes must implement to indicate whether they have
* further rows available
*
* @return True if further rows are available, false otherwise
* @throws SQLException
* Thrown if an error determining whether rows are available
*/
protected abstract boolean hasNext() throws SQLException;
/**
* Method which derived classes must implement to provide the next row
* available
*
* @return Next row available
* @throws SQLException
* Thrown if this method is invoked when no further rows are
* available
*/
protected abstract T moveNext() throws SQLException;
@Override
public final boolean absolute(int row) throws SQLException {
if (this.isClosed()) {
throw new SQLException("Cannot move to a row after the result set has been closed");
} else if (row == 1) {
// Try and move to the first row
return this.first();
} else if (row == -1) {
// Try and move to the last row
return this.last();
} else if (row <= 0) {
// Can't move to an arbitrary relative row from the end of the
// results
throw new SQLException(
"Jena JDBC result sets are forward only, cannot move to a row which is relative to the end of the result set since the number of result rows is not known in advance");
} else if (row == this.currRow) {
// Already at the desired row
return true;
} else if (row < this.currRow) {
throw new SQLException("Jena JDBC result sets are forward only, cannot move backwards");
} else {
// Before the desired row
while (this.hasNext() && this.currRow < row) {
this.currItem = this.moveNext();
this.currRow++;
}
// If we didn't reach it we hit the end of the result set
if (this.currRow < row) {
this.finished = true;
this.currItem = null;
}
return (row == this.currRow);
}
}
@Override
public final void afterLast() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
if (finished)
return;
// Move to end of results
while (this.hasNext()) {
this.currItem = this.moveNext();
this.currRow++;
}
this.currItem = null;
this.finished = true;
}
@Override
public final void beforeFirst() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
// If we've started throw an error as we can't move backwards
if (this.currRow > 0)
throw new SQLException(
"Jena JDBC result sets are forward only, can't move to before the start of the result set after navigation through the result set has begun");
// Otherwise OK
this.currItem = null;
}
@Override
protected final void closeInternal() throws SQLException {
this.currItem = null;
this.finished = true;
this.closeStreamInternal();
}
protected abstract void closeStreamInternal() throws SQLException;
@Override
public final boolean first() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
if (this.currRow == 1)
return true;
throw new SQLException(
"Jena JDBC result sets are forward only, can't move backwards to the first row after the first row has been passed");
}
@Override
public final int getFetchDirection() {
return ResultSet.FETCH_FORWARD;
}
@Override
public final int getFetchSize() {
// TODO Need a buffering wrapper around ResultSet to make this
// configurable
return 0;
}
@Override
public final int getRow() {
return this.currRow;
}
@Override
public final int getType() {
return ResultSet.TYPE_FORWARD_ONLY;
}
@Override
public final boolean isAfterLast() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
return this.finished;
}
@Override
public final boolean isBeforeFirst() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
return this.currRow == 0;
}
@Override
public final boolean isFirst() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
return this.currRow == 1;
}
@Override
public final boolean isLast() throws SQLException {
if (this.isClosed())
throw new SQLException("Result Set is closed");
return !this.hasNext();
}
@Override
public final boolean last() throws SQLException {
if (this.isClosed() || this.finished) {
throw new SQLException("Jena JDBC Result Sets are forward-only");
} else {
while (this.hasNext()) {
this.currItem = this.moveNext();
this.currRow++;
}
return true;
}
}
@Override
public final boolean next() throws SQLException {
if (this.isClosed()) {
throw new SQLException("Cannot move to the next row in a closed result set");
} else {
if (this.hasNext()) {
this.currItem = this.moveNext();
this.currRow++;
return true;
} else {
if (!this.finished)
this.currRow++;
this.finished = true;
return false;
}
}
}
@Override
public final boolean relative(int rows) throws SQLException {
if (this.isClosed()) {
throw new SQLException("Cannot move to a row after the result set has been closed");
} else if (rows == 0) {
// Already at the desired row
return true;
} else if (rows < 0) {
throw new SQLException("Jena JDBC result sets are forward only, cannot move backwards");
} else {
// Before the desired row
int moved = 0;
while (this.hasNext() && moved < rows) {
this.currItem = this.moveNext();
this.currRow++;
moved++;
}
// If we didn't reach it we hit the end of the result set
if (moved < rows) {
this.finished = true;
this.currItem = null;
}
return (rows == moved);
}
}
@Override
public final void setFetchDirection(int direction) throws SQLException {
if (direction != ResultSet.FETCH_FORWARD)
throw new SQLFeatureNotSupportedException("Jena JDBC Result Sets only support forward fetch");
}
@Override
public final void setFetchSize(int rows) throws SQLException {
// TODO Need to provide some buffering wrapper over a ResultSet to make
// this possible
throw new SQLFeatureNotSupportedException();
}
}