/**
* License Agreement.
*
* JBoss RichFaces - Ajax4jsf Component Library
*
* Copyright (C) 2008 CompuGROUP Holding AG
*
* 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.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import javax.faces.model.DataModel;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.ContextInitParameters;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.model.ExtendedDataModel;
import org.apache.commons.collections.iterators.IteratorChain;
import org.richfaces.event.extdt.ChangeColumnVisibilityEvent;
import org.richfaces.event.extdt.ColumnResizeEvent;
import org.richfaces.event.extdt.DragDropEvent;
import org.richfaces.event.extdt.ExtTableFilterEvent;
import org.richfaces.event.extdt.ExtTableSortEvent;
import org.richfaces.model.ExtendedTableDataModel;
import org.richfaces.model.ExtendedTableDataModifiableModel;
import org.richfaces.model.FilterField;
import org.richfaces.model.LocaleAware;
import org.richfaces.model.Modifiable;
import org.richfaces.model.ModifiableModel;
import org.richfaces.model.Ordering;
import org.richfaces.model.SortField2;
/**
* JSF component class
*
*/
public abstract class UIExtendedDataTable extends UIDataTable implements
Selectable, Filterable, Sortable2 {
/**
* COMPONENT_TYPE
*/
public static final String COMPONENT_TYPE = "org.richfaces.ExtendedDataTable";
/**
* COMPONENT_FAMILY
*/
public static final String COMPONENT_FAMILY = "org.richfaces.ExtendedDataTable";
protected ExtendedDataTableState state;
public abstract Object getActiveRowKey();
public abstract void setActiveRowKey(Object activeRowKey);
public abstract String getGroupingColumn();
public abstract void setGroupingColumn(String groupingColumn);
public abstract void setTableState(String tableState);
public abstract String getTableState();
public void broadcast(FacesEvent event) throws AbortProcessingException {
super.broadcast(event);
if (event instanceof AjaxEvent) {
//TODO nick - add regions from component too
AjaxContext.getCurrentInstance().addComponentToAjaxRender(this);
} else if (event instanceof DragDropEvent) {
processDradDrop((DragDropEvent) event);
} else if (event instanceof ChangeColumnVisibilityEvent) {
processChangeColumnVisibility((ChangeColumnVisibilityEvent) event);
} else if (event instanceof ColumnResizeEvent) {
processColumnResize((ColumnResizeEvent) event);
} else if (event instanceof ExtTableSortEvent) {
processSortingChange((ExtTableSortEvent) event);
} else if (event instanceof ExtTableFilterEvent) {
processFilteringChange((ExtTableFilterEvent) event);
}
}
public void queueEvent(FacesEvent event) {
if(event.getSource() instanceof UIExtendedDataTable) {
if (event instanceof AjaxEvent) {
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else if (event instanceof DragDropEvent) {
new AjaxEvent(this).queue();
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else if (event instanceof ChangeColumnVisibilityEvent) {
new AjaxEvent(this).queue();
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else if (event instanceof ColumnResizeEvent) {
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
} else if (event instanceof ExtTableSortEvent) {
new AjaxEvent(this).queue();
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
} else if (event instanceof ExtTableFilterEvent) {
new AjaxEvent(this).queue();
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
}
}
super.queueEvent(event);
}
public Iterator<UIColumn> getSortedColumns() {
return new SortedColumnsIterator(this);
}
public Iterator<UIColumn> getChildColumns() {
return new ExtendedTableColumnsIterator(this);
}
public void processDradDrop(DragDropEvent event) {
String dragValue = event.getDragValue().toString();// dnd_drag_script
String dropValue = event.getDropValue().toString();// dnd_drop_script
getState().changeColumnsOrder(dragValue, dropValue, event.isDropBefore());
//getFacesContext().renderResponse();
}
public List<UIComponent> getSortedChildren() {
return getState().sortColumns(getFacesContext(), super.getChildren());
}
public void processChangeColumnVisibility(ChangeColumnVisibilityEvent event) {
getState().toggleColumnVisibility(this, event.getColumnId());
//getFacesContext().renderResponse();
}
public void processSortingChange(ExtTableSortEvent event) {
DataModel dataModel = getDataModel();
if (dataModel instanceof ExtendedTableDataModifiableModel<?>) {
((ExtendedTableDataModifiableModel<?>)dataModel).resetSort();
}
else{
super.resetDataModel();
}
getFacesContext().renderResponse();
}
public void processFilteringChange(ExtTableFilterEvent event) {
DataModel dataModel = getDataModel();
if (dataModel instanceof ExtendedTableDataModifiableModel<?>) {
((ExtendedTableDataModifiableModel<?>)dataModel).resetFilter();
}
else{
super.resetDataModel();
}
resetGroupVisibilityState();
getFacesContext().renderResponse();
}
public boolean isColumnVisible(UIComponent column) {
return getState().isColumnVisible(column.getId());
}
public void processColumnResize(ColumnResizeEvent event) {
getState().changeColumnSize(this, event.getColumnWidths());
//getFacesContext().renderResponse();
}
public String getColumnSize(UIComponent column) {
return getState().getColumnSize(column);
}
public boolean isGroupingOn() {
//first get it from state
String groupingColumnId = getGroupByColumnId();
return ((groupingColumnId!=null) && (!"".equals(groupingColumnId)) && (!ExtendedDataTableState.NONE_COLUMN_ID.equals(groupingColumnId)));
}
/**
* Get id of column which the data is grouped by. First tries to get it
* from the table state. If the table state doesn't provide such
* information, get from component attribute.
*
* @return id of column which the data is grouped by
*/
public String getGroupByColumnId() {
String id = getState().getGroupingColumnId();
if (id == null){//grouping is not saved in state
//get column id from attribute
id = getGroupingColumn();
}
return id;
}
/**
* Get column component which the data is grouped by. First tries to get it
* from the table state. If the table state doesn't provide such
* information, get from component attribute.
*
* @return column which the data is grouped by
*/
public UIColumn getGroupByColumn() {
if (!isGroupingOn()){
return null;
}
String groupingColumnId = getGroupByColumnId();
for (Iterator<UIColumn> columns = getChildColumns(); columns.hasNext();) {
UIColumn child = columns.next();
if (groupingColumnId.equalsIgnoreCase(child.getId())) {
return child;
}
}
return null;
}
public void setGroupByColumn(org.richfaces.component.UIColumn column) {
if (column == null) {
getState().disableGrouping();
} else {
getState().groupBy(column.getId(), column.getSortOrder());
}
}
protected void resetGroupVisibilityState() {
getState().resetGroupVisibilityState();
}
public void disableGrouping() {
getState().disableGrouping();
}
public boolean groupIsExpanded(int index) {
return getState().groupIsExpanded(index);
}
public void toggleGroup(int index) {
getState().toggleGroup(index);
}
protected Collection<Object> getGroupPriority(){
Collection<Object> priority = getSortPriority();
if (isGroupingOn()) {// grouping is on
UIColumn column = getGroupByColumn();
if ((column.getSortOrder() == null) || (column.getSortOrder().equals(Ordering.UNSORTED))){
column.setSortOrder(Ordering.ASCENDING);
}
String groupColId = column.getId();
// try to add group column id as first
if (priority.contains(groupColId)) {
priority.remove(groupColId);
}
if (priority instanceof List) {
((List<Object>) priority).add(0, groupColId);
} else {
priority.add(groupColId);
}
}
return priority;
}
// public Object saveState(FacesContext context) {
// Object values[] = new Object[2];
// values[0] = super.saveState(context);
// values[1] = state;
// return values;
// }
//
// public void restoreState(FacesContext context, Object state) {
// Object values[] = (Object[]) state;
// super.restoreState(context, values[0]);
// this.state = (ExtendedDataTableState) values[1];
// }
public int getVisibleColumnsCount() {
int count = 0;
for (Iterator<UIColumn> iter = getChildColumns(); iter.hasNext();) {
UIColumn column = iter.next();
if (column.isRendered())
count++;
}// for
return count;
}// getVisibleColumnnCount
@Override
@SuppressWarnings("unchecked")
protected ExtendedDataModel createDataModel() {
List<FilterField> filterFields = new LinkedList<FilterField>();
Map<String, SortField2> sortFieldsMap = new LinkedHashMap<String, SortField2>();
Collection<Object> sortPriority = getGroupPriority();
List<UIComponent> list = getChildren();
for (Iterator<UIComponent> iterator = list.iterator(); iterator
.hasNext();) {
UIComponent component = iterator.next();
if (component instanceof UIColumn) {
UIColumn column = (UIColumn) component;
FilterField filterField = column.getFilterField();
if (filterField != null) {
filterFields.add(filterField);
}
SortField2 sortField = column.getSortField();
if (sortField != null) {
sortFieldsMap.put(component.getId(), sortField);
}
}
}
List<SortField2> sortFields = new LinkedList<SortField2>();
if (sortPriority != null) {
for (Object object : sortPriority) {
if (object instanceof String) {
String id = (String) object;
SortField2 sortField = sortFieldsMap.get(id);
if (sortField != null) {
sortFields.add(sortField);
sortFieldsMap.remove(id);
}
}
}
}
sortFields.addAll(sortFieldsMap.values());
setFilterFields(filterFields);
setSortFields(sortFields);
ExtendedDataModel dataModel = (ExtendedDataModel)getDataModel();
if (dataModel instanceof ExtendedTableDataModifiableModel<?>) {
((ExtendedTableDataModifiableModel<?>) dataModel).setVar(getVar());
}
if ((filterFields != null && !filterFields.isEmpty())
|| (sortFields != null && !sortFields.isEmpty())) {
Modifiable modifiable = null;
if (dataModel instanceof Modifiable) {
modifiable = (Modifiable) dataModel;
} else if (dataModel instanceof ExtendedTableDataModel<?>) {
ExtendedTableDataModel<?> tableDataModel = (ExtendedTableDataModel<?>) dataModel;
//ExtendedTableDataModifiableModelOld<?> modifiableModel = new ExtendedTableDataModifiableModelOld(tableDataModel, getVar());
ExtendedTableDataModifiableModel<?> modifiableModel = new ExtendedTableDataModifiableModel(tableDataModel, getVar());
dataModel = modifiableModel;
modifiable = modifiableModel;
}
else {
ModifiableModel modifiableModel = new ModifiableModel(dataModel, getVar());
dataModel = modifiableModel;
modifiable = modifiableModel;
}
if (dataModel instanceof LocaleAware) {
FacesContext facesContext = getFacesContext();
if (ContextInitParameters.isDatatableUsesViewLocale(facesContext)) {
UIViewRoot viewRoot = facesContext.getViewRoot();
((LocaleAware) dataModel).setLocale(viewRoot.getLocale());
}
}
modifiable.modify(filterFields, sortFields);
}
return dataModel;
}
/**
* Original version of this method is defined in
* {@link org.ajax4jsf.component.UIDataAdaptor} and is called before
* RENDER_RESPONSE phase. In that version data model is reseted which causes
* need to sort and filter every time component is rendered.
*/
// @Override
protected void resetDataModel() {
// Do not reset only for ExtendedTableDataModel model
if (!(getDataModel() instanceof ExtendedTableDataModel<?>)) {
super.resetDataModel();
}
}
@SuppressWarnings("unchecked")
public 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;
}
@Override
public void beforeRenderResponse(FacesContext context) {
super.beforeRenderResponse(context);
for (Iterator<UIColumn> columns = getChildColumns(); columns
.hasNext();) {
UIColumn column = columns.next();
column.setId(column.getId());
column.setVisible(isColumnVisible(column));
}
UIColumn groupingColumn = getGroupByColumn();
if (groupingColumn != null) {// grouping is on
if (groupingColumn.getSortOrder().equals(Ordering.UNSORTED)) {
groupingColumn.setSortOrder(Ordering.ASCENDING);
}
}
}
@Override
public void processUpdates(FacesContext context){
super.processUpdates(context);
updateTableState(context);
}
/**
* Puts own state into component state.
*/
protected void updateTableState(FacesContext context){
ValueExpression ve = getValueExpression("tableState");
if ((null != ve) && (!ve.isReadOnly(context.getELContext()))) {
ve.setValue(context.getELContext(), getState().toString());
}
}//publishChanges
protected ExtendedDataTableState getState() {
if (state == null){
state = ExtendedDataTableState.getExtendedDataTableState(this);
}
return state;
}
}