package com.joe.jsf.view; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.model.DataModel; /*** * An abstract class to represent a paginated DataModel * instance. * * @author minger * * @param <RowType> */ public abstract class AbstractPaginatedDataModel<RowType> extends DataModel { private int rowIndex = 0; private int loadedRowStart = -1; private int loadedRowEnd = -1; private List<RowType> loadedRows; private Integer rowCount; private Map<Long, Boolean> selectionModel = new HashMap<Long, Boolean>(); /*** * Constructor */ public AbstractPaginatedDataModel() { super(); } /*** * Get the page size for this data model * @return */ protected abstract int getPageSize(); /*** * Load the specified range of rows. * * @param startRow The starting row (inclusive) * @param endRow The ending row (inclusive) * @return */ protected abstract List<RowType> loadRows(int startRow, int endRow); /*** * Load the count of rows in the total row set. * * @return */ protected abstract int loadRowCount(); private void loadRows(int rowIndex) { int pageSize = getPageSize(); int startRow = ((rowIndex / pageSize) * pageSize); int endRow = Math.min(rowCount, startRow + pageSize) - 1; this.loadedRows = loadRows(startRow, endRow); loadedRowStart = startRow; loadedRowEnd = endRow; selectionModel.clear(); } @Override public final int getRowCount() { if (rowCount == null) { rowCount = loadRowCount(); } return rowCount.intValue(); } @Override public Object getRowData() { getRowCount(); if (! (rowIndex >= loadedRowStart && rowIndex <= loadedRowEnd)) { if (rowIndex < rowCount) { loadRows(rowIndex); } else { // deal with a bad row index; } } if (rowIndex - loadedRowStart >= loadedRows.size()) { return null; } return loadedRows.get(rowIndex - loadedRowStart); } @Override public final int getRowIndex() { return this.rowIndex; } @Override public final boolean isRowAvailable() { return rowIndex < rowCount; } @Override public final void setRowIndex(int rowIndex) { this.rowIndex = rowIndex; } @Override public void setWrappedData(Object data) { } @Override public Object getWrappedData() { return null; } /*** * Get the rows which have currently been loaded. * * @return */ public final List<RowType> getLoadedRows() { return Collections.unmodifiableList(loadedRows); } /*** * Refresh this data model by resetting the state, and loading * the rowset for the very first row, and clearing the selection * model. * */ public final void refresh() { this.refresh(true); } /*** * Refresh this data model by resetting the state, and loading * the rowset for the very first row, and clearing the selection * model. * */ public final void refresh(boolean loadRows) { this.rowCount = null; this.loadedRows = null; this.loadedRowStart = 0; this.loadedRowEnd = 0; this.selectionModel.clear(); getRowCount(); if (this.rowCount > 0) { this.rowIndex = 0; if (loadRows) { loadRows(this.rowIndex); } } else { this.rowIndex = -1; } } /*** * Gets the selection model. * * @return */ public final Map<Long, Boolean> getSelectionModel() { return selectionModel; } public final int getLoadedRowStart() { return loadedRowStart; } public final int getLoadedRowEnd() { return loadedRowEnd; } public final boolean isRowLoaded(int row) { return row >= this.loadedRowStart && row <= this.loadedRowEnd; } public final int getFirstDisplayRow() { if (loadedRowStart < 0) { return 0; } else { return loadedRowStart; } } }