/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed 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 javax.faces.component;
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.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PhaseId;
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;
/**
* @author Manfred Geiler (latest modification by $Author: mbr $)
* @version $Revision: 290979 $ $Date: 2005-09-22 12:52:51 -0400 (Thu, 22 Sep 2005) $
*/
public class UIData extends UIComponentBase implements NamingContainer
{
private static final int STATE_SIZE = 5;
private static final int SUPER_STATE_INDEX = 0;
private static final int FIRST_STATE_INDEX = 1;
private static final int ROWS_STATE_INDEX = 2;
private static final int VALUE_STATE_INDEX = 3;
private static final int VAR_STATE_INDEX = 4;
private static final String FOOTER_FACET_NAME = "footer";
private static final String HEADER_FACET_NAME = "header";
private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
private static final int PROCESS_DECODES = 1;
private static final int PROCESS_VALIDATORS = 2;
private static final int PROCESS_UPDATES = 3;
// private static final Integer INTEGER_MINUS1 = new Integer(-1);
private int _rowIndex = -1;
private String _var = null;
// holds for each row the states of the child components of this UIData
private Map _rowStates = new HashMap();
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;
private Object _initialDescendantComponentState = null;
public void setFooter(UIComponent footer)
{
getFacets().put(FOOTER_FACET_NAME, footer);
}
public UIComponent getFooter()
{
return (UIComponent) getFacets().get(FOOTER_FACET_NAME);
}
public void setHeader(UIComponent header)
{
getFacets().put(HEADER_FACET_NAME, header);
}
public UIComponent getHeader()
{
return (UIComponent) getFacets().get(HEADER_FACET_NAME);
}
public boolean isRowAvailable()
{
return getDataModel().isRowAvailable();
}
public int getRowCount()
{
return getDataModel().getRowCount();
}
public Object getRowData()
{
return getDataModel().getRowData();
}
public int getRowIndex()
{
return _rowIndex;
}
public void setRowIndex(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(
getChildren().iterator(), false);
}
}
else
{
_rowStates.put(getClientId(facesContext),
saveDescendantComponentStates(getChildren().iterator(),
false));
}
_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(getChildren().iterator(),
_initialDescendantComponentState, false);
}
else
{
Object rowState = _rowStates.get(getClientId(facesContext));
if (rowState == null)
{
restoreDescendantComponentStates(getChildren().iterator(),
_initialDescendantComponentState, false);
}
else
{
restoreDescendantComponentStates(getChildren().iterator(),
rowState, false);
}
}
}
private 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 (component instanceof EditableValueHolder)
{
((EditableValueHolderState) childState)
.restoreState((EditableValueHolder) component);
}
Iterator childsIterator;
if (restoreChildFacets)
{
childsIterator = component.getFacetsAndChildren();
}
else
{
childsIterator = component.getChildren().iterator();
}
restoreDescendantComponentStates(childsIterator, descendantState,
true);
}
}
}
private 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;
}
public void setRows(int rows)
{
_rows = new Integer(rows);
if (rows < 0)
throw new IllegalArgumentException("rows: " + rows);
}
public void setVar(String var)
{
_var = var;
}
public String getVar()
{
return _var;
}
public void setValueBinding(String name, ValueBinding binding)
{
if (name == null)
{
throw new NullPointerException("name");
}
else if (name.equals("value"))
{
_dataModelMap.clear();
}
else if (name.equals("var") || name.equals("rowIndex"))
{
throw new IllegalArgumentException("name " + name);
}
super.setValueBinding(name, binding);
}
public String getClientId(FacesContext context)
{
String clientId = super.getClientId(context);
int rowIndex = getRowIndex();
if (rowIndex == -1)
{
return clientId;
}
return clientId + "_" + rowIndex;
}
public void queueEvent(FacesEvent event)
{
super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
}
public void broadcast(FacesEvent event) throws AbortProcessingException
{
if (event instanceof FacesEventWrapper)
{
FacesEvent originalEvent = ((FacesEventWrapper) event)
.getWrappedFacesEvent();
int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
int currentRowIndex = getRowIndex();
setRowIndex(eventRowIndex);
try
{
originalEvent.getComponent().broadcast(originalEvent);
}
finally
{
setRowIndex(currentRowIndex);
}
}
else
{
super.broadcast(event);
}
}
public void encodeBegin(FacesContext context) throws IOException
{
_initialDescendantComponentState = null;
if (_isValidChilds)
{
//Refresh DataModel for rendering:
_dataModelMap.clear();
_rowStates.clear();
}
super.encodeBegin(context);
}
/**
* @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
*/
public void encodeEnd(FacesContext context) throws IOException
{
setRowIndex(-1);
super.encodeEnd(context);
}
public void processDecodes(FacesContext context)
{
if (context == null)
throw new NullPointerException("context");
if (!isRendered())
return;
setRowIndex(-1);
processFacets(context, PROCESS_DECODES);
processColumnFacets(context, PROCESS_DECODES);
processColumnChildren(context, PROCESS_DECODES);
setRowIndex(-1);
try
{
decode(context);
}
catch (RuntimeException e)
{
context.renderResponse();
throw e;
}
}
public void processValidators(FacesContext context)
{
if (context == null)
throw new NullPointerException("context");
if (!isRendered())
return;
setRowIndex(-1);
processFacets(context, PROCESS_VALIDATORS);
processColumnFacets(context, PROCESS_VALIDATORS);
processColumnChildren(context, PROCESS_VALIDATORS);
setRowIndex(-1);
// check if an validation error forces the render response for our data
if (context.getRenderResponse())
{
_isValidChilds = false;
}
}
public void processUpdates(FacesContext context)
{
if (context == null)
throw new NullPointerException("context");
if (!isRendered())
return;
setRowIndex(-1);
processFacets(context, PROCESS_UPDATES);
processColumnFacets(context, PROCESS_UPDATES);
processColumnChildren(context, PROCESS_UPDATES);
setRowIndex(-1);
if (context.getRenderResponse())
{
_isValidChilds = false;
}
}
private void processFacets(FacesContext context, int processAction)
{
for (Iterator it = getFacets().values().iterator(); it.hasNext();)
{
UIComponent facet = (UIComponent) it.next();
process(context, facet, processAction);
}
}
private void processColumnFacets(FacesContext context, int processAction)
{
for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
{
UIComponent child = (UIComponent) childIter.next();
if (child instanceof UIColumn)
{
if (!child.isRendered())
{
//Column is not visible
continue;
}
for (Iterator facetsIter = child.getFacets().values()
.iterator(); facetsIter.hasNext();)
{
UIComponent facet = (UIComponent) facetsIter.next();
process(context, facet, processAction);
}
}
}
}
private void processColumnChildren(FacesContext context, int processAction)
{
int first = getFirst();
int rows = getRows();
int last;
if (rows == 0)
{
last = getRowCount();
}
else
{
last = first + rows;
}
for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
{
setRowIndex(rowIndex);
//scrolled past the last row
if (!isRowAvailable())
break;
for (Iterator it = getChildren().iterator(); it.hasNext();)
{
UIComponent child = (UIComponent) it.next();
if (child instanceof UIColumn)
{
if (!child.isRendered())
{
//Column is not visible
continue;
}
for (Iterator columnChildIter = child.getChildren()
.iterator(); columnChildIter.hasNext();)
{
UIComponent columnChild = (UIComponent) columnChildIter
.next();
process(context, columnChild, processAction);
}
}
}
}
}
private void process(FacesContext context, UIComponent component,
int processAction)
{
switch (processAction)
{
case PROCESS_DECODES:
component.processDecodes(context);
break;
case PROCESS_VALIDATORS:
component.processValidators(context);
break;
case PROCESS_UPDATES:
component.processUpdates(context);
break;
}
}
private DataModel getDataModel()
{
DataModel dataModel = null;
String clientID = "";
UIComponent parent = getParent();
if (parent != null) {
clientID = parent.getClientId(getFacesContext());
}
dataModel = (DataModel) _dataModelMap.get(clientID);
if (dataModel == null)
{
dataModel = createDataModel();
_dataModelMap.put(clientID, dataModel);
}
return dataModel;
}
/**
* Creates a new DataModel around the current value.
*/
private DataModel createDataModel()
{
Object value = getValue();
if (value == null)
{
return EMPTY_DATA_MODEL;
}
else if (value instanceof DataModel)
{
return (DataModel) value;
}
else if (value instanceof List)
{
return new ListDataModel((List) value);
}
else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
{
return new ArrayDataModel((Object[]) value);
}
else if (value instanceof ResultSet)
{
return new ResultSetDataModel((ResultSet) value);
}
else if (value instanceof Result)
{
return new ResultDataModel((Result) value);
}
else
{
return new ScalarDataModel(value);
}
}
private static class FacesEventWrapper extends FacesEvent
{
private static final long serialVersionUID = 6648047974065628773L;
private FacesEvent _wrappedFacesEvent;
private int _rowIndex;
public FacesEventWrapper(FacesEvent facesEvent, int rowIndex,
UIData redirectComponent)
{
super(redirectComponent);
_wrappedFacesEvent = facesEvent;
_rowIndex = rowIndex;
}
public PhaseId getPhaseId()
{
return _wrappedFacesEvent.getPhaseId();
}
public void setPhaseId(PhaseId phaseId)
{
_wrappedFacesEvent.setPhaseId(phaseId);
}
public void queue()
{
_wrappedFacesEvent.queue();
}
public String toString()
{
return _wrappedFacesEvent.toString();
}
public boolean isAppropriateListener(FacesListener faceslistener)
{
return _wrappedFacesEvent.isAppropriateListener(faceslistener);
}
public void processListener(FacesListener faceslistener)
{
_wrappedFacesEvent.processListener(faceslistener);
}
public FacesEvent getWrappedFacesEvent()
{
return _wrappedFacesEvent;
}
public int getRowIndex()
{
return _rowIndex;
}
}
private static final DataModel EMPTY_DATA_MODEL = new DataModel()
{
public boolean isRowAvailable()
{
return false;
}
public int getRowCount()
{
return 0;
}
public Object getRowData()
{
throw new IllegalArgumentException();
}
public int getRowIndex()
{
return -1;
}
public void setRowIndex(int i)
{
if (i < -1)
throw new IllegalArgumentException();
}
public Object getWrappedData()
{
return null;
}
public void setWrappedData(Object obj)
{
if (obj == null)
return; //Clearing is allowed
throw new UnsupportedOperationException(this.getClass().getName()
+ " UnsupportedOperationException");
}
};
public void setValue(Object value)
{
_value = value;
_dataModelMap.clear();
_rowStates.clear();
_isValidChilds = true;
}
public Object saveState(FacesContext context)
{
Object[] values = new Object[STATE_SIZE];
values[SUPER_STATE_INDEX] = super.saveState(context);
values[FIRST_STATE_INDEX] = _first;
values[ROWS_STATE_INDEX] = _rows;
values[VALUE_STATE_INDEX] = _value;
values[VAR_STATE_INDEX] = _var;
return values;
}
public void restoreState(FacesContext context, Object state)
{
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
_first = (Integer) values[FIRST_STATE_INDEX];
_rows = (Integer) values[ROWS_STATE_INDEX];
_value = values[VALUE_STATE_INDEX];
_var = (String) values[VAR_STATE_INDEX];
}
private class EditableValueHolderState
{
private final Object _value;
private final boolean _localValueSet;
private final boolean _valid;
private final Object _submittedValue;
public EditableValueHolderState(EditableValueHolder evh)
{
_value = evh.getLocalValue();
_localValueSet = evh.isLocalValueSet();
_valid = evh.isValid();
_submittedValue = evh.getSubmittedValue();
}
public void restoreState(EditableValueHolder evh)
{
evh.setValue(_value);
evh.setLocalValueSet(_localValueSet);
evh.setValid(_valid);
evh.setSubmittedValue(_submittedValue);
}
}
//------------------ GENERATED CODE BEGIN (do not modify!) --------------------
public static final String COMPONENT_TYPE = "javax.faces.Data";
public static final String COMPONENT_FAMILY = "javax.faces.Data";
private static final String DEFAULT_RENDERER_TYPE = "javax.faces.Table";
private static final int DEFAULT_FIRST = 0;
private static final int DEFAULT_ROWS = 0;
private Integer _first = null;
private Integer _rows = null;
private Object _value = null;
public UIData()
{
setRendererType(DEFAULT_RENDERER_TYPE);
}
public String getFamily()
{
return COMPONENT_FAMILY;
}
public void setFirst(int first)
{
if (first < 0) {
throw new IllegalArgumentException("Illegal value for first row: " + first);
}
_first = new Integer(first);
}
public int getFirst()
{
if (_first != null)
return _first.intValue();
ValueBinding vb = getValueBinding("first");
Number v = vb != null ? (Number) vb.getValue(getFacesContext()) : null;
return v != null ? v.intValue() : DEFAULT_FIRST;
}
public int getRows()
{
if (_rows != null)
return _rows.intValue();
ValueBinding vb = getValueBinding("rows");
Number v = vb != null ? (Number) vb.getValue(getFacesContext()) : null;
return v != null ? v.intValue() : DEFAULT_ROWS;
}
public Object getValue()
{
if (_value != null)
return _value;
ValueBinding vb = getValueBinding("value");
return vb != null ? (Object) vb.getValue(getFacesContext()) : null;
}
//------------------ GENERATED CODE END ---------------------------------------
}