Chitika

Wednesday, March 30, 2005

List Pagination (Value List Holder)

Someone at our JUG Indonesia has a problem with displaying a List in a number of pages (aka List Pagination). This problem can be solved using the Value List Holder design pattern. I'm attaching my solution to the problem for him (or anyone who may found this useful), so that it'd be easier for him to have a look and discuss.

This solution can be used for both web application and non-web application.
Anyway, just put a comment if you have one.. :D

You can download the zipped source code (plus the test class) here.

import java.util.Iterator;
import java.util.List;

public class Page {

  private int pageNum;
  private int totalPage;
  private int pagesize;
  private List contents;

  public Page (final int pageNum,
      final int totalPage,
      final int pagesize,
      final List contents) {
    this.pageNum = pageNum;
    this.totalPage = totalPage;
    this.pagesize = pagesize;
    this.contents = contents;
  }

  public int getPageNum() {
    return pageNum;
  }

  public int getTotalPage() {
    return totalPage;
  }

  public int getPagesize() {
    return pagesize;
  }

  public List getContents() {
    return contents;
  }

  public boolean isFirstPage () {
    return pageNum == 1;
  }

  public boolean isLastPage () {
    return pageNum == totalPage;
  }

  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Page)) return false;

    final Page page = (Page) o;

    if (pageNum != page.pageNum) return false;
    if (pagesize != page.pagesize) return false;
    if (totalPage != page.totalPage) return false;
    if (contents != null ?
      !isListEqual (contents, page.contents)
      : page.contents != null)
        return false;

    return true;
  }

  public int hashCode() {
    int result;
    result = pageNum;
    result = 29 * result + totalPage;
    result = 29 * result + pagesize;
    result = 29 * result + (contents != null ?
      listHashCode (contents) : 0);
    return result;
  }

  private boolean isListEqual (
    final List a, final List b) {
    if (a == b || a.equals(b)) return true;

    final Iterator ia = a.iterator ();
    final Iterator ib = b.iterator ();
    while (ia.hasNext() && ib.hasNext()) {
      final Object oa = ia.next();
      final Object ob = ib.next();
      if (!oa.equals(ob)) {
        return false;
      }
    }
    if (ia.hasNext() || ib.hasNext()) {
      return false;
    }
    return true;
  }

  private int listHashCode (final List a) {
    int result = 0;
    for (Iterator iterator = a.iterator();
      iterator.hasNext();) {
      final Object o = iterator.next();
      result = 29 * result + o.hashCode();
    }
    return result;
    }

  public String toString () {
    final StringBuffer sb = new StringBuffer ();
    sb.append ("Page ").append (pageNum)
      .append (" of ").append (totalPage);
    sb.append ("\n");

    for (Iterator it = contents.iterator();
      it.hasNext();) {
      final Object o = it.next();
      sb.append (o).append ("\n");
    }
    return sb.toString ();
  }
}


public interface Paginating {

  Page getFirstPage ();

  Page getLastPage ();

  Page getNextPage (Page currentPage);

  Page getPrevPage (Page currentPage);
}


import java.util.List;

public class PaginatingImpl implements Paginating {

  private List originalList;
  private int pagesize;
  private static final String INVALID_PAGESIZE =
    "Pagesize must be a positive integer.";

  public PaginatingImpl (final List originalList,
    final int pagesize)
  throws IllegalArgumentException {
    if (pagesize <= 0)
      throw new IllegalArgumentException (INVALID_PAGESIZE);
    this.originalList = originalList;
    this.pagesize = pagesize;
  }

  public Page getFirstPage () {
    Page result = null;
    if (originalList != null && originalList.size () > 0) {
      result = new Page (1, getTotalPage(), pagesize,
        iterateFrom (0));
      }
    return result;
  }

  public Page getLastPage () {
    Page result = null;
    if (originalList != null && originalList.size() > 0) {
      final int totalPage = getTotalPage();
      final int startIndex = (totalPage - 1) * pagesize;
      result = new Page (totalPage, totalPage, pagesize,
        iterateFrom (startIndex));
    }
    return result;
  }

  public Page getNextPage (final Page currentPage) {
    if (currentPage == null) return getFirstPage ();
    if (currentPage.isLastPage()) return currentPage;

    Page result = null;
    if (originalList != null) {
      result = new Page (currentPage.getPageNum() + 1,
        currentPage.getTotalPage(),
        pagesize,
        iterateFrom (currentPage.getPageNum() * pagesize));
    }
    return result;
  }

  public Page getPrevPage (final Page currentPage) {
    if (currentPage == null) return getFirstPage ();
    if (currentPage.isFirstPage()) return currentPage;

    Page result = null;
    if (originalList != null) {
      result = new Page (currentPage.getPageNum() - 1,
         currentPage.getTotalPage(),
           pagesize,
        iterateFrom ((currentPage.getPageNum() - 2) *
          pagesize));
    }
    return result;
  }

  private List iterateFrom (final int startIndex) {
    final int totalSize = originalList.size ();

    int endIndex = startIndex + pagesize;
    if (endIndex > totalSize) endIndex = totalSize;

    return originalList.subList (startIndex, endIndex);
  }

  private int getTotalPage () {
    if (originalList == null || originalList.size() <= 0)
      return 0;
    final int totalSize = originalList.size();
    return ((totalSize - 1) / pagesize) + 1;
  }
}


Further Reading:
Head First Design Patterns
Design Patterns
JUnit in Action
JUnit Recipes

7 comments:

  1. Wah mau coding pagination aja panjang amat yak.

    ReplyDelete
  2. To Josh (defkewl),

    Please check out my explanation and let me know what you think. Thanks for the comment anyway.

    ReplyDelete
  3. please can u send me the zip to ibm_4u@yahoo.com not able to access the zip

    ReplyDelete
  4. Hi..Thanks a lot for your excellent work..it seems very good..can you please send the code to umasankarjavainfo@gmail.com code, the link what you gave is not working..thanks a lot for your help in advance

    ReplyDelete
  5. I am unable to access the zip code..can you please send the zip to umasankarjavainfo@gmail.com.
    Thanks a lot for your help in advance.

    ReplyDelete
  6. please can u send me the zip to sujit_pradhan@persistent.co.in since I am not able to access the PaginatingList.zip.

    ReplyDelete