package jeffaschenk.commons.container.session; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.Map; import java.util.TreeMap; /** * Simple POJO to contain the Search Criteria for a given Web Page. * * @author jeffaschenk@gmail.com * @version $Id: $ */ public class PagableSearchCriteria implements java.io.Serializable { private static final long serialVersionUID = 1L; // *************************************** // Logging /** * Constant <code>log</code> */ protected static Log log = LogFactory.getLog(PagableSearchCriteria.class); /** * Paging Directions */ public static final Integer DIRECTION_NONE = 0; /** * Constant <code>DIRECTION_BACKWARD</code> */ public static final Integer DIRECTION_BACKWARD = 1; /** * Constant <code>DIRECTION_FORWARD</code> */ public static final Integer DIRECTION_FORWARD = 2; /** * Constant <code>DIRECTION_GOTOPAGE</code> */ public static final Integer DIRECTION_GOTOPAGE = 3; /** * String Paging Directions */ public static final String SEARCH = "Search"; /** * Constant <code>PREVIOUS="Previous"</code> */ public static final String PREVIOUS = "Previous"; /** * Constant <code>NEXT="Next"</code> */ public static final String NEXT = "Next"; /** * Constant <code>GOTOPAGE="GoToPage"</code> */ public static final String GOTOPAGE = "GoToPage"; /** * Default Maximum Rows per Page */ public static final int DEFAULT_MAX_PER_PAGE = 50; /** * Name of this Search Criteria */ private String name; /** * Button Clicked to Determine Direction and if a new search filter was * entered. */ private String buttonClicked; /** * Search criteria for Page, used to Display. */ private String searchCriteria; /** * Actual Search criteria for Page used to send to DAO Layer. */ private String actualSearchCriteria; /** * Full Row count of Element List. */ private int rowCount = 0; /** * Rows per Page */ private int rowsPerPage = DEFAULT_MAX_PER_PAGE; /** * Current Page Number */ private int currentPageNumber = 1; /** * Map of Row Segments */ private Map<Integer, PageSegmentBounds> pageSegments = new TreeMap<Integer, PageSegmentBounds>(); /** * Selected Page Number for Go To */ private int selectedPageNumber = 1; /** * Available Rows Per Page */ private int[] availableRowsPerPage = {10, 20, 30, 40, 50, 100, 300}; /** * default Constructor */ public PagableSearchCriteria() { super(); } /** * Constructor with specified Name * * @param name a {@link java.lang.String} object. */ public PagableSearchCriteria(String name) { super(); this.name = name; } /** * name * * @return {@link java.lang.String} object. */ public String getName() { return name; } /** * <p>Setter for the field <code>name</code>.</p> * * @param name the name to set */ public void setName(String name) { this.name = name; } /** * Get Search criteria on Display and Entered by User * * @return {@link java.lang.String} object. */ public String getSearchCriteria() { return searchCriteria; } /** * <p>Setter for the field <code>searchCriteria</code>.</p> * * @param searchCriteria the searchCriteria to set */ public void setSearchCriteria(String searchCriteria) { this.searchCriteria = searchCriteria; this.actualSearchCriteria = searchCriteria; if ((this.actualSearchCriteria != null) && (this.actualSearchCriteria.trim().length() > 0) && (!this.actualSearchCriteria.trim().contains("%"))) { this.actualSearchCriteria = this.actualSearchCriteria + "%"; } } /** * Actual Search Criteria modified for a Like Condition * * @return {@link java.lang.String} object. */ public String getActualSearchCriteria() { return actualSearchCriteria; } /** * <p>Setter for the field <code>actualSearchCriteria</code>.</p> * * @param actualSearchCriteria the actualSearchCriteria to set */ protected void setActualSearchCriteria(String actualSearchCriteria) { this.actualSearchCriteria = actualSearchCriteria; } /** * buttonClicked * * @return {@link java.lang.String} object. */ public String getButtonClicked() { return buttonClicked; } /** * <p>Setter for the field <code>buttonClicked</code>.</p> * * @param buttonClicked the buttonClicked to set */ public void setButtonClicked(String buttonClicked) { this.buttonClicked = buttonClicked; } /** * rowCount * * @return int. */ public int getRowCount() { return rowCount; } /** * <p>Setter for the field <code>rowCount</code>.</p> * * @param rowCount the rowCount to set */ public void setRowCount(int rowCount) { this.rowCount = rowCount; } /** * rowsPerPage * * @return int. */ public int getRowsPerPage() { return rowsPerPage; } /** * <p>Setter for the field <code>rowsPerPage</code>.</p> * * @param rowsPerPage the rowsPerPage to set */ public void setRowsPerPage(int rowsPerPage) { this.rowsPerPage = rowsPerPage; } /** * pageSegments * * @return {@link java.util.Map} object. */ public Map<Integer, PageSegmentBounds> getPageSegments() { return pageSegments; } /** * <p>Setter for the field <code>pageSegments</code>.</p> * * @param pageSegments the pageSegments to set */ public void setPageSegments(Map<Integer, PageSegmentBounds> pageSegments) { this.pageSegments = pageSegments; } /** * SelectedPageNumber * * @return int. */ public int getSelectedPageNumber() { return selectedPageNumber; } /** * <p>Setter for the field <code>selectedPageNumber</code>.</p> * * @param selectedPageNumber the selectedPageNumber to set */ public void setSelectedPageNumber(int selectedPageNumber) { this.selectedPageNumber = selectedPageNumber; } /** * currentPageNumber * * @return int. */ public int getCurrentPageNumber() { return this.currentPageNumber; } /** * <p>Setter for the field <code>currentPageNumber</code>.</p> * * @param currentPageNumber the currentPageNumber to set */ public void setCurrentPageNumber(int currentPageNumber) { this.currentPageNumber = currentPageNumber; } /** * availableRowsPerPage * * @return an array of int. */ public int[] getAvailableRowsPerPage() { return availableRowsPerPage; } /** * beginningPageRowNumber * * @return int. */ public int getBeginningPageRowNumber() { PageSegmentBounds currentPageSegment = this.pageSegments .get(new Integer(this.getCurrentPageNumber())); if (currentPageSegment != null) { return currentPageSegment.getBeginningRowNumber(); } else { return 0; } } /** * endingPageRowNumber * * @return int. */ public int getEndingPageRowNumber() { PageSegmentBounds currentPageSegment = this.pageSegments .get(new Integer(this.getCurrentPageNumber())); if (currentPageSegment != null) { return currentPageSegment.getEndingRowNumber(); } else { return 0; } } /** * rowsPerPage for Current Page Segment. * * @return int. */ public int getRowsPerCurrentPage() { PageSegmentBounds currentPageSegment = this.pageSegments.get(new Integer(this.getCurrentPageNumber())); if (currentPageSegment == null) { return this.getRowsPerPage(); } return ((currentPageSegment.getEndingRowNumber() - currentPageSegment.getBeginningRowNumber()) + 1); } /** * Provide Number of Pages per our Segment Map. * * @return int. */ public int getNumberOfPages() { return this.pageSegments.size(); } /** * Provide an array of availablePages for direct selection from a drop-down. * * @return an array of int. */ public int[] getAvailablePages() { if (this.pageSegments == null) { return new int[1]; } int[] availablePages = new int[this.pageSegments.size()]; int i = 0; for (Integer key : this.getPageSegments().keySet()) { PageSegmentBounds currentPageSegment = this.pageSegments.get(key); availablePages[i] = currentPageSegment.getPageNumber(); i++; } return availablePages; } /** * Indicator to indicate the Last Page. * * @return boolean */ public boolean isLastPage() { PageSegmentBounds currentPageSegment = this.pageSegments .get(new Integer(this.getCurrentPageNumber())); if ((currentPageSegment != null) && (currentPageSegment.getPageNumber() == this.pageSegments .size())) { return true; } return false; } /** * {@inheritDoc} * <p/> * ToString Override */ @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Pagable Search Criteria:{ Name:[" + this.name + "], Criteria:[" + this.getSearchCriteria() + "], Button Clicked:[" + this.getButtonClicked() + "]"); sb.append(", Row Count:[" + this.rowCount + "], Rows Per Page:[" + this.rowsPerPage + "], Current Page Number:[" + this.getCurrentPageNumber() + "], Selected Page Number:[" + this.getSelectedPageNumber() + "]"); // ************************************* // Show Specifics for this current page PageSegmentBounds currentPageSegment = this.pageSegments .get(new Integer(this.getCurrentPageNumber())); if (currentPageSegment != null) { sb.append(", Beginning Row Number:[" + currentPageSegment.getBeginningRowNumber() + "], Ending Row Number:[" + currentPageSegment.endingRowNumber + "]"); sb.append(", Last Page:[" + this.isLastPage() + "] }"); } // ************************************** // Show All Page Segments we have. sb.append(this.dumpPageSegments()); // ************************************** // Return for Debugging Purposes. return sb.toString(); } /** * Dump the Page Segments * * @return a {@link java.lang.String} object. */ public String dumpPageSegments() { StringBuffer sb = new StringBuffer(); sb.append("\n*** Page Segments:\n"); for (Integer key : this.getPageSegments().keySet()) { sb.append(" " + this.getPageSegments().get(key).toString() + "\n"); } return sb.toString(); } /** * Helper method to generate a map of Page Segments based upon the * current row count and rows per page. */ public void generateSegments() { this.pageSegments = new TreeMap<Integer, PageSegmentBounds>(); if (this.rowCount == 0) { return; } else if (this.rowCount <= this.rowsPerPage) { this.pageSegments.put(new Integer(1), new PageSegmentBounds(1, 0, this.rowCount - 1)); } else if ((this.rowCount > 0) && (this.rowsPerPage > 0)) { double pages = ((double) this.rowCount / (double) this.rowsPerPage); if (pages <= 0) { this.pageSegments.put(new Integer(1), new PageSegmentBounds(1, 0, this.rowCount - 1)); } else { double remainingCount = this.rowCount; double page = 0; double begin = 0; double end = 0; for (double p = 0; (p + 1) < pages; p++) { page = p + 1; begin = p * this.rowsPerPage; end = (begin + this.rowsPerPage - 1); remainingCount = remainingCount - this.rowsPerPage; this.pageSegments.put(new Integer((int) page), new PageSegmentBounds(page, begin, end)); } // End of For Loop. // ***************************** // Place Last Page. this.pageSegments.put(new Integer((int) page + 1), new PageSegmentBounds(page + 1, ((end > 0) ? end + 1 : 0), (end + remainingCount))); } } else if (this.rowCount > 0) { this.pageSegments.put(new Integer(1), new PageSegmentBounds(1, 0, this.rowCount - 1)); } } /** * Page based upon Specified Direction * * @param direction a {@link java.lang.Integer} object. */ public void page(Integer direction) { if (log.isTraceEnabled()) { log.trace("Paging: Direction:[" + direction + "]"); log.trace("Prior Paging Operation:[" + this.toString() + "]"); } switch (direction.intValue()) { case 0: // ************** // Do not Page. break; case 1: // *************************** // Page Backward this.setCurrentPageNumber(this.getCurrentPageNumber() - 1); break; case 2: // *************************** // Page Forward this.setCurrentPageNumber(this.getCurrentPageNumber() + 1); break; case 3: // ************************** // Page GoTo this.setCurrentPageNumber(this.getSelectedPageNumber()); break; default: // *************************** // Page Forward this.setCurrentPageNumber(this.getCurrentPageNumber() + 1); break; } // End of Switch statement // ********************************************** // Ensure Selected Page in Sync with new Current this.setSelectedPageNumber(this.getCurrentPageNumber()); if (log.isTraceEnabled()) { log.trace("After Paging Operation:[" + this.toString() + "]"); } } /** * Page based upon Specified Direction */ public void page() { // **************************************** // Determine what Button was pressed. if ((this.buttonClicked == null) || (this.buttonClicked.trim().length() <= 0)) { page(PagableSearchCriteria.DIRECTION_NONE); } else if (this.buttonClicked.equalsIgnoreCase(SEARCH)) { // ******************************* // Handle New search Request this.setSelectedPageNumber(1); page(PagableSearchCriteria.DIRECTION_GOTOPAGE); } else if (this.buttonClicked.equalsIgnoreCase(GOTOPAGE)) { // ******************************* // Handle Go To Specific Page page(PagableSearchCriteria.DIRECTION_GOTOPAGE); } else if (this.buttonClicked.equalsIgnoreCase(PREVIOUS)) { // ******************************* // Previous Page. page(PagableSearchCriteria.DIRECTION_BACKWARD); } else { // ******************************* // Assume Next Page. page(PagableSearchCriteria.DIRECTION_FORWARD); } } /** * Inner Class to provide Page Segment Bounds * * @author jeffaschenk@gmail.com */ public class PageSegmentBounds { /** * Page Number Relative to One. */ private int pageNumber = 0; /** * Beginning Page Row Number */ private int beginningRowNumber = 0; /** * Ending Page Row Number */ private int endingRowNumber = 0; /** * @param pageNumber * @param beginningRowNumber * @param endingRowNumber */ public PageSegmentBounds(int pageNumber, int beginningRowNumber, int endingRowNumber) { super(); this.pageNumber = pageNumber; this.beginningRowNumber = beginningRowNumber; this.endingRowNumber = endingRowNumber; } /** * @param pageNumber * @param beginningRowNumber * @param endingRowNumber */ public PageSegmentBounds(double pageNumber, double beginningRowNumber, double endingRowNumber) { super(); this.pageNumber = (int) pageNumber; this.beginningRowNumber = (int) beginningRowNumber; this.endingRowNumber = (int) endingRowNumber; } /** * pageNumber for this segment */ public int getPageNumber() { return pageNumber; } /** * @param pageNumber the pageNumber to set */ public void setPageNumber(int pageNumber) { this.pageNumber = pageNumber; } /** * beginningRowNumber for this segment */ public int getBeginningRowNumber() { return beginningRowNumber; } /** * @param beginningRowNumber the beginningRowNumber to set */ public void setBeginningRowNumber(int beginningRowNumber) { this.beginningRowNumber = beginningRowNumber; } /** * endingRowNumber for this segment */ public int getEndingRowNumber() { return endingRowNumber; } /** * @param endingRowNumber the endingRowNumber to set */ public void setEndingRowNumber(int endingRowNumber) { this.endingRowNumber = endingRowNumber; } @Override public String toString() { return "Segment Page Number:[" + this.pageNumber + "], " + "Begin Row:[" + this.beginningRowNumber + "], End Row:[" + this.endingRowNumber + "]"; } } // End of Inner Class /** * Main Simple Test For Paging Segments * * @param args an array of {@link java.lang.String} objects. */ public static void main(String[] args) { if (args.length < 2) { System.out.println("Usage: <row count> <rows per page>"); System.exit(0); } for (int i = 0; i < args.length; i = i + 2) { int rowcount = Integer.parseInt(args[i]); int rowsPerPage = Integer.parseInt(args[i + 1]); // ******************************** // Initialize PagableSearchCriteria psc = new PagableSearchCriteria(); psc.setRowsPerPage(rowsPerPage); psc.setRowCount(rowcount); psc.generateSegments(); System.out.println(psc); } System.out.println("Done"); System.exit(0); } }