All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.velocity.tools.view.PagerTool Maven / Gradle / Ivy

The newest version!
package org.apache.velocity.tools.view;

/*
 * 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.
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.velocity.tools.Scope;
import org.apache.velocity.tools.config.DefaultKey;
import org.apache.velocity.tools.config.InvalidScope;
import org.apache.velocity.tools.generic.SafeConfig;

/**
 * 

View tool for doing request-based pagination of * items in an a list. *

*

Usage:
* To use this class, you typically push a List of items to it * by putting it in the request attributes under the value returned by * {@link #getNewItemsKey()} (default is "new.items"). * You can also set the list of items to be paged in a subclass * using the setItems(List) method, or you can always set the * item list at another point (even from within the template). This * need only happen once per session if a session is available, but the * item list can be (re)set as often as you like. *

*

* Here's an example of how your subclass would be used in a template: *

 *   #if( $pager.hasItems() )
 *   Showing $!pager.pageDescription<br>
 *     #set( $i = $pager.index )
 *     #foreach( $item in $pager.page )
 *       ${i}. $!item <br>
 *       #set( $i = $i + 1 )
 *     #end
 *     <br>
 *     #if ( $pager.pagesAvailable > 1 )
 *       #set( $pagelink = $link.self.param("show",$!pager.itemsPerPage) )
 *       #if( $pager.prevIndex )
 *           <a href="$pagelink.param('index',$!pager.prevIndex)">Prev</a>
 *       #end
 *       #foreach( $index in $pager.slip )
 *         #if( $index == $pager.index )
 *           <b>$pager.pageNumber</b>
 *         #else
 *           <a href="$pagelink.param('index',$!index)">$!pager.getPageNumber($index)</a>
 *         #end
 *       #end
 *       #if( $pager.nextIndex )
 *           <a href="$pagelink.param('index',$!pager.nextIndex)">Next</a>
 *       #end
 *     #end
 *   #else
 *   No items in list.
 *   #end
 * 
* *

The output of this might look like:

*
 *   Showing 1-5 of 8<br>
 *   1. foo<br>
 *   2. bar<br>
 *   3. blah<br>
 *   4. woogie<br>
 *   5. baz<br><br>
 *   <b>1</b> <a href="">2</a> <a href="">Next</a>
 * 
*

Example tools.xml configuration:

*
 * <tools>
 *   <toolbox scope="request">
 *     <tool class="org.apache.velocity.tools.view.PagerTool"/>
 *   </toolbox>
 * </tools>
 * 
* * @author Nathan Bubna * @version $Revision$ $Date$ * @since VelocityTools 2.0 */ @DefaultKey("pager") @InvalidScope({Scope.APPLICATION,Scope.SESSION}) public class PagerTool extends SafeConfig { public static final String DEFAULT_NEW_ITEMS_KEY = "new.items"; public static final String DEFAULT_INDEX_KEY = "index"; public static final String DEFAULT_ITEMS_PER_PAGE_KEY = "show"; public static final String DEFAULT_SLIP_SIZE_KEY = "slipSize"; /** the default number of items shown per page */ public static final int DEFAULT_ITEMS_PER_PAGE = 10; /** the default max number of page indices to list */ public static final int DEFAULT_SLIP_SIZE = 20; /** the key under which items are stored in session */ protected static final String STORED_ITEMS_KEY = PagerTool.class.getName(); private String newItemsKey = DEFAULT_NEW_ITEMS_KEY; private String indexKey = DEFAULT_INDEX_KEY; private String itemsPerPageKey = DEFAULT_ITEMS_PER_PAGE_KEY; private String slipSizeKey = DEFAULT_SLIP_SIZE_KEY; private boolean createSession = false; private List items; private int index = 0; private int slipSize = DEFAULT_SLIP_SIZE; private int itemsPerPage = DEFAULT_ITEMS_PER_PAGE; protected HttpSession session; /** * Initializes this tool with the specified {@link HttpServletRequest}. * This is required for this tool to operate and will throw a * NullPointerException if this is not set or is set to {@code null}. * @param request servlet request */ public void setRequest(HttpServletRequest request) { if (request == null) { throw new NullPointerException("request should not be null"); } this.session = request.getSession(getCreateSession()); setup(request); } /** * Sets the index, itemsPerPage, and/or slipSize *if* they are set * in the request parameters. Likewise, this will set the item list * to be paged *if* there is a list pushed into the request attributes * under the {@link #getNewItemsKey()}. * * @param request the current HttpServletRequest */ public void setup(HttpServletRequest request) { ParameterTool params = new ParameterTool(request); // only change these settings if they're present in the params int index = params.getInt(getIndexKey(), -1); if (index >= 0) { setIndex(index); } int show = params.getInt(getItemsPerPageKey(), 0); if (show > 0) { setItemsPerPage(show); } int slipSize = params.getInt(getSlipSizeKey(), 0); if (slipSize > 0) { setSlipSize(slipSize); } // look for items in the request attributes List newItems = (List)request.getAttribute(getNewItemsKey()); if (newItems != null) { // only set the items if a list was pushed into the request setItems(newItems); } } public void setNewItemsKey(String key) { this.newItemsKey = key; } public String getNewItemsKey() { return this.newItemsKey; } public void setIndexKey(String key) { this.indexKey = key; } public String getIndexKey() { return this.indexKey; } public void setItemsPerPageKey(String key) { this.itemsPerPageKey = key; } public String getItemsPerPageKey() { return this.itemsPerPageKey; } public void setSlipSizeKey(String key) { this.slipSizeKey = key; } public String getSlipSizeKey() { return this.slipSizeKey; } public void setCreateSession(boolean createSession) { this.createSession = createSession; } public boolean getCreateSession() { return this.createSession; } /** * Sets the item list to null, page index to zero, and * items per page to the default. */ public void reset() { items = null; index = 0; itemsPerPage = DEFAULT_ITEMS_PER_PAGE; } /** * Sets the List to page through. * * @param items - the {@link List} of items to be paged through */ public void setItems(List items) { this.items = items; setStoredItems(items); } /** * Sets the index of the first result in the current page * * @param index the result index to start the current page with */ public void setIndex(int index) { if (index < 0) { /* quietly override to a reasonable value */ index = 0; } this.index = index; } /** * Sets the number of items returned in a page of items * * @param itemsPerPage the number of items to be returned per page */ public void setItemsPerPage(int itemsPerPage) { if (itemsPerPage < 1) { /* quietly override to a reasonable value */ itemsPerPage = DEFAULT_ITEMS_PER_PAGE; } this.itemsPerPage = itemsPerPage; } /** * Sets the number of result page indices for {@link #getSlip} to list. * (for google-ish result page links). * * @see #getSlip * @param slipSize - the number of result page indices to list */ public void setSlipSize(int slipSize) { if (slipSize < 2) { /* quietly override to a reasonable value */ slipSize = DEFAULT_SLIP_SIZE; } this.slipSize = slipSize; } /* ---------------------- accessors ----------------------------- */ /** * Returns the set number of items to be displayed per page of items * * @return current number of items shown per page */ public int getItemsPerPage() { return itemsPerPage; } /** * Returns the number of result page indices {@link #getSlip} * will return per request (if available). * * @return the number of result page indices {@link #getSlip} * will try to return */ public int getSlipSize() { return slipSize; } /** * Returns the current search result index. * * @return the index for the beginning of the current page */ public int getIndex() { return index; } /** * Checks whether or not the result list is empty. * * @return true if the result list is not empty. */ public boolean hasItems() { return !getItems().isEmpty(); } /** * Returns the item list. This is guaranteed * to never return null. * * @return {@link List} of all the items */ public List getItems() { if (items == null) { items = getStoredItems(); } return (items != null) ? items : Collections.EMPTY_LIST; } /** * Returns the index of the last item on the current page of results * (as determined by the current index, items per page, and * the number of items). If there is no current page, then null is * returned. * * @return index for the last item on this page or null * if none exists * @since VelocityTools 1.3 */ public Integer getLastIndex() { if (!hasItems()) { return null; } return Integer.valueOf(Math.min(getTotal() - 1, index + itemsPerPage - 1)); } /** * Returns the index for the next page of items * (as determined by the current index, items per page, and * the number of items). If no "next page" exists, then null is * returned. * * @return index for the next page or null if none exists */ public Integer getNextIndex() { int next = index + itemsPerPage; if (next < getTotal()) { return Integer.valueOf(next); } return null; } /** * Returns the index of the first item on the current page of results * (as determined by the current index, items per page, and * the number of items). If there is no current page, then null is * returned. This is different than {@link #getIndex()} in that it * is adjusted to fit the reality of the items available and is not a * mere accessor for the current, user-set index value. * * @return index for the first item on this page or null * if none exists * @since VelocityTools 1.3 */ public Integer getFirstIndex() { if (!hasItems()) { return null; } return Integer.valueOf(Math.min(getTotal() - 1, index)); } /** * Return the index for the previous page of items * (as determined by the current index, items per page, and * the number of items). If no "next page" exists, then null is * returned. * * @return index for the previous page or null if none exists */ public Integer getPrevIndex() { int prev = Math.min(index, getTotal()) - itemsPerPage; if (index > 0) { return Integer.valueOf(Math.max(0, prev)); } return null; } /** * Returns the number of pages that can be made from this list * given the set number of items per page. * @return number of available pages */ public int getPagesAvailable() { return (int)Math.ceil(getTotal() / (double)itemsPerPage); } /** * Returns the current "page" of search items. * * @return a {@link List} of items for the "current page" */ public List getPage() { /* return null if we have no items */ if (!hasItems()) { return null; } /* quietly keep the page indices to legal values for robustness' sake */ int start = getFirstIndex().intValue(); int end = getLastIndex().intValue() + 1; return getItems().subList(start, end); } /** * Returns the "page number" for the specified index. Because the page * number is used for the user interface, the page numbers are 1-based. * * @param i the index that you want the page number for * @return the approximate "page number" for the specified index or * null if there are no items */ public Integer getPageNumber(int i) { if (!hasItems()) { return null; } return Integer.valueOf(1 + i / itemsPerPage); } /** * Returns the "page number" for the current index. Because the page * number is used for the user interface, the page numbers are 1-based. * * @return the approximate "page number" for the current index or * null if there are no items */ public Integer getPageNumber() { return getPageNumber(index); } /** * Returns the total number of items available. * @return number of items * @since VelocityTools 1.3 */ public int getTotal() { if (!hasItems()) { return 0; } return getItems().size(); } /** *

Returns a description of the current page. This implementation * displays a 1-based range of result indices and the total number * of items. (e.g. "1 - 10 of 42" or "7 of 7") If there are no items, * this will return "0 of 0".

* *

Sub-classes may override this to provide a customized * description (such as one in another language).

* * @return a description of the current page */ public String getPageDescription() { if (!hasItems()) { return "0 of 0"; } StringBuilder out = new StringBuilder(); int first = getFirstIndex().intValue() + 1; int total = getTotal(); if (first >= total) { out.append(total); out.append(" of "); out.append(total); } else { int last = getLastIndex().intValue() + 1; out.append(first); out.append(" - "); out.append(last); out.append(" of "); out.append(total); } return out.toString(); } /** * Returns a Sliding List of Indices for Pages * of items. * *

Essentially, this returns a list of item indices that correspond * to available pages of items (as based on the set items-per-page). * This makes it relativly easy to do a google-ish set of links to * available pages.

* *

Note that this list of Integers is 0-based to correspond with the * underlying result indices and not the displayed page numbers (see * {@link #getPageNumber}).

* * @return {@link List} of Integers representing the indices of result * pages or empty list if there's one or less pages available */ public List getSlip() { /* return an empty list if there's no pages to list */ int totalPgs = getPagesAvailable(); if (totalPgs <= 1) { return Collections.EMPTY_LIST; } /* page number is 1-based so decrement it */ int curPg = getPageNumber().intValue() - 1; /* start at zero or just under half of max slip size * this keeps "forward" and "back" pages about even * but gives preference to "forward" pages */ int slipStart = Math.max(0, (curPg - (slipSize / 2))); /* push slip end as far as possible */ int slipEnd = Math.min(totalPgs, (slipStart + slipSize)); /* if we're out of "forward" pages, then push the * slip start toward zero to maintain slip size */ if (slipEnd - slipStart < slipSize) { slipStart = Math.max(0, slipEnd - slipSize); } /* convert 0-based page numbers to indices and create list */ List slip = new ArrayList(slipEnd - slipStart); for (int i=slipStart; i < slipEnd; i++) { slip.add(Integer.valueOf(i * itemsPerPage)); } return slip; } /* ---------------------- protected methods ------------------------ */ /** * Retrieves stored search items (if any) from the user's * session attributes. * * @return the {@link List} retrieved from memory */ protected List getStoredItems() { if (session != null) { return (List)session.getAttribute(STORED_ITEMS_KEY); } return null; } /** * Stores current search items in the user's session attributes * (if one currently exists) in order to do efficient result pagination. * *

Override this to store search items somewhere besides the * HttpSession or to prevent storage of items across requests. In * the former situation, you must also override getStoredItems().

* * @param items the {@link List} to be stored */ protected void setStoredItems(List items) { if (session != null) { session.setAttribute(STORED_ITEMS_KEY, items); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy