org.richfaces.component.UIScrollableDataTable.java Source code

Java tutorial

Introduction

Here is the source code for org.richfaces.component.UIScrollableDataTable.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */
package org.richfaces.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;

import org.ajax4jsf.component.AjaxComponent;
import org.ajax4jsf.component.SequenceDataAdaptor;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.model.DataComponentState;
import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.ExtendedDataModel;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.util.ELUtils;
import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.convert.rowkey.ScrollableDataTableRowKeyConverter;
import org.richfaces.event.AttributeHolder;
import org.richfaces.event.ScrollableGridViewEvent;
import org.richfaces.event.sort.MultiColumnSortListener;
import org.richfaces.event.sort.SingleColumnSortListener;
import org.richfaces.event.sort.SortEvent;
import org.richfaces.event.sort.SortListener;
import org.richfaces.model.DataModelCache;
import org.richfaces.model.Modifiable;
import org.richfaces.model.ModifiableModel;
import org.richfaces.model.Ordering;
import org.richfaces.model.ScrollableTableDataModel;
import org.richfaces.model.ScrollableTableDataRange;
import org.richfaces.model.SelectionMode;
import org.richfaces.model.SortField;
import org.richfaces.model.SortField2;
import org.richfaces.model.SortOrder;
import org.richfaces.renderkit.html.ScrollableDataTableUtils;

/**
 * @author Anton Belevich
 *
 */

public abstract class UIScrollableDataTable extends SequenceDataAdaptor
        implements AjaxComponent, Sortable, Selectable, ScriptExportable {

    public static final String COMPONENT_TYPE = "org.richfaces.component.ScrollableDataTable";
    public static final String SORT_SINGLE = "single";
    public static final String SORT_MULTI = "multi";

    private final static Log log = LogFactory.getLog(UIScrollableDataTable.class);

    /**
     * Flag set on each phase to determine what range of data to walk
     * true means - locally saved ranges (for data pending update)
     * false means - use range that comes from component state
     */
    private boolean useSavedRanges = true;

    /**
     * hold list of ranges previously accessed until updates are fully done for them
     */
    private List<Range> ranges;

    private Collection<String> responseData = new ArrayList<String>();

    private int reqRowsCount = -1;

    private String scrollPos;

    private SortListener sortListener;

    private Converter defaultRowKeyConverter = new ScrollableDataTableRowKeyConverter();

    public abstract SortOrder getSortOrder();

    public abstract void setSortOrder(SortOrder sortOrder);

    public Collection<String> getResponseData() {
        return responseData;
    }

    public void setResponseData(Collection<String> responseData) {
        this.responseData = responseData;
    }

    protected DataComponentState createComponentState() {

        return new DataComponentState() {

            public Range getRange() {

                int curentRow = getFirst();

                int rows;
                if (reqRowsCount == -1) {
                    rows = getRows();
                } else {
                    rows = reqRowsCount;
                }

                int rowsCount = getExtendedDataModel().getRowCount();

                if (rows > 0) {

                    rows += curentRow;

                    if (rows >= 0) {
                        rows = Math.min(rows, rowsCount);
                    }

                } else if (rowsCount >= 0) {
                    rows = rowsCount;

                } else {
                    rows = -1;
                }

                return new ScrollableTableDataRange(curentRow, rows, getSortOrder());
            }
        };
    }

    public void processDecodes(FacesContext faces) {

        useSavedRanges = false;

        if (log.isTraceEnabled()) {
            log.trace("UIScrollableDataTable.processDecodes(faces)");
        }
        checkRange();
        super.processDecodes(faces);
    }

    public void processValidators(FacesContext faces) {
        useSavedRanges = true;
        if (log.isTraceEnabled()) {
            log.trace("UIScrollableDataTable.processValidators(faces)");
        }
        super.processValidators(faces);
    }

    public void processUpdates(FacesContext faces) {
        useSavedRanges = true;

        if (log.isTraceEnabled()) {
            log.trace("UIScrollableDataTable.processUpdates(faces)");
        }

        super.processUpdates(faces);
        ranges = null;
    }

    public void encodeBegin(FacesContext context) throws IOException {

        if (log.isTraceEnabled()) {
            log.trace("UIScrollableDataTable.encodeBegin(context)");
        }

        useSavedRanges = false;
        checkRange();
        super.encodeBegin(context);
    }

    protected ExtendedDataModel createDataModel() {
        ExtendedDataModel model = super.createDataModel();
        if (model instanceof ScrollableTableDataModel) {
            if (isCacheable()) {
                if (log.isDebugEnabled()) {
                    log.debug("Initializing cache of type " + DataModelCache.class);
                }
                model = new DataModelCache((ScrollableTableDataModel<?>) model);
            }
        } else {
            List<SortField2> sortFields = getSortFields();
            if (sortFields != null && !sortFields.isEmpty()) {
                Modifiable modifiable = null;
                if (model instanceof Modifiable) {
                    modifiable = (Modifiable) model;
                } else {
                    ModifiableModel modifiableModel = new ModifiableModel(model, getVar());
                    model = modifiableModel;
                    modifiable = modifiableModel;
                }
                modifiable.modify(null, sortFields);
            }
        }
        /*
         * TODO: Verify - do we really need it
           model.setSortOrder(getSortOrder());
        */
        return model;
    }

    private List<SortField2> getSortFields() {
        FacesContext context = FacesContext.getCurrentInstance();
        ELContext eLContext = context.getELContext();
        ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
        String var = getVar();
        SortOrder sortOrder = getSortOrder();
        List<SortField2> sortFields2 = null;
        if (sortOrder != null) {
            SortField[] sortFields = sortOrder.getFields();
            sortFields2 = new LinkedList<SortField2>();
            for (SortField sortField : sortFields) {
                ValueExpression valueExpression = null;
                String name = sortField.getName();
                if (ELUtils.isValueReference(name)) {
                    valueExpression = expressionFactory.createValueExpression(eLContext, name, Object.class);
                } else if (!name.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) {
                    valueExpression = expressionFactory.createValueExpression(eLContext,
                            "#{" + var + "." + name + "}", Object.class);
                }
                Ordering ordering = Ordering.UNSORTED;
                Boolean ascending = sortField.getAscending();
                if (ascending != null) {
                    if (ascending) {
                        ordering = Ordering.ASCENDING;
                    } else {
                        ordering = Ordering.DESCENDING;
                    }
                }
                if (valueExpression != null && !Ordering.UNSORTED.equals(ordering)) {
                    sortFields2.add(new SortField2(valueExpression, ordering));
                }
            }
        }
        return sortFields2;
    }

    public Object saveState(FacesContext context) {

        Object values[] = new Object[4];
        values[0] = super.saveState(context);
        values[1] = ranges;
        values[2] = scrollPos;
        values[3] = saveAttachedState(context, sortListener);

        return (Object) values;

    }

    @SuppressWarnings("unchecked")
    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[]) state;
        super.restoreState(context, values[0]);
        ranges = ((List<Range>) values[1]);
        scrollPos = (String) values[2];
        sortListener = (SortListener) restoreAttachedState(context, values[3]);
    }

    @SuppressWarnings("unchecked")
    protected Iterator<UIComponent> dataChildren() {
        IteratorChain chain = new IteratorChain();
        //RF-1248 Adding facets to both dataChildren and fixed children
        //To make both supports and header/footer work
        chain.addIterator(getFacets().values().iterator());
        for (Iterator<UIComponent> i = getChildren().iterator(); i.hasNext();) {
            UIComponent kid = (UIComponent) i.next();
            if (kid instanceof Column || kid instanceof UIColumn) {
                chain.addIterator(kid.getChildren().iterator());
            }
        }

        return chain;
    }

    @SuppressWarnings("unchecked")
    protected Iterator<UIComponent> fixedChildren() {
        IteratorChain chain = new IteratorChain(getFacets().values().iterator());
        //RF-1248 Adding facets to both dataChildren and fixed children
        //To make both supports and header/footer work
        for (Iterator<UIComponent> i = getChildren().iterator(); i.hasNext();) {
            UIComponent kid = (UIComponent) i.next();
            if (kid instanceof Column || kid instanceof UIColumn) {
                chain.addIterator(kid.getFacets().values().iterator());
            }
        }

        return chain;
    }

    public void broadcast(FacesEvent event) throws AbortProcessingException {

        super.broadcast(event);

        if (event instanceof AttributeHolder) {
            ((AttributeHolder) event).applyAttributes(this);
        }

        if (event instanceof AjaxEvent) {
            AjaxContext.getCurrentInstance().addComponentToAjaxRender(this);
        } else if (event instanceof SortEvent) {
            processSortingChange(event);
            //   new AjaxEvent(this).queue();
        } else if (event instanceof ScrollableGridViewEvent) {
            //   new AjaxEvent(this).queue();
            processScrolling(event);
        }

    }

    protected boolean broadcastLocal(FacesEvent event) {
        return super.broadcastLocal(event) || event instanceof SortEvent
                || event instanceof ScrollableGridViewEvent;
    }

    public void queueEvent(FacesEvent event) {

        if (event instanceof AjaxEvent) {
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
        } else if (event instanceof SortEvent) {
            new AjaxEvent(this).queue();
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
        } else if (event instanceof ScrollableGridViewEvent) {
            event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
            new AjaxEvent(this).queue();
        }
        super.queueEvent(event);
    }

    public void processScrolling(FacesEvent event) {
        ScrollableGridViewEvent e = (ScrollableGridViewEvent) event;

        setFirst(e.getFirst());
        reqRowsCount = e.getRows();

        getFacesContext().renderResponse();
    }

    public void processSortingChange(FacesEvent event) {

        SortEvent e = (SortEvent) event;

        getSortListener().processSort(e);

        setFirst(e.getFirst());
        reqRowsCount = e.getRows();

        resetDataModel();

        getFacesContext().renderResponse();
    }

    public void walk(FacesContext faces, DataVisitor visitor, Object argument) throws IOException {

        Range visitedRange = getComponentState().getRange();

        if (ranges == null) {
            ranges = new ArrayList<Range>();
        }

        if (!ranges.contains(visitedRange)) {
            ranges.add(visitedRange);
        }

        if (useSavedRanges) {

            for (Iterator<Range> iter = ranges.iterator(); iter.hasNext();) {
                ScrollableTableDataRange range = (ScrollableTableDataRange) iter.next();

                if (log.isDebugEnabled()) {
                    log.debug("Range is: " + range.getFirst() + " - " + range.getLast() + " sortOrder: "
                            + range.getSortOrder());
                }

                getExtendedDataModel().walk(faces, visitor, range, argument);
            }

        } else {
            super.walk(faces, visitor, argument);
        }
    }

    public void encodeEnd(FacesContext context) throws IOException {
        super.encodeEnd(context);
    }

    public boolean isCacheable() {
        return true;
    }

    public String getScrollPos() {
        return scrollPos;
    }

    public void setScrollPos(String scrollPos) {
        this.scrollPos = scrollPos;
    }

    public SortListener getSortListener() {
        if (sortListener != null) {
            return sortListener;
        }

        if (SORT_MULTI.equals(getSortMode())) {

            return MultiColumnSortListener.INSTANCE;

        } else {

            return SingleColumnSortListener.INSTANCE;

        }
    }

    public void setSortListener(SortListener sortListener) {
        this.sortListener = sortListener;
    }

    public abstract String getSortMode();

    public abstract void setSortMode(String mode);

    public abstract SelectionMode getSelectionMode();

    public abstract void setSelectionMode(SelectionMode mode);

    public boolean isSelectionEnabled() {
        return getSelectionMode().isSelectionEnabled();
    }

    public abstract Object getActiveRowKey();

    public abstract void setActiveRowKey(Object activeRowKey);

    /* (non-Javadoc)
     * @see org.ajax4jsf.component.UIDataAdaptor#setRowIndex(int)
     */
    public void setRowIndex(int index) {
        if (index < 0) {
            setRowKey(null);
        }
        //super.setRowIndex(index);
    }

    public void resetReqRowsCount() {
        this.reqRowsCount = -1;
    }

    private void checkRange() {
        int rows;
        if (reqRowsCount == -1) {
            rows = getRows();
        } else {
            rows = reqRowsCount;
        }
        if (getRowCount() < getFirst() + rows) {
            setFirst(0);
            setScrollPos("0,0," + rows + "," + ScrollableDataTableUtils.getClientRowIndex(this));
        }
    }

    //RF-2771
    public boolean isLimitToList() {
        // TODO Auto-generated method stub
        return false;
    }

    public void setLimitToList(boolean submitForm) {
        // TODO Auto-generated method stub

    }

    public boolean isAjaxSingle() {
        // TODO Auto-generated method stub
        return false;
    }

    public void setAjaxSingle(boolean single) {
        // TODO Auto-generated method stub

    }

    @Override
    public Converter getRowKeyConverter() {
        Converter converter = super.getRowKeyConverter();
        if (null == converter) {
            return defaultRowKeyConverter;
        }
        return converter;
    }

    @Override
    public void setRowKeyConverter(Converter rowKeyConverter) {
        super.setRowKeyConverter(rowKeyConverter);
    }
}