org.apache.myfaces.component.html.ext.HtmlDataTableHack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tomahawk21 Show documentation
Show all versions of tomahawk21 Show documentation
JSF components and utilities that can be used with any JSF implementation.
This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build
changes to take advantage of JSF2.1 features. A JSF2.1 implementation is required to use this
version of the Tomahawk library.
The 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.component.html.ext;
import java.io.IOException;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.ResultDataModel;
import javax.faces.model.ResultSetDataModel;
import javax.faces.model.ScalarDataModel;
import javax.servlet.jsp.jstl.sql.Result;
import org.apache.commons.lang.StringUtils;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
import org.apache.myfaces.component.html.util.HtmlComponentUtils;
import org.apache.myfaces.custom.ExtendedComponentBase;
/**
* Reimplement all UIData functionality to be able to have (protected) access
* the internal DataModel.
*
* @JSFComponent
* configExcluded = "true"
*
* @author Manfred Geiler (latest modification by $Author: lu4242 $)
* @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (mié, 03 sep 2008) $
*/
public abstract class HtmlDataTableHack extends
javax.faces.component.html.HtmlDataTable implements
ExtendedComponentBase
{
@SuppressWarnings("unchecked")
private Map _dataModelMap = new HashMap();
// will be set to false if the data should not be refreshed at the beginning of the encode phase
private boolean _isValidChilds = true;
// holds for each row the states of the child components of this UIData
private Map _rowStates = new HashMap();
// holds delta state information
private Map > _rowDeltaStates = new HashMap >();
private Map > _rowTransientStates = new HashMap >();
// contains the initial row state which is used to initialize each row
private Object _initialDescendantComponentState = null;
private Object _initialDescendantFullComponentState = null;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Every field and method from here is identical to UIData !!!!!!!!!
// EXCEPTION: we can create a DataModel from a Collection
@SuppressWarnings("unchecked")
private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
private static final boolean DEFAULT_PRESERVEROWSTATES = false;
private static final String UNIQUE_ROW_ID_PREFIX = "r_id_";
private int _rowIndex = -1;
// BEGIN Reset mode saveState
/**
* Normal saveState behavior
*/
static final int RESET_MODE_OFF = 0;
/**
* Clear all transient values like _dataModelMap,_rowTransientStates and
* other variables used in the lifecycle (isValidChildren or cachedFacesContext
* for example)
*/
static final int RESET_MODE_SOFT = 1;
/**
* Clear all transient values and state set after markInitialState() call,
* like _rowDeltaStates
*/
static final int RESET_MODE_HARD = 2;
static final String RESET_SAVE_STATE_MODE_KEY =
"oam.view.resetSaveStateMode";
// END Reset mode saveState
public boolean isRowAvailable()
{
return getDataModel().isRowAvailable();
}
public int getRowCount()
{
return getDataModel().getRowCount();
}
public Object getRowData()
{
return getDataModel().getRowData();
}
public int getRowIndex()
{
return _rowIndex;
}
/**
* Hack since RI does not call getRowIndex()
*/
public String getClientId(FacesContext context)
{
// check for forceId
String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
if (clientId == null)
{
clientId = super.getClientId(context);
// remove index if necesssary
int rowIndex = getRowIndex();
if (rowIndex != -1)
{
char separator = UINamingContainer.getSeparatorChar(context);
int index = clientId.lastIndexOf(separator);
if(index != -1)
{
String rowIndexString = clientId.substring(index + 1);
if (rowIndexString.length() > 0 &&
StringUtils.isNumeric(rowIndexString) &&
rowIndex == Integer.valueOf(rowIndexString).intValue())
{
clientId = clientId.substring(0,index - 1);
}
}
}
}
return clientId;
}
@Override
public String getContainerClientId(FacesContext context)
{
// check for forceId
String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
if (clientId == null)
{
clientId = super.getClientId(context);
}
int rowIndex = getRowIndex();
if (rowIndex == -1)
{
return clientId;
}
// the following code tries to avoid rowindex to be twice in the client id
char separator = UINamingContainer.getSeparatorChar(context);
int index = clientId.lastIndexOf(separator);
if(index != -1)
{
String rowIndexString = clientId.substring(index + 1);
if(rowIndexString.length() > 0 &&
StringUtils.isNumeric(rowIndexString) &&
Integer.valueOf(rowIndexString) == rowIndex)
{
//return clientId;
return clientId.substring(0,index+1)+getDerivedSubClientId();
}
}
//return clientId + separator + rowIndex;
return clientId + separator + getDerivedSubClientId();
}
/**
* @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext)
*/
public void processUpdates(FacesContext context)
{
super.processUpdates(context);
// check if a update model error forces the render response for our data
if (context.getRenderResponse())
{
_isValidChilds = false;
}
}
/**
* This method is used when a custom processUpdates and processValidators
* is defined, to check if a update model error forces the render
* response for our data, because _isValidChilds is a private field
* and is not available on child components that inherits this
* component class like t:dataList. (TOMAHAWK-1225)
*/
protected void checkUpdateModelError(FacesContext context)
{
if (context.getRenderResponse())
{
_isValidChilds = false;
}
}
/**
* @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext)
*/
public void processValidators(FacesContext context)
{
super.processValidators(context);
// check if a validation error forces the render response for our data
if (context.getRenderResponse())
{
_isValidChilds = false;
}
}
/**
* @see javax.faces.component.UIData#encodeBegin(javax.faces.context.FacesContext)
*/
public void encodeBegin(FacesContext context) throws IOException
{
_initialDescendantComponentState = null;
if (_isValidChilds && !hasErrorMessages(context))
{
//Refresh DataModel for rendering:
_dataModelMap.clear();
if (!isPreserveRowStates())
{
_rowStates.clear();
}
}
super.encodeBegin(context);
}
public void setPreserveRowStates(boolean preserveRowStates)
{
getStateHelper().put(PropertyKeys.preserveRowStates, preserveRowStates);
}
/**
* Indicates whether the state for each row should not be
* discarded before the datatable is rendered again.
*
* Setting this to true might be hepful if an input
* component inside the datatable has no valuebinding and
* the value entered in there should be displayed again.
*
* This will only work reliable if the datamodel of the
* datatable did not change either by sorting, removing or
* adding rows. Default: false
*
* @JSFProperty
* defaultValue="false"
*/
public boolean isPreserveRowStates()
{
Object value = getStateHelper().eval(PropertyKeys.preserveRowStates,DEFAULT_PRESERVEROWSTATES);
if (value != null)
{
return (Boolean) value;
}
return DEFAULT_PRESERVEROWSTATES;
}
/**
* Indicates whether the state for a component in each row should not be
* discarded before the datatable is rendered again.
*
* In tomahawk, this property is the same as t:dataTable preserveRowComponentState
*
* This will only work reliable if the datamodel of the
* datatable did not change either by sorting, removing or
* adding rows. Default: false
*
* @return
*/
@JSFProperty(literalOnly=true, faceletsOnly=true, defaultValue="false")
public boolean isRowStatePreserved()
{
Boolean b = (Boolean) getStateHelper().get(PropertyKeys.rowStatePreserved);
return b == null ? false : b.booleanValue();
}
public void setRowStatePreserved(boolean preserveComponentState)
{
getStateHelper().put(PropertyKeys.rowStatePreserved, preserveComponentState);
}
protected boolean hasErrorMessages(FacesContext context)
{
for(Iterator iter = context.getMessages(); iter.hasNext();)
{
FacesMessage message = (FacesMessage) iter.next();
if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
{
return true;
}
}
return false;
}
/**
* @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
*/
public void encodeEnd(FacesContext context) throws IOException
{
setRowIndex(-1);
super.encodeEnd(context);
}
@SuppressWarnings("unchecked")
public void setRowIndex(int rowIndex)
{
if ( (isPreserveRowComponentState() || isRowStatePreserved()) && _initialDescendantFullComponentState != null)
{
setRowIndexPreserveComponentState(rowIndex);
}
else
{
setRowIndexWithoutPreserveComponentState(rowIndex);
}
}
private void setRowIndexWithoutPreserveComponentState(int rowIndex)
{
if (rowIndex < -1)
{
throw new IllegalArgumentException("rowIndex is less than -1");
}
if (_rowIndex == rowIndex)
{
return;
}
FacesContext facesContext = getFacesContext();
if (_rowIndex == -1)
{
if (_initialDescendantComponentState == null)
{
_initialDescendantComponentState = saveDescendantComponentStates();
}
}
else
{
_rowStates.put(getContainerClientId(facesContext),
saveDescendantComponentStates());
}
_rowIndex = rowIndex;
DataModel dataModel = getDataModel();
dataModel.setRowIndex(rowIndex);
String var = getVar();
if (rowIndex == -1)
{
if (var != null)
{
facesContext.getExternalContext().getRequestMap().remove(var);
}
}
else
{
if (var != null)
{
if (isRowAvailable())
{
Object rowData = dataModel.getRowData();
facesContext.getExternalContext().getRequestMap().put(var,
rowData);
}
else
{
facesContext.getExternalContext().getRequestMap().remove(
var);
}
}
}
if (_rowIndex == -1)
{
restoreDescendantComponentStates(_initialDescendantComponentState);
}
else
{
Object rowState = _rowStates.get(getContainerClientId(facesContext));
if (rowState == null)
{
restoreDescendantComponentStates(_initialDescendantComponentState);
}
else
{
restoreDescendantComponentStates(rowState);
}
}
}
private void setRowIndexPreserveComponentState(int rowIndex)
{
if (rowIndex < -1)
{
throw new IllegalArgumentException("rowIndex is less than -1");
}
if (_rowIndex == rowIndex)
{
return;
}
FacesContext facesContext = getFacesContext();
if (_initialDescendantFullComponentState != null)
{
//Just save the row
Map sm = saveFullDescendantComponentStates(facesContext);
if (sm != null && !sm.isEmpty())
{
_rowDeltaStates.put(getContainerClientId(facesContext), sm);
}
if (_rowIndex != -1)
{
_rowTransientStates.put(getContainerClientId(facesContext),
saveTransientDescendantComponentStates(facesContext));
}
}
_rowIndex = rowIndex;
DataModel dataModel = getDataModel();
dataModel.setRowIndex(rowIndex);
String var = getVar();
if (rowIndex == -1)
{
if (var != null)
{
facesContext.getExternalContext().getRequestMap().remove(var);
}
}
else
{
if (var != null)
{
if (isRowAvailable())
{
Object rowData = dataModel.getRowData();
facesContext.getExternalContext().getRequestMap().put(var,
rowData);
}
else
{
facesContext.getExternalContext().getRequestMap().remove(
var);
}
}
}
if (_initialDescendantFullComponentState != null)
{
Map rowState = _rowDeltaStates.get(getContainerClientId(facesContext));
if (rowState == null)
{
//Restore as original
restoreFullDescendantComponentStates(facesContext, _initialDescendantFullComponentState);
}
else
{
//Restore first original and then delta
restoreFullDescendantComponentDeltaStates(facesContext, rowState, _initialDescendantFullComponentState);
}
if (_rowIndex == -1)
{
restoreTransientDescendantComponentStates(facesContext, null);
}
else
{
rowState = _rowTransientStates.get(getContainerClientId(facesContext));
if (rowState == null)
{
restoreTransientDescendantComponentStates(facesContext, null);
}
else
{
restoreTransientDescendantComponentStates(facesContext, rowState);
}
}
}
}
@SuppressWarnings("unchecked")
protected void restoreDescendantComponentStates(Object state)
{
restoreDescendantComponentStates(getChildren().iterator(), state, false);
}
@SuppressWarnings("unchecked")
protected void restoreDescendantComponentStates(Iterator childIterator,
Object state, boolean restoreChildFacets)
{
Iterator descendantStateIterator = null;
while (childIterator.hasNext())
{
if (descendantStateIterator == null && state != null)
{
descendantStateIterator = ((Collection) state).iterator();
}
UIComponent component = (UIComponent) childIterator.next();
// reset the client id (see spec 3.1.6)
component.setId(component.getId());
if(!component.isTransient())
{
Object childState = null;
Object descendantState = null;
if (descendantStateIterator != null
&& descendantStateIterator.hasNext())
{
Object[] object = (Object[]) descendantStateIterator.next();
childState = object[0];
descendantState = object[1];
}
if (childState != null && component instanceof EditableValueHolder)
{
((EditableValueHolderState) childState)
.restoreState((EditableValueHolder) component);
}
Iterator childsIterator;
if (restoreChildFacets)
{
childsIterator = component.getFacetsAndChildren();
}
else
{
childsIterator = component.getChildren().iterator();
}
restoreDescendantComponentStates(childsIterator, descendantState,
true);
}
}
}
@SuppressWarnings("unchecked")
protected Object saveDescendantComponentStates()
{
return saveDescendantComponentStates(getChildren().iterator(), false);
}
@SuppressWarnings("unchecked")
protected Object saveDescendantComponentStates(Iterator childIterator,
boolean saveChildFacets)
{
Collection childStates = null;
while (childIterator.hasNext())
{
if (childStates == null)
{
childStates = new ArrayList();
}
UIComponent child = (UIComponent) childIterator.next();
if(!child.isTransient())
{
Iterator childsIterator;
if (saveChildFacets)
{
childsIterator = child.getFacetsAndChildren();
}
else
{
childsIterator = child.getChildren().iterator();
}
Object descendantState = saveDescendantComponentStates(
childsIterator, true);
Object state = null;
if (child instanceof EditableValueHolder)
{
state = new EditableValueHolderState(
(EditableValueHolder) child);
}
childStates.add(new Object[] { state, descendantState });
}
}
return childStates;
}
@Override
public void markInitialState()
{
if (isPreserveRowComponentState() || isRowStatePreserved())
{
if (getFacesContext().getAttributes().containsKey("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE"))
{
_initialDescendantFullComponentState = saveDescendantInitialComponentStates(getFacesContext());
}
}
super.markInitialState();
}
protected void restoreFullDescendantComponentStates(FacesContext facesContext, Object initialState)
{
restoreFullDescendantComponentStates(facesContext, getChildren().iterator(), initialState, false);
}
protected void restoreFullDescendantComponentStates(FacesContext facesContext,
Iterator childIterator, Object initialState,
boolean restoreChildFacets)
{
Iterator descendantStateIterator = null;
while (childIterator.hasNext())
{
if (descendantStateIterator == null && initialState != null)
{
descendantStateIterator = ((Collection) initialState)
.iterator();
}
UIComponent component = childIterator.next();
// reset the client id (see spec 3.1.6)
component.setId(component.getId());
if (!component.isTransient())
{
Object childState = null;
Object descendantState = null;
String childId = null;
if (descendantStateIterator != null
&& descendantStateIterator.hasNext())
{
do
{
Object[] object = descendantStateIterator.next();
childState = object[0];
descendantState = object[1];
childId = (String) object[2];
}
while(descendantStateIterator.hasNext() && !component.getId().equals(childId));
if (!component.getId().equals(childId))
{
// cannot apply initial state to components correctly.
throw new IllegalStateException("Cannot restore row correctly.");
}
}
component.clearInitialState();
component.restoreState(facesContext, childState);
component.markInitialState();
Iterator childsIterator;
if (restoreChildFacets)
{
childsIterator = component.getFacetsAndChildren();
}
else
{
childsIterator = component.getChildren().iterator();
}
restoreFullDescendantComponentStates(facesContext, childsIterator,
descendantState, true);
}
}
}
protected Collection