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

org.apache.myfaces.custom.datascroller.AbstractHtmlDataScroller Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is compatible with both JSF1.1 and JSF1.2; however for JSF1.2 users there is an alternative build of Tomahawk available that takes advantage of JSF1.2 features to offer some additional benefits.

There is a newer version: 1.1.14
Show newest version
/*
 * 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(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy