Example 17-8: Custom XSQL action handler simplifies stateless paging

import oracle.xml.xsql.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.*;
import java.net.*;
import java.util.StringTokenizer;
import java.sql.SQLException;

public class Paging extends XSQLActionHandlerImpl {
  private static final String PAGE_PARAM_NAME = "p";
  private static final String ROWSPERPAGE     = "rows-per-page";
  private static final String TARGETPAGEARGS  = "url-params";

  public void handleAction(Node root) throws SQLException {
    XSQLPageRequest req = getPageRequest();
    Element actElt  = getActionElement();
    // Get the count query from the action element content
    String query    = getActionElementContent();
    // Get the number of rows per page, defaulting to 10
    long pageSize   = longVal(getAttributeAllowingParam(ROWSPERPAGE,actElt),10);
    long totalRows  = longVal(firstColumnOfFirstRow(root,query),0);
    long curPage = longVal(variableValue(PAGE_PARAM_NAME,actElt),1);
    // Get the name of the current page to use as the target
    String pageName = curPageName(req);
    // Get any URL parameter names that need to be echoed into paging URL's
    String pageArgs = getAttributeAllowingParam(TARGETPAGEARGS,actElt);
    // Calculate the total number of pages
    long totalPages = totalRows / pageSize;
    long fract = totalRows % pageSize;
    if (fract > 0) totalPages++;
    // Make sure current page is between 1 < cur < totalPages
    if (curPage < 1) curPage = 1;if (curPage > totalPages) curPage = totalPages;
    // Create the <paging> fragment to add to the "data page"
    Document d = actElt.getOwnerDocument();
    Element e = d.createElement("paging");
    root.appendChild(e);
      addResultElement(e,"total-rows",Long.toString(totalRows));
      addResultElement(e,"total-pages",Long.toString(totalPages));
      addResultElement(e,"current-page",Long.toString(curPage));
      if (curPage < totalPages)
        addResultElement(e,"next-page",Long.toString(curPage+1));
      if (curPage > 1)
        addResultElement(e,"prev-page",Long.toString(curPage-1));
      addResultElement(e,"target-page",pageName);
      if (pageArgs != null && !pageArgs.equals(""))
        addResultElement(e,"target-args",expandedUrlParams(pageArgs,actElt));
    // Set to page-level parameters that the <xsql:query> can use
    req.setPageParam("paging-skip",Long.toString((curPage-1)*pageSize));
    req.setPageParam("paging-max",Long.toString(pageSize));
  }
  // Get the name of the current page from the current page's URI
  private String curPageName(XSQLPageRequest req) {
    String thisPage = req.getSourceDocumentURI();;
    int pos = thisPage.lastIndexOf('/');
    if (pos >=0) thisPage = thisPage.substring(pos+1);
    pos = thisPage.indexOf('?');
    if (pos >=0) thisPage = thisPage.substring(0,pos-1);
    return thisPage;
  }
  // Convert String to long with a default in case blank, null, or not a number
  private long longVal(String longstr, long def) {
    long val = def;
    try {
      if (longstr != null && !longstr.equals(""))
        val = Long.parseLong(longstr);
    }
    catch (NumberFormatException nfe) {}
    return val;
  }
  private String expandedUrlParams(String paramNameList,Element actElt) {
    // Allow comma-separate or space-separated name list
    paramNameList = paramNameList.replace(',',' ');
    StringTokenizer st = new StringTokenizer(paramNameList);
    StringBuffer paramString = new StringBuffer();
    int tokens = 0;
    while (st.hasMoreTokens()) {
      String paramName = st.nextToken();
      String paramValue = variableValue(paramName,actElt);
      if (++tokens > 1) {
        paramString.append("&");
      }
      paramString.append(paramName)
                 .append("=")
                 .append(paramValue);
    }
    return paramString.toString();
  }
}