org.apache.myfaces.custom.datascroller.AbstractHtmlDataScroller Maven / Gradle / Ivy
Show all versions of tomahawk Show documentation
/*
* 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.
*/
package org.apache.myfaces.custom.datascroller;
import javax.faces.FacesException;
import javax.faces.component.ActionSource;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIPanel;
import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.component.DisplayValueOnlyAware;
import org.apache.myfaces.component.ForceIdAware;
import org.apache.myfaces.component.StyleAware;
import org.apache.myfaces.component.UniversalProperties;
import org.apache.myfaces.component.UserRoleAware;
import org.apache.myfaces.component.UserRoleUtils;
import org.apache.myfaces.component.html.util.HtmlComponentUtils;
import org.apache.myfaces.shared_tomahawk.component.DisplayValueOnlyCapable;
/**
* Scroller for UIData components eg. dataTable
*
* Must be nested inside footer facet of dataTable OR for
* attribute must be given so that corresponding uiData can be found.
*
* Unless otherwise specified, all attributes accept static values or EL expressions.
*
* A component which works together with a UIData component to allow a
* user to view a large list of data one "page" at a time, and navigate
* between pages.
*
* @JSFComponent
* name = "t:dataScroller"
* class = "org.apache.myfaces.custom.datascroller.HtmlDataScroller"
* tagClass = "org.apache.myfaces.custom.datascroller.HtmlDataScrollerTag"
* @since 1.1.7
* @author Thomas Spiegl (latest modification by $Author: lu4242 $)
* @version $Revision: 955086 $ $Date: 2010-06-15 18:32:00 -0500 (Tue, 15 Jun 2010) $
*/
public abstract class AbstractHtmlDataScroller extends UIPanel
implements ActionSource, UserRoleAware, DisplayValueOnlyCapable,
DisplayValueOnlyAware, ForceIdAware, UniversalProperties, StyleAware
{
public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlDataScroller";
public static final String COMPONENT_FAMILY = "javax.faces.Panel";
private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.DataScroller";
private static final boolean DEFAULT_IMMEDIATE = false;
private static final Log log = LogFactory.getLog(AbstractHtmlDataScroller.class);
private static final String FIRST_FACET_NAME = "first";
private static final String LAST_FACET_NAME = "last";
private static final String NEXT_FACET_NAME = "next";
private static final String PREVIOUS_FACET_NAME = "previous";
private static final String FAST_FORWARD_FACET_NAME = "fastforward";
private static final String FAST_REWIND_FACET_NAME = "fastrewind";
public static final String FACET_FIRST = "first".intern();
public static final String FACET_PREVIOUS = "previous".intern();
public static final String FACET_NEXT = "next".intern();
public static final String FACET_LAST = "last".intern();
public static final String FACET_FAST_FORWARD = "fastf".intern();
public static final String FACET_FAST_REWIND = "fastr".intern();
private static final String TABLE_LAYOUT = "table";
private static final String LIST_LAYOUT = "list";
private static final String SINGLE_LIST_LAYOUT = "singleList";
private static final String SINGLE_TABLE_LAYOUT = "singleTable";
// just for caching the associated uidata
private transient UIData _UIData;
private transient Boolean _listLayout;
private transient Boolean _singleElementLayout;
private MethodBinding _actionListener;
public String getClientId(FacesContext context)
{
String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
if (clientId == null)
{
clientId = super.getClientId(context);
}
return clientId;
}
public boolean isRendered()
{
if (!UserRoleUtils.isVisibleOnUserRole(this)) return false;
return super.isRendered();
}
public boolean isSetDisplayValueOnly(){
return getDisplayValueOnly() != null ? true : false;
}
public boolean isDisplayValueOnly(){
return getDisplayValueOnly() != null ? getDisplayValueOnly().booleanValue() : false;
}
public void setDisplayValueOnly(boolean displayValueOnly){
this.setDisplayValueOnly((Boolean) Boolean.valueOf(displayValueOnly));
}
/**
* The layout this scroller should render with. Default is 'table',
* 'list' is implemented as well. Additionally you can use
* 'singleList' - then the data-scroller will render a list, but
* not the paginator - same with the value 'singleTable'.
*
* @JSFProperty
* defaultValue = "table"
*/
public abstract String getLayout();
/**
* standard html colspan attribute for table cell
*
* @JSFProperty
* defaultValue = "Integer.MIN_VALUE"
*/
public abstract int getColspan();
/**
* HTML: Script to be invoked when the element is clicked.
*
* @JSFProperty
*/
public abstract String getOnclick();
/**
* HTML: Script to be invoked when the element is double-clicked.
*
* @JSFProperty
*/
public abstract String getOndblclick();
public boolean isListLayout()
{
if(_listLayout == null)
{
String layout=getLayout();
if(layout == null || layout.equals(TABLE_LAYOUT) || layout.equals(SINGLE_TABLE_LAYOUT))
_listLayout = Boolean.FALSE;
else if(layout.equals(LIST_LAYOUT) || layout.equals(SINGLE_LIST_LAYOUT))
{
_listLayout = Boolean.TRUE;
}
else
{
log.error("Invalid layout-parameter : "+layout +" provided. Defaulting to table-layout.");
_listLayout = Boolean.FALSE;
}
}
return _listLayout.booleanValue();
}
public boolean isSingleElementLayout()
{
if(_singleElementLayout == null)
{
String layout=getLayout();
if(layout == null || layout.equals(SINGLE_LIST_LAYOUT) || layout.equals(SINGLE_TABLE_LAYOUT))
_singleElementLayout = Boolean.TRUE;
else
_singleElementLayout = Boolean.FALSE;
}
return _singleElementLayout.booleanValue();
}
/**
* Catch any attempts to queue events for this component, and ensure
* the event's phase is set appropriately. Events are expected to be
* queued by this component's renderer.
*
* When this component is marked "immediate", any ActionEvent will
* be marked to fire in the "apply request values" phase. When this
* component is not immediate the event will fire during the
* "invoke application" phase instead.
*/
public void queueEvent(FacesEvent event)
{
if (event != null && event instanceof ActionEvent)
{
if (isImmediate())
{
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
}
else
{
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
super.queueEvent(event);
}
/**
* Invoke any action listeners attached to this class.
*
* After listener invocation, the associated UIData's properties get
* updated:
*
* - if the user selected an absolute page# then setFirst is called with
* uiData.getRows() * pageNumber.
*
- if the user selected the "first page" option then setFirst(0) is called.
*
- if the user selected the "previous page" option then setFirst is decremented
* by uiData.getRows().
*
- if the user selected the "fast rewind" option then setFirst is decremented
* by uiData.getRows() * fastStep.
*
- next, fast-forward and last options have the obvious effect.
*
*/
public void broadcast(FacesEvent event) throws AbortProcessingException
{
super.broadcast(event);
if (event instanceof ScrollerActionEvent)
{
ScrollerActionEvent scrollerEvent = (ScrollerActionEvent) event;
// huh? getUIData never returns null.
UIData uiData = getUIData();
if (uiData == null)
{
return;
}
int pageindex = scrollerEvent.getPageIndex();
if (pageindex == -1)
{
String facet = scrollerEvent.getScrollerfacet();
if (FACET_FIRST.equals(facet))
{
setFirst(uiData, 0);
}
else if (FACET_PREVIOUS.equals(facet))
{
int previous = uiData.getFirst() - uiData.getRows();
if (previous >= 0)
setFirst(uiData, previous);
}
else if (FACET_NEXT.equals(facet))
{
int next = uiData.getFirst() + uiData.getRows();
if (next < uiData.getRowCount())
setFirst(uiData, next);
}
else if (FACET_FAST_FORWARD.equals(facet))
{
int fastStep = getFastStep();
if (fastStep <= 0)
fastStep = 1;
int next = uiData.getFirst() + uiData.getRows() * fastStep;
int rowcount = uiData.getRowCount();
if (next >= rowcount)
next = (rowcount - 1) - ((rowcount - 1) % uiData.getRows());
setFirst(uiData, next);
}
else if (FACET_FAST_REWIND.equals(facet))
{
int fastStep = getFastStep();
if (fastStep <= 0)
fastStep = 1;
int previous = uiData.getFirst() - uiData.getRows() * fastStep;
if (previous < 0)
previous = 0;
setFirst(uiData, previous);
}
else if (FACET_LAST.equals(facet))
{
int rowcount = uiData.getRowCount();
int rows = uiData.getRows();
int delta = (rows != 0) ? (rowcount % rows) : 0;
int first = delta > 0 && delta < rows ? rowcount - delta : rowcount - rows;
if (first >= 0)
{
setFirst(uiData, first);
}
else
{
setFirst(uiData, 0);
}
}
}
else
{
int pageCount = getPageCount();
if (pageindex > pageCount)
{
pageindex = pageCount;
}
if (pageindex <= 0)
{
pageindex = 1;
}
setFirst(uiData, uiData.getRows() * (pageindex - 1));
}
broadcastToActionListener(scrollerEvent);
}
}
protected void setFirst(UIData uiData, int value) {
//there might be special cases where the first-property of the data-table
//is bound to a backing bean. If this happens, the user probably wants
//the data-scroller to update this backing-bean value - if not, you can always
//override this method in a subclass.
if(uiData.getValueBinding("first")!=null)
{
ValueBinding vb = uiData.getValueBinding("first");
vb.setValue(getFacesContext(),new Integer(value));
}
else
{
uiData.setFirst(value);
}
}
/**
* @param event
*/
protected void broadcastToActionListener(ScrollerActionEvent event)
{
FacesContext context = getFacesContext();
MethodBinding actionListenerBinding = getActionListener();
if (actionListenerBinding != null)
{
try
{
actionListenerBinding.invoke(context, new Object[] {event});
}
catch (EvaluationException e)
{
Throwable cause = e.getCause();
if (cause != null && cause instanceof AbortProcessingException)
{
throw (AbortProcessingException)cause;
}
throw e;
}
}
ActionListener defaultActionListener
= context.getApplication().getActionListener();
if (defaultActionListener != null)
{
defaultActionListener.processAction((ActionEvent)event);
}
}
/**
* @return int
*/
public UIData getUIData()
{
if (_UIData == null)
{
_UIData = findUIData();
}
return _UIData;
}
/**
* @return the page index of the uidata
*/
public int getPageIndex()
{
UIData uiData = getUIData();
int rows = uiData.getRows();
if (0 == rows)
{
throw new FacesException("You need to set a value to the 'rows' attribute of component '" + uiData.getClientId(getFacesContext()) + "'" );
}
int pageIndex;
if (rows > 0)
{
pageIndex = uiData.getFirst() / rows + 1;
}
else
{
log.warn("DataTable " + uiData.getClientId(FacesContext.getCurrentInstance())
+ " has invalid rows attribute.");
pageIndex = 0;
}
if (uiData.getFirst() % rows > 0)
{
pageIndex++;
}
return pageIndex;
}
/**
* @return the page count of the uidata
*/
public int getPageCount()
{
UIData uiData = getUIData();
int rows = uiData.getRows();
int pageCount;
if (rows > 0)
{
pageCount = rows <= 0 ? 1 : uiData.getRowCount() / rows;
if (uiData.getRowCount() % rows > 0)
{
pageCount++;
}
}
else
{
rows = 1;
pageCount = 1;
}
return pageCount;
}
/**
* @return int
*/
public int getRowCount()
{
return getUIData().getRowCount();
}
/**
* @return int
*/
public int getRows()
{
return getUIData().getRows();
}
/**
* @return int
*/
public int getFirstRow()
{
return getUIData().getFirst();
}
/**
* Find the UIData component associated with this scroller.
*
* If the "for" attribute is not null then that value is used to find the
* specified component by id. Both "relative" and "absolute" ids are allowed;
* see method UIComponent.findComponent for details.
*
* If the "for" attribute is not defined, then this component is expected to
* be a child of a UIData component.
*
* @throws IllegalArgumentException if an associated UIData component
* cannot be found.
*/
protected UIData findUIData()
{
String forStr = getFor();
UIComponent forComp;
if (forStr == null)
{
// DataScroller may be a child of uiData
forComp = getParent();
}
else
{
forComp = findComponent(forStr);
}
if (forComp == null)
{
throw new IllegalArgumentException(
"could not find UIData referenced by attribute dataScroller@for = '"
+ forStr + "'");
}
else if (!(forComp instanceof UIData))
{
throw new IllegalArgumentException(
"uiComponent referenced by attribute dataScroller@for = '"
+ forStr + "' must be of type " + UIData.class.getName()
+ ", not type " + forComp.getClass().getName());
}
return (UIData) forComp;
}
public void setFirst(UIComponent first)
{
getFacets().put(FIRST_FACET_NAME, first);
}
/**
* @JSFFacet
*/
public UIComponent getFirst()
{
return (UIComponent) getFacets().get(FIRST_FACET_NAME);
}
public void setLast(UIComponent last)
{
getFacets().put(LAST_FACET_NAME, last);
}
/**
* @JSFFacet
*/
public UIComponent getLast()
{
return (UIComponent) getFacets().get(LAST_FACET_NAME);
}
public void setNext(UIComponent next)
{
getFacets().put(NEXT_FACET_NAME, next);
}
/**
* @JSFFacet
*/
public UIComponent getNext()
{
return (UIComponent) getFacets().get(NEXT_FACET_NAME);
}
public void setFastForward(UIComponent previous)
{
getFacets().put(FAST_FORWARD_FACET_NAME, previous);
}
/**
* @JSFFacet
*/
public UIComponent getFastForward()
{
return (UIComponent) getFacets().get(FAST_FORWARD_FACET_NAME);
}
public void setFastRewind(UIComponent previous)
{
getFacets().put(FAST_REWIND_FACET_NAME, previous);
}
/**
* @JSFFacet
*/
public UIComponent getFastRewind()
{
return (UIComponent) getFacets().get(FAST_REWIND_FACET_NAME);
}
public void setPrevious(UIComponent previous)
{
getFacets().put(PREVIOUS_FACET_NAME, previous);
}
/**
* @JSFFacet
*/
public UIComponent getPrevious()
{
return (UIComponent) getFacets().get(PREVIOUS_FACET_NAME);
}
public boolean getRendersChildren()
{
return true;
}
/**
* @see javax.faces.component.ActionSource#getAction()
*/
public MethodBinding getAction()
{
// not used
return null;
}
/**
* @see javax.faces.component.ActionSource#setAction(javax.faces.el.MethodBinding)
*/
public void setAction(MethodBinding action)
{
throw new UnsupportedOperationException(
"defining an action is not supported. use an actionlistener");
}
/**
* @see javax.faces.component.ActionSource#setActionListener(javax.faces.el.MethodBinding)
*/
public void setActionListener(MethodBinding actionListener)
{
_actionListener = actionListener;
}
/**
* MethodBinding pointing at method acception an ActionEvent with return type void.
*
* @JSFProperty
* returnSignature="void"
* methodSignature="javax.faces.event.ActionEvent"
* @see javax.faces.component.ActionSource#getActionListener()
*/
public MethodBinding getActionListener()
{
return _actionListener;
}
/**
* @see javax.faces.component.ActionSource#addActionListener(javax.faces.event.ActionListener)
*/
public void addActionListener(ActionListener listener)
{
addFacesListener(listener);
}
/**
* @see javax.faces.component.ActionSource#getActionListeners()
*/
public ActionListener[] getActionListeners()
{
return (ActionListener[]) getFacesListeners(ActionListener.class);
}
/**
* @see javax.faces.component.ActionSource#removeActionListener(javax.faces.event.ActionListener)
*/
public void removeActionListener(ActionListener listener)
{
removeFacesListener(listener);
}
public Object saveState(FacesContext context)
{
Object values[] = new Object[2];
values[0] = super.saveState(context);
values[1] = saveAttachedState(context, _actionListener);
return values;
}
public void restoreState(FacesContext context, Object state)
{
Object values[] = (Object[]) state;
super.restoreState(context, values[0]);
_actionListener = (MethodBinding)restoreAttachedState(context, values[1]);
}
/**
* The JSF id of a UIData component that this scroller will affect.
*
* If this attribute is not present then the datascroller must be
* a child of a UIData component.
*
* @JSFProperty
*/
public abstract String getFor();
/**
* step (pages) used for fastforward and fastrewind
*
* @JSFProperty
* defaultValue="Integer.MIN_VALUE"
*/
public abstract int getFastStep();
/**
* A parameter name, under which the actual page index is set
* in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getPageIndexVar();
/**
* A parameter name, under which the actual page count is set
* in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getPageCountVar();
/**
* A parameter name, under which the actual rows count is set
* in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getRowsCountVar();
/**
* A parameter name, under which the actual displayed rows count
* is set in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getDisplayedRowsCountVar();
/**
* A parameter name, under which the actual first displayed row
* index is set in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getFirstRowIndexVar();
/**
* A parameter name, under which the actual last displayed row
* index is set in request scope similar to the var parameter.
*
* @JSFProperty
*/
public abstract String getLastRowIndexVar();
/**
* If set true, then the paginator gets rendered
*
* @JSFProperty
* defaultValue = "false"
*/
public abstract boolean isPaginator();
/**
* The maximum amount of pages to be displayed in the paginator.
*
* @JSFProperty
* defaultValue = "Integer.MIN_VALUE"
*/
public abstract int getPaginatorMaxPages();
/**
* styleclass for pagingator
*
* @JSFProperty
*/
public abstract String getPaginatorTableClass();
/**
* style for pagingator
*
* @JSFProperty
*/
public abstract String getPaginatorTableStyle();
/**
* styleClass for paginator's column
*
* @JSFProperty
*/
public abstract String getPaginatorColumnClass();
/**
* style for paginator's column
*
* @JSFProperty
*/
public abstract String getPaginatorColumnStyle();
/**
* styleClass for paginator's column with pageIndex = currentPageIndex
*
* @JSFProperty
*/
public abstract String getPaginatorActiveColumnClass();
/**
* 'true' - render a link for the paginator's column with
* pageIndex = currentPageIndex. Default-value is 'true'.
*
* @JSFProperty
* defaultValue = "true"
*/
public abstract boolean isPaginatorRenderLinkForActive();
/**
* style-class for data-scroller first-element
*
* @JSFProperty
*/
public abstract String getFirstStyleClass();
/**
* style-class for data-scroller last-element
*
* @JSFProperty
*/
public abstract String getLastStyleClass();
/**
* style-class for data-scroller previous-element
*
* @JSFProperty
*/
public abstract String getPreviousStyleClass();
/**
* style-class for dataScroller next-element
*
* @JSFProperty
*/
public abstract String getNextStyleClass();
/**
* style-class for data-scroller fast-forward-element
*
* @JSFProperty
*/
public abstract String getFastfStyleClass();
/**
* style-class for data-scroller fast-rewind-element
*
* @JSFProperty
*/
public abstract String getFastrStyleClass();
/**
* style for paginator's column with pageIndex = currentPageIndex
*
* @JSFProperty
*/
public abstract String getPaginatorActiveColumnStyle();
/**
* If set to false, the facets aren't renderd if all the
* lines are contained on a single page. Default is true.
*
* @JSFProperty
* defaultValue="true"
*/
public abstract boolean isRenderFacetsIfSinglePage();
/**
* True means that the default ActionListener should be
* executed immediately (i.e. during Apply Request
* Values phase of the request processing lifecycle),
* rather than waiting until the Invoke Application phase.
*
* @JSFProperty
* defaultValue="false"
*/
public abstract boolean isImmediate();
/**
* If the dataScroller is on the first page (index is at 1), links for
* first, prev and fastprev are disabled. Default is false.
*
* @JSFProperty
* defaultValue="false"
*/
public abstract boolean isDisableFacetLinksIfFirstPage();
/**
* If the dataScroller is on the last page (index is at pagecount), links for
* last, next and fastnext are disabled. Default is false.
*
* @JSFProperty
* defaultValue="false"
*/
public abstract boolean isDisableFacetLinksIfLastPage();
/**
* If the dataScroller is on the first page (index is at 1), links for
* first, prev and fastprev are rendered. Default is true.
*
* @JSFProperty
* defaultValue="true"
*/
public abstract boolean isRenderFacetLinksIfFirstPage();
/**
* If the dataScroller is on the last page (index is at pagecount), links for
* last, next and fastnext are rendered. Default is true.
*
* @JSFProperty
* defaultValue="true"
*/
public abstract boolean isRenderFacetLinksIfLastPage();
}