Java tutorial
/** * 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); } }