Considered the reason you gave in the comment to your question I suggest you to implement your own custom renderer for the pager.
In the faces-config.xml
declare as follows:
<render-kit>
<renderer>
<component-family>com.ibm.xsp.Pager
</component-family>
<renderer-type>com.ibm.xsp.XPager
</renderer-type>
<renderer-class>com.irc.xsp.renderer.UIPagerRenderer
</renderer-class>
</renderer>
</render-kit>
Where com.irc.xsp.renderer.UIPagerRenderer
is the rendering class. Obviously you can name it whatever you want. Just use the same name in both places.
The renderer could look something like this:
package com.irc.xsp.renderer;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.component.UIPager;
import com.ibm.xsp.component.UIPagerControl;
import com.ibm.xsp.component.xp.XspPager;
import com.ibm.xsp.component.xp.XspPagerControl;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.event.PagerEvent;
import com.ibm.xsp.extsn.ResourceHandler;
import com.ibm.xsp.renderkit.html_extended.XPagerRenderer;
import com.ibm.xsp.util.AjaxUtilEx;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.JavaScriptUtil;
import com.ibm.xsp.util.TypedUtil;
public class UIPagerRenderer extends XPagerRenderer {
public static final String VAR_PAGE = "page"; //$NON-NLS-1$
private enum Property {
LIST_ITEM_CLASS, PAGER_LINK_CLASS, ACTIVE_CLASS("active"), DISABLED_CLASS("disabled");
String className;
Property() {
}
Property(String s) {
this.className = s;
}
String getClassName() {
return className;
}
}
@Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
// check that this component cause the submit
if (decodeCausedSubmit(context, component)) {
PagerEvent pagerEvent = new PagerEvent(component);
String hiddenValue = FacesUtil.getHiddenFieldValue(context);
if (StringUtil.isNotEmpty(hiddenValue)) {
int pos = hiddenValue.lastIndexOf('_');
if (pos > -1) {
hiddenValue = hiddenValue.substring(pos + 1);
if (isFirst(hiddenValue)) {
pagerEvent.setAction(PagerEvent.ACTION_FIRST);
} else if (isLast(hiddenValue)) {
pagerEvent.setAction(PagerEvent.ACTION_LAST);
} else if (isNext(hiddenValue)) {
pagerEvent.setAction(PagerEvent.ACTION_NEXT);
} else if (isPrevious(hiddenValue)) {
pagerEvent.setAction(PagerEvent.ACTION_PREVIOUS);
} else {
try {
int value = Integer.parseInt(hiddenValue);
pagerEvent.setAction(PagerEvent.ACTION_GOTOPAGE);
pagerEvent.setPage(value);
} catch (NumberFormatException nfe) {
return; // just don't queue the event
}
}
} else {
return;
}
}
((UIPager) component).queueEvent(pagerEvent);
}
}
private boolean decodeCausedSubmit(FacesContext context, UIComponent component) {
String currentClientId = component.getClientId(context);
String hiddenValue = FacesUtil.getHiddenFieldValue(context);
if (currentClientId != null && hiddenValue != null) {
return StringUtil.indexOfIgnoreCase(hiddenValue, currentClientId) > -1;
}
return false;
}
@Override
public boolean getRendersChildren() {
return true;
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
if (context == null || component == null) {
throw new IOException();
}
List<?> controls = component.getChildren();
if (controls.isEmpty()) {
return;
}
XspPager pager = (XspPager) component;
UIPager.PagerState pagerState = ((UIPager) component).createPagerState();
if (pagerState == null) {
throw new FacesExceptionEx(ResourceHandler.getString("PagerRenderer.Pagerisnotassociatedwithanydataco"));
}
encodePagerContent(context, context.getResponseWriter(), pager, pagerState, controls, false);
}
protected void encodePagerContent(FacesContext context, ResponseWriter writer, XspPager pager,
UIPager.PagerState pagerState, List<?> controls, boolean rtl) throws IOException {
// Compute the pages that should be displayed
int pageCount = pagerState.getPageCount();
int startCount = getStartCount(pagerState, pageCount);
int endCount = getEndCount(pagerState, pageCount, startCount);
String pagerId = pager.getClientId(context);
String pagerRole = pager.getRole();
String pagerTitle = pager.getTitle();
String pagerOuterClass = pager.getOuterStyleClass();
String pagerAriaLabel = pager.getAriaLabel();
boolean closeOuterTag = false;
if (isAnyAssigned(pagerRole, pagerTitle, pagerOuterClass, pagerAriaLabel)) {
writer.startElement("div", null); // $NON-NLS-1$
writeNonNullAttributeOnly(writer, "role", pagerRole);
writeNonNullAttributeOnly(writer, "title", pagerTitle);
writeNonNullAttributeOnly(writer, "class", pagerOuterClass);
writeNonNullAttributeOnly(writer, "aria-label", pagerAriaLabel);
closeOuterTag = true;
}
writer.startElement("ul", null);
writeNonNullAttributeOnly(writer, "class", pager.getStyleClass());
writeNonNullAttributeOnly(writer, "id", pagerId);
Iterator<?> it = controls.iterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj instanceof XspPagerControl) {
XspPagerControl control = (XspPagerControl) obj;
String type = control.getType();
if (StringUtil.isNotEmpty(type)) {
if (isFirst(type) || isNext(type) || isPrevious(type)
|| (isLast(type) && pager.isAlwaysCalculateLast())) {
encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount, endCount,
rtl);
continue;
} else if (isLast(type) && !pager.isAlwaysCalculateLast()) {
if (!pagerState.hasMoreRows()) {
encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount,
endCount, rtl);
} else {
writer.startElement("li", null);
writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS
.getClassName(), Property.DISABLED_CLASS.getClassName()));
writer.startElement("a", null);
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
writer.writeText(getMayBeMorePages(), null);
writer.endElement("a");
writer.endElement("li");
}
continue;
} else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GROUP)) {
encodeGroup(context, writer, pager, pagerState, pagerId, control, startCount, endCount);
continue;
} else if (type.equalsIgnoreCase(UIPagerControl.TYPE_STATUS)) {
encodeStatus(context, writer, pager, pagerState, control, startCount, endCount);
continue;
} else if (isSeparator(type)) {
encodeSeparator(context, writer, control, type);
continue;
} else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GOTO)) {
encodeGoto();
continue;
}
}
// "Unknown control type {0}"
String msg = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Unknowncontroltype0"); //$NON-NLS-1$
msg = StringUtil.format(msg, type);
throw new FacesExceptionEx(msg);
}
}
writer.endElement("ul");
if (closeOuterTag) {
writer.endElement("div");
}
}
protected void encodeAction(FacesContext context, ResponseWriter writer, XspPager pager,
UIPager.PagerState pagerState, String pagerId, XspPagerControl control, String type, int startCount,
int endCount, boolean rtl) throws IOException {
String controlId = pagerId + "__" + type;
String defaultText = "";
String ariaLabel = "";
boolean renderLink = true;
//TODO need to handle BIDI here for the unicode symbols
if (isFirst(type)) {
renderLink = pagerState.getCurrentPage() > startCount;
// "\u00AB" FirstSymbol
defaultText = "\u00AB"; //$NON-NLS-1$
// "First page"
ariaLabel = ResourceHandler.getString("PagerRenderer.First"); //$NON-NLS-1$
} else if (isPrevious(type)) {
renderLink = pagerState.getCurrentPage() > startCount;
// "\u2039" PreviousSymbol
defaultText = "\u2039"; //$NON-NLS-1$
// "Previous page"
ariaLabel = ResourceHandler.getString("PagerRenderer.Previous"); //$NON-NLS-1$
} else if (isNext(type)) {
renderLink = pagerState.getCurrentPage() < endCount - 1;
// "\u203A" NextSymbol
defaultText = "\u203A"; //$NON-NLS-1$;
// "Next page"
ariaLabel = ResourceHandler.getString("PagerRenderer.Next"); //$NON-NLS-1$
} else if (isLast(type)) {
renderLink = pagerState.getCurrentPage() < endCount - 1;
// "\u00BB" LastSymbol
defaultText = "\u00BB"; //$NON-NLS-1$
// "Last page"
ariaLabel = ResourceHandler.getString("PagerRenderer.Last"); //$NON-NLS-1$
}
writer.startElement("li", null);
String listItemClass = Property.LIST_ITEM_CLASS.getClassName();
if (!renderLink) {
//If current page is the first, disable first/previous pagers
//and if current page is the last, disable last/next pagers
listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.DISABLED_CLASS.getClassName());
}
writeNonNullAttributeOnly(writer, "class", listItemClass);
// Generate the image link
String val = (String) control.getValue();
if (StringUtil.isEmpty(val)) {
val = defaultText;
}
// Generate the text link
if (StringUtil.isNotEmpty(val)) {
writer.startElement("a", null); // $NON-NLS-1$
if (renderLink) {
writer.writeAttribute("aria-disabled", "false", null); // $NON-NLS-1$ $NON-NLS-2$
writer.writeAttribute("href", "#", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
//add a11y attributes
writer.writeAttribute("aria-disabled", "true", null); // $NON-NLS-1$ $NON-NLS-2$
}
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
writer.writeAttribute("id", controlId + "__lnk", null); // $NON-NLS-1$ $NON-NLS-2$
writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$
writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
writer.writeText(val, null);
writer.endElement("a"); // $NON-NLS-1$
if (renderLink) {
setupSubmitOnClick(context, pager, pagerState, controlId, controlId + "__lnk"); // $NON-NLS-1$
}
}
writer.endElement("li"); // $NON-NLS-1$
}
protected void encodeGroup(FacesContext context, ResponseWriter writer, XspPager pager,
UIPager.PagerState pagerState, String pagerId, XspPagerControl control, int startCount, int endCount)
throws IOException {
// Save the old page value
Map<String, Object> requestMap = TypedUtil.getRequestMap(context.getExternalContext());
Object oldPage = requestMap.get(VAR_PAGE);
String controlId = pagerId + "__" + control.getType();//$NON-NLS-1$
// Encode the pages
for (int i = startCount; i < endCount; i++) {
// Push the page number
requestMap.put(VAR_PAGE, i + 1);
boolean renderLink = (i != pagerState.getCurrentPage());
writer.startElement("li", null); // $NON-NLS-1$
String listItemClass = Property.LIST_ITEM_CLASS.getClassName();
if (!renderLink) {
listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.ACTIVE_CLASS.getClassName());
}
writeNonNullAttributeOnly(writer, "class", listItemClass);
// Generate the image link
String val = (String) control.getValue();
if (StringUtil.isEmpty(val)) {
val = Integer.toString(i + 1);
}
// Generate the text link
if (StringUtil.isNotEmpty(val)) { // Generate the text link
writer.startElement("a", control); //$NON-NLS-1$
writer.writeAttribute("id", controlId + "__lnk__" + i, null); // $NON-NLS-1$ $NON-NLS-2$
// "Page {0}"
String ariaLabel = ResourceHandler.getString("PagerRenderer.Gotopage0"); //$NON-NLS-1$
ariaLabel = StringUtil.format(ariaLabel, val);
writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
if (renderLink) { //make sure the a is tab-able
writer.writeAttribute("tabindex", "0", null); // $NON-NLS-1$ $NON-NLS-2$
writer.writeAttribute("aria-pressed", "false", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
writer.writeAttribute("aria-pressed", "true", null); // $NON-NLS-1$ $NON-NLS-2$
}
writer.writeText(val, null);
writer.endElement("a"); // $NON-NLS-1$
if (renderLink) {
setupSubmitOnClick(context, pager, pagerState, controlId + "__lnk__" + i, controlId + "__lnk__" + i); // $NON-NLS-1$ $NON-NLS-2$
}
}
writer.endElement("li"); // $NON-NLS-1$
}
// Encode after the pages
if (!pager.isAlwaysCalculateLast()) {
if (endCount < pagerState.getLastPage() || pagerState.hasMoreRows()) {
writer.startElement("li", null); // $NON-NLS-1$
writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
Property.DISABLED_CLASS.getClassName()));
writer.startElement("a", control); //$NON-NLS-1$
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
writer.writeText(getMayBeMorePages(), null);
writer.endElement("a"); // $NON-NLS-1$
writer.endElement("li"); // $NON-NLS-1$
}
}
// Restore the old page value
if (oldPage != null) {
requestMap.put(VAR_PAGE, oldPage);
} else {
requestMap.remove(VAR_PAGE);
}
}
protected void setupSubmitOnClick(FacesContext context, XspPager component, UIPager.PagerState st, String clientId,
String sourceId) {
boolean immediate = false;
UIComponent subTree = ((FacesContextEx) context).getSubTreeComponent();
boolean partialExec = component.isPartialExecute();
String execId = null;
if (partialExec) {
execId = component.getClientId(context);
immediate = true;
} else {
if (subTree != null) {
partialExec = true;
execId = subTree.getClientId(context);
immediate = true;
}
}
boolean partialRefresh = component.isPartialRefresh();
String refreshId = null;
if (partialRefresh) {
UIComponent refreshComponent = component.findSharedDataPagerParent();
if (null == refreshComponent) {
refreshComponent = (UIComponent) st.getDataIterator();
}
refreshId = AjaxUtilEx.getRefreshId(context, refreshComponent);
} else {
if (subTree != null) {
partialRefresh = true;
refreshId = subTree.getClientId(context);
}
}
// call some JavaScript in xspClient.js
final String event = "onclick"; // $NON-NLS-1$
// Note, the onClick event is also triggered if the user tabs to the
// image\link and presses enter (Not just when clicked with a
// mouse).
// When the source is clicked, put its id in the hidden field and
// submit the form.
StringBuilder buff = new StringBuilder();
if (partialRefresh) {
JavaScriptUtil.appendAttachPartialRefreshEvent(buff, clientId, sourceId, execId, event,
/* clientSideScriptName */null, immediate ? JavaScriptUtil.VALIDATION_NONE
: JavaScriptUtil.VALIDATION_FULL,
/* refreshId */refreshId,
/* onstart */getOnStart(component),
/* oncomplete */getOnComplete(component),
/* onerror */getOnError(component));
} else {
JavaScriptUtil.appendAttachEvent(buff, clientId, sourceId, execId, event,
/* clientSideScriptName */null,
/* submit */true, immediate ? JavaScriptUtil.VALIDATION_NONE : JavaScriptUtil.VALIDATION_FULL);
}
String script = buff.toString();
// Add the script block we just generated.
JavaScriptUtil.addScriptOnLoad(script);
}
protected String getOnStart(XspPager component) {
return (String) component.getAttributes().get("onStart"); // $NON-NLS-1$
}
protected String getOnComplete(XspPager component) {
return (String) component.getAttributes().get("onComplete"); // $NON-NLS-1$
}
protected String getOnError(XspPager component) {
return (String) component.getAttributes().get("onError"); // $NON-NLS-1$
}
protected void encodeStatus(FacesContext context, ResponseWriter writer, XspPager pager,
UIPager.PagerState pagerState, XspPagerControl control, int startCount, int endCount) throws IOException {
writer.startElement("li", null); // $NON-NLS-1$
writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
Property.DISABLED_CLASS.getClassName()));
String val = (String) control.getValue();
if (StringUtil.isEmpty(val)) {
val = "{0}"; // $NON-NLS-1$
}
if (pagerState.getLastPage() > 0) {
writer.startElement("a", null); // $NON-NLS-1$
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
writer.writeAttribute("role", "button", null); // $NON-NLS-2$ $NON-NLS-1$
val = StringUtil.format(val, pagerState.getCurrentPage() + 1, pagerState.getLastPage(), startCount,
endCount);
writer.writeText(val, null);
writer.endElement("a"); // $NON-NLS-1$
}
writer.endElement("li"); // $NON-NLS-1$
}
protected void encodeSeparator(FacesContext context, ResponseWriter writer, XspPagerControl control, String type)
throws IOException {
String val = (String) control.getValue();
writer.startElement("li", null); // $NON-NLS-1$
if (StringUtil.isEmpty(val)) {
String defaultSeparator = "|"; // $NON-NLS-1$
if (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE)) {
// "Page"
defaultSeparator = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Page"); //$NON-NLS-1$
}
val = defaultSeparator;
}
writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
Property.DISABLED_CLASS.getClassName()));
// Generate the text link
if (StringUtil.isNotEmpty(val)) {
writer.startElement("a", null); // $NON-NLS-1$
writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
writer.writeText(val, null);
writer.endElement("a"); // $NON-NLS-1$
}
writer.endElement("li"); // $NON-NLS-1$
}
protected void encodeGoto() {
// Do not exists in core XPages yet..
}
protected int getStartCount(UIPager.PagerState st, int pageCount) {
int start = (st.getFirst() / st.getRows()) - pageCount / 2;
start = Math.min(Math.max(0, st.getLastPage() - pageCount), Math.max(0, start));
return start;
}
protected int getEndCount(UIPager.PagerState st, int pageCount, int start) {
int sizeOfPageRange = Math.min(start + pageCount, st.getLastPage()) - start;
int end = start + sizeOfPageRange;
return end;
}
protected boolean isFirst(String type) {
return (type.equalsIgnoreCase(UIPagerControl.TYPE_FIRST)
|| type.equalsIgnoreCase(UIPagerControl.TYPE_FIRSTARROW) || type
.equalsIgnoreCase(UIPagerControl.TYPE_FIRSTIMAGE));
}
protected boolean isNext(String type) {
return (type.equalsIgnoreCase(UIPagerControl.TYPE_NEXT) || type.equalsIgnoreCase(UIPagerControl.TYPE_NEXTARROW) || type
.equalsIgnoreCase(UIPagerControl.TYPE_NEXTIMAGE));
}
protected boolean isLast(String type) {
return (type.equalsIgnoreCase(UIPagerControl.TYPE_LAST) || type.equalsIgnoreCase(UIPagerControl.TYPE_LASTARROW) || type
.equalsIgnoreCase(UIPagerControl.TYPE_LASTIMAGE));
}
protected boolean isPrevious(String type) {
return (type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUS)
|| type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSARROW) || type
.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSIMAGE));
}
protected boolean isSeparator(String type) {
return (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATOR) || type
.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE));
}
protected String getMayBeMorePages() {
return "..."; // $NON-NLS-1$
}
private void writeNonNullAttributeOnly(ResponseWriter writer, String attributeName, String attributeValue)
throws IOException {
if (attributeValue != null && !attributeValue.isEmpty()) {
writer.writeAttribute(attributeName, attributeValue, null);
}
}
private boolean isAnyAssigned(String... objs) {
for (String s : objs) {
if (s != null && !s.isEmpty())
return true;
}
return false;
}
}
That is a class I've been using in my last project. Basically it's re-implemeting everything that the pager does in order to have control over the look and feel of the component - it's called renderer after all. You could try to tweak it here and there to see how the various things work out on the page. It's a learning process.