xdev.db.jdbc.VTPageScroller Maven / Gradle / Ivy
/*
* XDEV Application Framework - XDEV Application Framework
* Copyright © 2003 XDEV Software (https://xdev.software)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program. If not, see .
*/
package xdev.db.jdbc;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import xdev.db.DBException;
import xdev.db.DelegatingResult;
import xdev.db.Result;
import xdev.vt.VirtualTable;
/**
* This class wraps a scrollable ResultSet
object and provides
* methods to create "pages" of it as VirtualTable
objects. A
* "page" is a number of consecutive rows of the ResultSet
object,
* ranging from rows x to y.
* The ResultSet
object object is kept open until closed
* explicitely.
*
* Example:
*
*
* //rs is any scrollable ResultSet object
* final VTPageScroller pager = new VTPageScroller(rs, 100, 1);
* //ResultSet rs, int rowsPerPage, int firstRowIndex
*
* VirtualTable vt;
* vt = pager.currentPage(); 
* //create VT instance for current page (rows 1 to 100)
* vt = pager.nextPage();    
* //scroll page index forward and create new VT instance (rows 101 to 200)
* vt = pager.nextPage();    
* //scroll page index forward and create new VT instance (rows 201 to 300)
* vt = pager.previousPage();
* //scroll page index backward and create new VT instance (rows 101 to 200)
*
* pager.close(); //rs must be closed manually after use
*
*
* @author Thomas Muenz
*
*/
public class VTPageScroller
{
// /////////////////////////////////////////////////////////////////////////
// instance fields //
// //////////////////
/**
* the ResultSet
object wrapped by this wrapper object.
*/
private final ResultSet rs;
/**
* A hint to the total row count the wrapped ResultSet
object
* can yield, may be null.
*/
private Integer totalRows;
/**
* The amount of rows to be retrieved per "page" (per scroll step) .
*/
private int rowsPerPage;
/**
* The index of the first row that will be contained in the next "page".
*/
private int currentRowIndex;
/**
* Flag to allow the page index to be negative (scroll rs from the end).
*/
private boolean allowNegativeRowIndex = true;
// /////////////////////////////////////////////////////////////////////////
// constructors //
// ///////////////
/**
* Alias for VTPageScroller(rs, 10)
.
*
* @param rs
* the scrollable ResultSet
object to be wrapped.
* @throws SQLException
* if rs
is not scrollable or
* rs.getType()
throws a SQLExcpetion
*/
public VTPageScroller(final ResultSet rs) throws SQLException
{
this(rs,10);
}
/**
* Alias for VTPageScroller(rs, rowsPerPage, 1)
.
*
*
* @param rs
* the scrollable ResultSet
object to be wrapped.
* @param rowsPerPage
* the number of rows to be retrieved per "page".
* @throws SQLException
* if rs
is not scrollable or
* rs.getType()
throws a SQLExcpetion
*/
public VTPageScroller(final ResultSet rs, final int rowsPerPage) throws SQLException
{
this(rs,rowsPerPage,1);
}
/**
* Alias for
* VTPageScroller(rs, rowsPerPage, firstRowIndex, null)
.
*
* @param rs
* the scrollable ResultSet
object to be wrapped.
* @param rowsPerPage
* the number of rows to be retrieved per "page".
* @param firstRowIndex
* the current row index of the first row in the first "page" to
* be created.
* @throws SQLException
* if rs
is not scrollable or
* rs.getType()
throws a SQLExcpetion
*/
public VTPageScroller(final ResultSet rs, final int rowsPerPage, final int firstRowIndex)
throws SQLException
{
this(rs,rowsPerPage,firstRowIndex,null);
}
/**
* Creates a new VTPageScroller
object with rs
as
* its wrapped scrollable ResultSet
object. If rs
* is not scrollable, a is thrown. Also, the call of
* rs.getType()
my throw a SQLException depending on the state
* and implementation of rs
.
*
* @param rs
* the scrollable ResultSet
object to be wrapped.
* @param rowsPerPage
* the number of rows to be retrieved per "page".
* @param firstRowIndex
* the current row index of the first row in the first "page" to
* be created.
* @param totalRows
* a hint to how much rows the ResultSet
object
* provides in total.
* @throws SQLException
* if rs
is not scrollable or
* rs.getType()
throws a SQLExcpetion
*/
public VTPageScroller(final ResultSet rs, final int rowsPerPage, final int firstRowIndex,
final Integer totalRows) throws SQLException
{
super();
final int rsType = rs.getType();
if(rsType != ResultSet.TYPE_SCROLL_SENSITIVE && rsType != ResultSet.TYPE_SCROLL_INSENSITIVE)
{
throw new SQLException("ResultSet of type " + rsType + " is not scrollable!");
}
this.rs = rs;
this.totalRows = totalRows;
this.rowsPerPage = rowsPerPage;
this.currentRowIndex = firstRowIndex;
}
// /////////////////////////////////////////////////////////////////////////
// getters //
// ///////////////////
/**
* @return the totalRows
*/
public Integer getTotalRows()
{
return totalRows;
}
/**
* @return the rowsPerPage
*/
public int getRowsPerPage()
{
return rowsPerPage;
}
/**
* @return the currentRowIndex
*/
public int getCurrentRowIndex()
{
return currentRowIndex;
}
/**
* @return the allowNegativePosition
*/
public boolean isAllowNegativeRowIndex()
{
return allowNegativeRowIndex;
}
// /////////////////////////////////////////////////////////////////////////
// setters //
// ///////////////////
/**
* @param totalRows
* the totalRows to set
*/
public VTPageScroller setTotalRows(Integer totalRows)
{
this.totalRows = totalRows;
return this;
}
/**
* @param rowsPerPage
* the rowsPerPage to set
*/
public VTPageScroller setRowsPerPage(int rowsPerPage)
{
this.rowsPerPage = rowsPerPage;
return this;
}
/**
* @param currentRowIndex
* the currentRowIndex to set
*/
public VTPageScroller setCurrentRowIndex(int currentRowIndex)
{
this.currentRowIndex = currentRowIndex;
return this;
}
/**
* @param allowNegativeRowIndex
* the allowNegativeRowIndex to set
*/
public VTPageScroller setAllowNegativeRowIndex(boolean allowNegativeRowIndex)
{
this.allowNegativeRowIndex = allowNegativeRowIndex;
return this;
}
// /////////////////////////////////////////////////////////////////////////
// declared methods //
// ///////////////////
/**
* Sets the cursor (current row) of the wrapped scrollable
* ResultSet
object to rowIndexFrom
and creates a
* new VirtualTable
object from that row on with a length of
* rowCount
rows.
*
* @param rowIndexFrom
* the index of the ResultSet
object row to be the
* first row of the created "page".
* @param rowCount
* the amount of rows to be contained in the created "page"
* @return a newly created VirtualTable
object with rows from
* rowIndexFrom
till
* (rowIndexFrom + rowCount)
.
* @throws SQLException
* any SQLException that can be thrown in the process by the
* ResultSet
implementation
*/
public VirtualTable createVirtualTable(final int rowIndexFrom, final int rowCount)
throws SQLException
{
if(rowIndexFrom < 0 && !this.allowNegativeRowIndex)
{
throw new IndexOutOfBoundsException("No negative rowIndex allowed by setting");
}
rs.absolute(rowIndexFrom);
Result r = new JDBCResult(rs,0,0,rowsPerPage);
r = new DelegatingResult(r)
{
public void close() throws DBException
{
// do nothing
}
};
return new VirtualTable(r,true);
}
/**
* Alias for createVirtualTable(rowIndexFrom, getRowsPerPage())
* .
*
* @param rowIndexFrom
* @return a newly created VirtualTable
object with rows from
* rowIndexFrom
till
* (rowIndexFrom + getRowsPerPage())
.
* @throws SQLException
* any SQLException that can be thrown in the process by the
* ResultSet
implementation
*/
public VirtualTable createVirtualTable(final int rowIndexFrom) throws SQLException
{
return createVirtualTable(rowIndexFrom,getRowsPerPage());
}
/**
* Alias for
* createVirtualTable(getCurrentRowIndex(), getRowsPerPage())
.
*
* @return a newly created VirtualTable
object with rows from
* getCurrentRowIndex()
till
* (getCurrentRowIndex() + getRowsPerPage())
.
* @throws SQLException
* any SQLException that can be thrown in the process by the
* ResultSet
implementation.
*/
public VirtualTable currentPage() throws SQLException
{
return createVirtualTable(getCurrentRowIndex(),getRowsPerPage());
}
/**
*
* @return true if either allowNegativeRowIndex
or
* currentRowIndex - rowsPerPage >= 0
is true.
*/
public boolean hasPreviousPage()
{
return this.allowNegativeRowIndex || this.currentRowIndex - this.rowsPerPage >= 0;
}
/**
* Decrements currentRowIndex
by rowsPerPage
and
* calls createVirtualTable(currentRowIndex, rowsPerPage)
.
*
* @return a newly created VirtualTable containing the previous "page" (data
* from currentRowIndex
with a length of
* rowsPerPage
)
* @throws SQLException
* any SQLException that can be thrown in the process by the
* ResultSet
implementation.
*/
public VirtualTable previousPage() throws SQLException
{
if(!this.hasPreviousPage())
return null;
return this.previousPageNoCheck();
}
protected VirtualTable previousPageNoCheck() throws SQLException
{
this.currentRowIndex -= this.rowsPerPage;
return createVirtualTable(this.currentRowIndex,this.rowsPerPage);
}
/**
* If the totalRows
value of this VTPageScroller
* object is null (unknown), then null
* (unknown) is returned.
* Otherwise, if currentRowIndex
+ rowsPerPage
<=
* totalRows
, then TRUE is returned. Else
* FALSE.
*
* @return either null (unknown), TRUE or
* FALSEdescribing if there is a next page.
*/
public Boolean hasNextPage()
{
if(this.totalRows == null)
return null;
return this.currentRowIndex + this.rowsPerPage <= this.totalRows ? TRUE : FALSE;
}
/**
* Increments currentRowIndex
by rowsPerPage
and
* calls createVirtualTable(currentRowIndex, rowsPerPage)
.
*
* @return a newly created VirtualTable containing the next "page" (data
* from currentRowIndex
with a length of
* rowsPerPage
) or null if no next page is
* available according to totalRows
.
* @throws SQLException
*/
public VirtualTable nextPage() throws SQLException
{
final Boolean b = this.hasNextPage();
if(b == null ? false : !b)
return null;
// if(isFalse(this.hasNextPage())) return null;
return nextPageNoCheck();
}
protected VirtualTable nextPageNoCheck() throws SQLException
{
this.currentRowIndex += this.rowsPerPage;
return createVirtualTable(this.currentRowIndex,this.rowsPerPage);
}
/**
* Tries to call ResultSet.close()
on the used
* ResultSet
object.
* If a SQLexception occurs, it is ignored. To preserve the exception
* without throwing it to the outer context, the occured SQLException is
* returned. If not SQLException occured, null is returned.
*
* @return the occured SQLException or null if no exception
* occured.
*/
public SQLException close()
{
try
{
this.rs.close();
return null;
}
catch(SQLException e)
{
return e;
}
}
/**
* If closeStatment
is false this method behaves
* exactely like close()
.
* Otherwise, the wrapped ResultSet
object's statement is
* closed (which automatically closes the ResultSet
object as
* specified in Statement.close()
). If the wrapped
* ResultSet
object does not have an associated statment, again
* simply close()
is called.
*
* If a SQLexception occurs, it is ignored. To preserve the exception
* without throwing it to the outer context, the occured SQLException is
* returned. If no SQLException occured, null is returned.
*
* @param closeStatement
* flag indicating whether the ResultSet
object's
* statement shall be closed instead.
* @return any SQLException occured during the process to preserve that
* information.
*/
public SQLException close(final boolean closeStatement)
{
if(!closeStatement)
{
return this.close();
}
try
{
final Statement stmt = this.rs.getStatement();
if(stmt == null)
{
return this.close();
}
stmt.close(); // resultSet is closed automatically as demanded by
// JDBC standard
return null;
}
catch(SQLException e)
{
return e;
}
}
}