org.ajax4jsf.ajax.repeat.UIDataAdaptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ajax4jsf Show documentation
Show all versions of ajax4jsf Show documentation
Ajax4jsf is an open source extension to the JavaServer Faces standard that adds AJAX capability to JSF applications without requiring the writing of any JavaScript.
The newest version!
/*
* Licensed under the Common Development and Distribution License,
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.sun.com/cddl/
*
* 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.ajax4jsf.ajax.repeat;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
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.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.render.Renderer;
import org.ajax4jsf.framework.ajax.AjaxEvent;
import org.ajax4jsf.framework.renderer.AjaxChildrenRenderer;
/**
* Base class for iterable components, like dataTable, Tomahawk dataList,
* Facelets repeat, tree etc., with support for partial rendering on AJAX
* responces for one or more selected iterations.
*
* @author shura
*
*/
public abstract class UIDataAdaptor extends UIData implements AjaxDataEncoder {
public final static DataModel EMPTY_MODEL = new ListDataModel(
Collections.EMPTY_LIST);
/**
* Base class for visit data model at phases decode, validation and update
* model
*
* @author shura
*
*/
protected abstract class ComponentVisitor implements DataVisitor {
public void process(FacesContext context, Object rowKey, Object argument)
throws IOException {
setRowKey(context,rowKey);
Iterator childIterator = dataChildren();
while (childIterator.hasNext()) {
UIComponent component = (UIComponent) childIterator.next();
processComponent(context, component, argument);
}
}
public abstract void processComponent(FacesContext context,
UIComponent c, Object argument);
}
/**
* Visitor for process decode on children components.
*/
protected ComponentVisitor decodeVisitor = new ComponentVisitor() {
public void processComponent(FacesContext context, UIComponent c,
Object argument) {
c.processDecodes(context);
}
};
/**
* Visitor for process validation phase
*/
protected ComponentVisitor validateVisitor = new ComponentVisitor() {
public void processComponent(FacesContext context, UIComponent c,
Object argument) {
c.processValidators(context);
}
};
/**
* Visitor for process update model phase.
*/
protected ComponentVisitor updateVisitor = new ComponentVisitor() {
public void processComponent(FacesContext context, UIComponent c,
Object argument) {
c.processUpdates(context);
}
};
/**
* Base client id's of this component, for wich invoked encode... methods.
* Component will save state and serialisable models for this keys only.
*/
private transient Set _encoded;
/**
* Storage for data model instances with different client id's of this
* component. In case of child for UIData component, this map will keep data
* models for different iterations between phases.
*/
private transient Map _modelsMap = new HashMap();
/**
* Reference for curent data model
*/
private transient ExtendedDataModel _currentModel = null;
/**
* States of this component for diferent iterations, same as for models.
*/
private Map _statesMap = new HashMap();
/**
* Reference for current component state.
*/
private transient DataComponentState _currentState = null;
/**
* Name of EL variable for current component state.
*/
private String _varState;
/**
* Key for current value in model.
*/
private Object _rowKey = null;
/**
* Values of row keys, encoded on ajax response rendering.
*/
private Set _ajaxKeys = null;
/**
* Get name of EL variable for component state.
*
* @return the varState
*/
public String getVarState() {
return _varState;
}
/**
* @param varStatus
* the varStatus to set
*/
public void setVarState(String varStatus) {
this._varState = varStatus;
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#getRowCount()
*/
public int getRowCount() {
return getExtendedDataModel().getRowCount();
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#getRowData()
*/
public Object getRowData() {
return getExtendedDataModel().getRowData();
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#isRowAvailable()
*/
public boolean isRowAvailable() {
return this.getExtendedDataModel().isRowAvailable();
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#setRowIndex(int)
*/
public void setRowIndex(int index) {
FacesContext faces = FacesContext.getCurrentInstance();
ExtendedDataModel localModel = getExtendedDataModel();
// if(key == localModel.getRowIndex()){
// return;
// }
// save child state
this.saveChildState(faces);
// Set current model row by int, but immediately get value from model.
// for compability, complex models must provide values map between
// integer and key value.
localModel.setRowIndex(index);
this._rowKey = localModel.getRowKey();
this._clientId = null;
boolean rowSelected = this._rowKey != null;
setupVariable(faces, localModel, rowSelected);
// restore child state
this.restoreChildState(faces);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#getRowIndex()
*/
public int getRowIndex() {
return getExtendedDataModel().getRowIndex();
}
/**
* Same as for int index, but for complex model key.
*
* @return
*/
public Object getRowKey() {
return this._rowKey;
}
public void setRowKey(Object key){
setRowKey(FacesContext.getCurrentInstance(), key);
}
/**
* Setup current roy by key. Perform same functionality as
* {@link UIData#setRowIndex(int)}, but for key object - it may be not only
* row number in sequence data, but, for example - path to current node in
* tree.
* @param faces - current FacesContext
* @param key new key value.
*/
public void setRowKey(FacesContext faces,Object key) {
ExtendedDataModel localModel = getExtendedDataModel();
// save child state
this.saveChildState(faces);
this._rowKey = key;
this._clientId = null;
localModel.setRowKey(key);
boolean rowSelected = key != null;
setupVariable(faces, localModel, rowSelected);
// restore child state
this.restoreChildState(faces);
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.ajax.repeat.AjaxDataEncoder#getAjaxKeys()
*/
public Set getAjaxKeys() {
if (this._ajaxKeys != null) {
return (this._ajaxKeys);
}
ValueBinding vb = getValueBinding("ajaxKeys");
if (vb != null) {
return (Set) (vb.getValue(getFacesContext()));
} else {
return (null);
}
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.ajax.repeat.AjaxDataEncoder#setAjaxKeys(java.util.Set)
*/
public void setAjaxKeys(Set ajaxKeys) {
this._ajaxKeys = ajaxKeys;
}
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.ajax.AjaxChildrenEncoder#encodeAjaxChild(javax.faces.context.FacesContext,
* java.lang.String, java.util.Set, java.util.Set)
*/
public void encodeAjaxChild(FacesContext context, String path, Set ids,
Set renderedAreas) throws IOException {
resetDataModel();
Renderer renderer = getRenderer(context);
if (null != renderer && renderer instanceof AjaxChildrenRenderer) {
// If renderer support partial encoding - call them.
AjaxChildrenRenderer childrenRenderer = (AjaxChildrenRenderer) renderer;
childrenRenderer.encodeAjaxChild(context, this, path, ids,
renderedAreas);
} else {
// Use simple ajax children encoding for iterate other keys.
AjaxChildrenRenderer childrenRenderer = getChildrenRenderer();
Set ajaxKeys = getAjaxKeys();
if (null != ajaxKeys) {
Object savedKey = getRowKey();
for (Iterator iter = ajaxKeys.iterator(); iter.hasNext();) {
Object key = (Object) iter.next();
setRowKey(key);
if (isRowAvailable()) {
childrenRenderer.encodeAjaxChild(context, this, path,
ids, renderedAreas);
}
}
setRowKey(savedKey);
} else {
if (isRowAvailable()) {
childrenRenderer.encodeAjaxChild(context, this, path, ids,
renderedAreas);
}
}
}
}
/**
* Instance of default renderer in ajax responses.
*/
private transient AjaxChildrenRenderer _childrenRenderer = null;
/**
* getter for simple {@link AjaxChildrenRenderer} instance in case of ajax
* responses. If default renderer not support search of children for encode
* in ajax response, component will use this instance by default.
*
* @return
*/
protected AjaxChildrenRenderer getChildrenRenderer() {
if (_childrenRenderer == null) {
_childrenRenderer = new AjaxChildrenRenderer() {
protected Class getComponentClass() {
return UIDataAdaptor.class;
}
};
}
return _childrenRenderer;
}
/**
* @return Set of values for clientId's of this component, for wich was
* invoked "encode" methods.
*/
protected Set getEncodedIds() {
if (_encoded == null) {
_encoded = new HashSet();
}
return _encoded;
}
/**
* Setup EL variable for different iteration. Value of row data and
* component state will be put into request scope attributes with names
* given by "var" and "varState" bean properties.
* @param faces current faces context
* @param localModel
* @param rowSelected
*/
protected void setupVariable(FacesContext faces, DataModel localModel, boolean rowSelected) {
String var = getVar();
if (var != null) {
Map attrs = faces.getExternalContext().getRequestMap();
if (rowSelected && isRowAvailable()) {
attrs.put(getVar(), localModel.getRowData());
} else {
attrs.remove(var);
}
}
}
/**
* Reset data model. this method must be called twice per request - before
* decode phase and before component encoding.
*/
protected void resetDataModel() {
this.setExtendedDataModel(null);
}
/**
* Set data model. Model value will be stored in Map with key as current
* clientId for this component, to keep models between phases for same
* iteration in case if this component child for other UIData
*
* @param model
*/
protected void setExtendedDataModel(ExtendedDataModel model) {
this._currentModel = model;
this._modelsMap.put(getBaseClientId(getFacesContext()), model);
}
/**
* Get current data model, or create it by {@link #createDataModel()}
* method. For different iterations in ancestor UIData ( if present ) will
* be returned different models.
*
* @return current data model.
*/
protected ExtendedDataModel getExtendedDataModel() {
if (this._currentModel == null) {
String baseClientId = getBaseClientId(getFacesContext());
ExtendedDataModel model = (ExtendedDataModel) this._modelsMap
.get(baseClientId);
if (null == model) {
model = createDataModel();
this._modelsMap.put(baseClientId, model);
}
this._currentModel = model;
}
return this._currentModel;
}
/**
* Hook mathod for create data model in concrete implementations.
*
* @return
*/
protected abstract ExtendedDataModel createDataModel();
/**
* Set current state ( at most cases, visual representation ) of this
* component. Same as for DataModel, component will keep states for
* different iterations.
*
* @param state
*/
protected void setComponentState(DataComponentState state) {
this._currentState = state;
this._statesMap.put(getBaseClientId(getFacesContext()),
this._currentState);
}
/**
* @return current state of this component.
*/
public DataComponentState getComponentState() {
if (this._currentState == null) {
String baseClientId = getBaseClientId(getFacesContext());
DataComponentState state = (DataComponentState) this._statesMap
.get(baseClientId);
if (null == state) {
state = createComponentState();
this._statesMap.put(baseClientId, state);
}
this._currentState = state;
}
return this._currentState;
}
/**
* Hook method for create default state in concrete implementations.
*
* @return
*/
protected abstract DataComponentState createComponentState();
private String _clientId = null;
public String getClientId(FacesContext faces) {
if (null == _clientId) {
StringBuffer id = new StringBuffer(getBaseClientId(faces));
Object rowKey = getRowKey();
if (rowKey != null) {
id.append(NamingContainer.SEPARATOR_CHAR).append(rowKey.toString());
}
Renderer renderer;
if (null != (renderer = getRenderer(faces))) {
_clientId = renderer.convertClientId(faces, id.toString());
} else {
_clientId = id.toString();
}
}
return _clientId;
}
private String _baseClientId = null;
/**
* Get base clietntId of this component ( withowt iteration part )
*
* @param faces
* @return
*/
public String getBaseClientId(FacesContext faces) {
// Return any previously cached client identifier
if (_baseClientId == null) {
// Search for an ancestor that is a naming container
UIComponent ancestorContainer = this;
StringBuffer parentIds = new StringBuffer();
while (null != (ancestorContainer = ancestorContainer.getParent())) {
if (ancestorContainer instanceof NamingContainer) {
parentIds.append(ancestorContainer.getClientId(faces))
.append(NamingContainer.SEPARATOR_CHAR);
break;
}
}
String id = getId();
if (null != id) {
_baseClientId = parentIds.append(id).toString();
} else {
_baseClientId = parentIds.append(
faces.getViewRoot().createUniqueId()).toString();
}
}
return (_baseClientId);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIComponentBase#setId(java.lang.String)
*/
public void setId(String id) {
// If component created by restoring tree or JSP, initial Id is null.
boolean haveId = null != super.getId();
super.setId(id);
_baseClientId = null;
_clientId = null;
if (haveId) {
// parent UIData ( if present ) will be set same Id at iteration -
// we use it for
// switch to different model and state.
String baseClientId = getBaseClientId(getFacesContext());
this._currentState = (DataComponentState) this._statesMap
.get(baseClientId);
this._currentModel = (ExtendedDataModel) this._modelsMap
.get(baseClientId);
if (null != this._currentModel) {
this._rowKey = this._currentModel.getRowKey();
// restoreChildState();
}
}
}
private transient Object origValue;
/**
* Save current state of data variable.
*/
public void captureOrigValue() {
captureOrigValue(FacesContext.getCurrentInstance());
}
/**
* Save current state of data variable.
* @param faces current faces context
*/
public void captureOrigValue(FacesContext faces) {
if (getVar() != null) {
Map attrs = faces.getExternalContext().getRequestMap();
this.origValue = attrs.get(getVar());
}
}
/**
* Restore value of data variable after processing phase.
*/
public void restoreOrigValue() {
restoreOrigValue(FacesContext.getCurrentInstance());
}
/**
* Restore value of data variable after processing phase.
* @param faces current faces context
*/
public void restoreOrigValue(FacesContext faces) {
if (getVar() != null) {
Map attrs = faces.getExternalContext().getRequestMap();
if (this.origValue != null) {
attrs.put(getVar(), this.origValue);
} else {
attrs.remove(getVar());
}
}
}
/**
* Saved values of {@link EditableValueHolder} fields per iterations.
*/
private transient Map childState;
/**
* @param faces
* @return Saved values of {@link EditableValueHolder} fields per
* iterations.
*/
protected Map getChildState(FacesContext faces) {
if (this.childState == null) {
this.childState = new HashMap();
}
String baseClientId = getBaseClientId(faces);
Map currentChildState = (Map) childState.get(baseClientId);
if (null == currentChildState) {
currentChildState = new HashMap();
childState.put(baseClientId, currentChildState);
}
return currentChildState;
}
/**
* Save values of {@link EditableValueHolder} fields before change current
* row.
* @param faces
*/
protected void saveChildState(FacesContext faces) {
Iterator itr = dataChildren();
while (itr.hasNext()) {
Map childState = this.getChildState(faces);
this.saveChildState(faces, (UIComponent) itr.next(), childState);
}
}
/**
* Recursive method for Iterate on children for save
* {@link EditableValueHolder} fields states.
*
* @param faces
* @param c
* @param childState
*/
private void saveChildState(FacesContext faces, UIComponent c,
Map childState) {
if (c instanceof EditableValueHolder && !c.isTransient()) {
String clientId = c.getClientId(faces);
SavedState ss = (SavedState) childState.get(clientId);
if (ss == null) {
ss = new SavedState();
childState.put(clientId, ss);
}
ss.populate((EditableValueHolder) c);
}
// continue hack
Iterator itr = c.getChildren().iterator();
while (itr.hasNext()) {
saveChildState(faces, (UIComponent) itr.next(), childState);
}
itr = c.getFacets().values().iterator();
while (itr.hasNext()) {
saveChildState(faces, (UIComponent) itr.next(), childState);
}
}
/**
* Restore values of {@link EditableValueHolder} fields after change current
* row.
* @param faces
*/
protected void restoreChildState(FacesContext faces) {
Iterator itr = dataChildren();
while (itr.hasNext()) {
Map childState = this.getChildState(faces);
this.restoreChildState(faces, (UIComponent) itr.next(), childState);
}
}
/**
* Recursive part of
* {@link #restoreChildState(FacesContext, UIComponent, Map)}
*
* @param faces
* @param c
* @param childState
*
*/
private void restoreChildState(FacesContext faces, UIComponent c,
Map childState) {
// reset id
String id = c.getId();
c.setId(id);
// hack
if (c instanceof EditableValueHolder) {
EditableValueHolder evh = (EditableValueHolder) c;
String clientId = c.getClientId(faces);
SavedState ss = (SavedState) childState.get(clientId);
if (ss != null) {
ss.apply(evh);
} else {
NullState.apply(evh);
}
}
// continue hack
Iterator itr = c.getChildren().iterator();
while (itr.hasNext()) {
restoreChildState(faces, (UIComponent) itr.next(), childState);
}
itr = c.getFacets().values().iterator();
while (itr.hasNext()) {
restoreChildState(faces, (UIComponent) itr.next(), childState);
}
}
/**
* Check for validation errors on children components. If true, saved values
* must be keep on render phase
*
* @param context
* @return
*/
private boolean keepSaved(FacesContext context) {
Iterator clientIds = this.getChildState(context).keySet().iterator();
while (clientIds.hasNext()) {
String clientId = (String) clientIds.next();
Iterator messages = context.getMessages(clientId);
while (messages.hasNext()) {
FacesMessage message = (FacesMessage) messages.next();
if (message.getSeverity()
.compareTo(FacesMessage.SEVERITY_ERROR) >= 0) {
return (true);
}
}
}
return false;
}
/**
* Perform iteration on all children components and all data rows with given
* visitor.
*
* @param faces
* @param visitor
*/
protected void iterate(FacesContext faces, ComponentVisitor visitor,
Object argument) {
// stop if not rendered
if (!this.isRendered())
return;
// reset rowIndex
this.captureOrigValue(faces);
this.setRowKey(faces,null);
try {
Iterator fixedChildren = fixedChildren();
while (fixedChildren.hasNext()) {
UIComponent component = (UIComponent) fixedChildren.next();
visitor.processComponent(faces, component, argument);
}
walk(faces, visitor, argument);
} catch (Exception e) {
throw new FacesException(e);
} finally {
this.setRowKey(faces,null);
this.restoreOrigValue(faces);
}
}
/**
* Walk ( visit ) this component on all data-avare children for each row.
*
* @param faces
* @param visitor
* @throws IOException
*/
public void walk(FacesContext faces, DataVisitor visitor, Object argument)
throws IOException {
getExtendedDataModel().walk(faces, visitor,
getComponentState().getRange(), argument);
}
protected void processDecodes(FacesContext faces, Object argument) {
if (!this.isRendered())
return;
this.resetComponent(faces);
this.iterate(faces, decodeVisitor, argument);
this.decode(faces);
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#processDecodes(javax.faces.context.FacesContext)
*/
public void processDecodes(FacesContext faces) {
processDecodes(faces, null);
}
/**
* Reset per-request fields in component.
*
* @param faces
*
*/
protected void resetComponent(FacesContext faces) {
// resetDataModel();
if (null != this.childState) {
childState.remove(getBaseClientId(faces));
}
this._encoded = null;
}
protected void processUpdates(FacesContext faces, Object argument) {
if (!this.isRendered())
return;
this.iterate(faces, updateVisitor, argument);
}
public void processUpdates(FacesContext faces) {
processUpdates(faces, null);
}
protected void processValidators(FacesContext faces, Object argument) {
if (!this.isRendered())
return;
this.iterate(faces, validateVisitor, argument);
}
public void processValidators(FacesContext faces) {
processValidators(faces, null);
}
public void encodeBegin(FacesContext context) throws IOException {
resetDataModel();
// if(!keepSaved(context)){
// childState.remove(getBaseClientId(context));
// }
// Mark component as used, if parent UIData change own range states not
// accessed at
// encode phase must be unsaved.
getEncodedIds().add(getBaseClientId(context));
// getComponentState().setUsed(true);
super.encodeBegin(context);
}
/**
* This method must create iterator for all non-data avare children of this
* component ( header/footer facets for components and columns in dataTable,
* facets for tree etc.
*
* @return iterator for all components not sensitive for row data.
*/
protected abstract Iterator fixedChildren();
/**
* This method must create iterator for all children components, processed
* "per row" It can be children of UIColumn in dataTable, nodes in tree
*
* @return iterator for all components processed per row.
*/
protected abstract Iterator dataChildren();
private final static SavedState NullState = new SavedState();
// from RI
/**
* This class keep values of {@link EditableValueHolder} row-sensitive
* fields.
*
* @author shura
*
*/
private final static class SavedState implements Serializable {
private Object submittedValue;
private static final long serialVersionUID = 2920252657338389849L;
Object getSubmittedValue() {
return (this.submittedValue);
}
void setSubmittedValue(Object submittedValue) {
this.submittedValue = submittedValue;
}
private boolean valid = true;
boolean isValid() {
return (this.valid);
}
void setValid(boolean valid) {
this.valid = valid;
}
private Object value;
Object getValue() {
return (this.value);
}
public void setValue(Object value) {
this.value = value;
}
private boolean localValueSet;
boolean isLocalValueSet() {
return (this.localValueSet);
}
public void setLocalValueSet(boolean localValueSet) {
this.localValueSet = localValueSet;
}
public String toString() {
return ("submittedValue: " + submittedValue + " value: " + value
+ " localValueSet: " + localValueSet);
}
public void populate(EditableValueHolder evh) {
this.value = evh.getValue();
this.valid = evh.isValid();
this.submittedValue = evh.getSubmittedValue();
this.localValueSet = evh.isLocalValueSet();
}
public void apply(EditableValueHolder evh) {
evh.setValue(this.value);
evh.setValid(this.valid);
evh.setSubmittedValue(this.submittedValue);
evh.setLocalValueSet(this.localValueSet);
}
}
/*
* (non-Javadoc)
*
* @see javax.faces.component.UIData#queueEvent(javax.faces.event.FacesEvent)
*/
public void queueEvent(FacesEvent event) {
if (event.getComponent() != this) {
event = new IndexedEvent(this, event, getRowKey());
}
// Send event directly to parent, to avoid wrapping in superclass.
UIComponent parent = getParent();
if (parent == null) {
throw new IllegalStateException(
"No parent component for queue event");
} else {
parent.queueEvent(event);
}
}
public void broadcast(FacesEvent event) throws AbortProcessingException {
if (!(event instanceof IndexedEvent)) {
if (!broadcastLocal(event)) {
super.broadcast(event);
}
return;
}
// Set up the correct context and fire our wrapped event
IndexedEvent revent = (IndexedEvent) event;
Object oldRowKey = getRowKey();
FacesContext faces = FacesContext.getCurrentInstance();
captureOrigValue(faces);
setRowKey(faces,revent.getKey());
FacesEvent rowEvent = revent.getTarget();
rowEvent.getComponent().broadcast(rowEvent);
// For Ajax events, keep row value.
if (!(rowEvent instanceof AjaxEvent)) {
setRowKey(faces,oldRowKey);
restoreOrigValue(faces);
}
return;
}
/**
* Process events targetted for concrete implementation. Hook method called
* from {@link #broadcast(FacesEvent)}
*
* @param event -
* processed event.
* @return true if event processed, false if component must continue
* processing.
*/
protected boolean broadcastLocal(FacesEvent event) {
return false;
}
/**
* Wrapper for event from child component, with value of current row key.
*
* @author shura
*
*/
protected static final class IndexedEvent extends FacesEvent {
private static final long serialVersionUID = -8318895390232552385L;
private final FacesEvent target;
private final Object key;
public IndexedEvent(UIDataAdaptor owner, FacesEvent target, Object key) {
super(owner);
this.target = target;
this.key = key;
}
public PhaseId getPhaseId() {
return (this.target.getPhaseId());
}
public void setPhaseId(PhaseId phaseId) {
this.target.setPhaseId(phaseId);
}
public boolean isAppropriateListener(FacesListener listener) {
return this.target.isAppropriateListener(listener);
}
public void processListener(FacesListener listener) {
UIDataAdaptor owner = (UIDataAdaptor) this.getComponent();
Object prevIndex = owner._rowKey;
try {
owner.setRowKey(this.key);
this.target.processListener(listener);
} finally {
owner.setRowKey(prevIndex);
}
}
public Object getKey() {
return key;
}
public FacesEvent getTarget() {
return target;
}
}
/**
* "memento" pattern class for state of component.
*
* @author shura
*
*/
private static class DataState implements Serializable {
/**
*
*/
private static final long serialVersionUID = 17070532L;
private Object superState;
private Map componentStates = new HashMap();
private Set ajaxKeys;
}
/**
* Serialisable model and component state per iteration of parent UIData.
*
* @author shura
*
*/
private static class PerIdState implements Serializable {
/**
*
*/
private static final long serialVersionUID = 9037454770537726418L;
private Object componentState;
private ExtendedDataModel model;
}
public void restoreState(FacesContext faces, Object object) {
DataState state = (DataState) object;
super.restoreState(faces, state.superState);
this._ajaxKeys = state.ajaxKeys;
this._statesMap = new HashMap();
// Restore serializable models and component states for all rows of
// parent UIData ( single if this
// component not child of iterable )
for (Iterator iter = state.componentStates.entrySet().iterator(); iter
.hasNext();) {
Map.Entry stateEntry = (Map.Entry) iter.next();
PerIdState idState = (PerIdState) stateEntry.getValue();
DataComponentState compState = createComponentState();
compState.restoreState(faces, idState.componentState);
Object key = stateEntry.getKey();
this._statesMap.put(key, compState);
this._modelsMap.put(key, idState.model);
}
}
public Object saveState(FacesContext faces) {
DataState state = new DataState();
state.superState = super.saveState(faces);
state.ajaxKeys = this._ajaxKeys;
// Save all states of component and data model for all valies of
// clientId, encoded in this request.
for (Iterator iter = this._statesMap.entrySet().iterator(); iter
.hasNext();) {
Map.Entry stateEntry = (Map.Entry) iter.next();
DataComponentState dataComponentState = ((DataComponentState) stateEntry
.getValue());
Set encodedIds = getEncodedIds();
Object stateKey = stateEntry.getKey();
if (encodedIds.isEmpty() || encodedIds.contains(stateKey)) {
PerIdState idState = new PerIdState();
idState.model = getExtendedDataModel().getSerializableModel(
dataComponentState.getRange());
idState.componentState = dataComponentState.saveState(faces);
if (null != idState.model || null != idState.componentState) {
state.componentStates.put(stateKey, idState);
}
}
}
return state;
}
}