You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2012/02/25 17:35:32 UTC
svn commit: r1293638 -
/myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java
Author: lu4242
Date: Sat Feb 25 16:35:32 2012
New Revision: 1293638
URL: http://svn.apache.org/viewvc?rev=1293638&view=rev
Log:
MYFACES-3463 Refactor UIRepeat code to implement PSS algorithm like UIData and fix state behavior AND
MYFACES-3415 [ui:repeat] field value disappears if validation error exists on current site
Modified:
myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java
Modified: myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java?rev=1293638&r1=1293637&r2=1293638&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java (original)
+++ myfaces/core/branches/2.0.x/impl/src/main/java/org/apache/myfaces/view/facelets/component/UIRepeat.java Sat Feb 25 16:35:32 2012
@@ -21,6 +21,7 @@ package org.apache.myfaces.view.facelets
import java.io.IOException;
import java.io.Serializable;
import java.sql.ResultSet;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -36,7 +37,6 @@ import javax.faces.component.EditableVal
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
-import javax.faces.component.UIData;
import javax.faces.component.UINamingContainer;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
@@ -57,7 +57,7 @@ import org.apache.myfaces.buildtools.mav
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
/**
- * TODO: PartialStateSaving and pluginize this component!
+ *
*/
@JSFComponent(name="ui:repeat", defaultRendererType="facelets.ui.Repeat")
public class UIRepeat extends UIComponentBase implements NamingContainer
@@ -69,34 +69,35 @@ public class UIRepeat extends UIComponen
private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
private final static DataModel<?> EMPTY_MODEL = new ListDataModel<Object>(Collections.emptyList());
+
+ private static final Class<Object[]> OBJECT_ARRAY_CLASS = Object[].class;
- private final static SavedState NULL_STATE = new SavedState();
-
- private Map<String, SavedState> _childState;
-
- // our data
- private Object _value;
+ private static final Object[] LEAF_NO_STATE = new Object[]{null,null};
+
+ private Object _initialDescendantComponentState = null;
- // variables
- private String _var;
+ // Holds for each row the states of the child components of this UIData.
+ // Note that only "partial" component state is saved: the component fields
+ // that are expected to vary between rows.
+ private Map<String, Collection<Object[]>> _rowStates = new HashMap<String, Collection<Object[]>>();
+
+ /**
+ * Handle case where this table is nested inside another table. See method getDataModel for more details.
+ * <p>
+ * Key: parentClientId (aka rowId when nested within a parent table) Value: DataModel
+ */
+ private Map<String, DataModel> _dataModelMap = new HashMap<String, DataModel>();
+ // will be set to false if the data should not be refreshed at the beginning of the encode phase
+ private boolean _isValidChilds = true;
+
private int _end = -1;
private int _count;
private int _index = -1;
- // scoping
- private int _offset = -1;
-
- private int _size = -1;
-
- private int _step = -1;
-
- private String _varStatus;
-
- private transient StringBuffer _buffer;
- private transient DataModel<?> _model;
+ private transient StringBuilder _clientIdBuffer;
private transient Object _origValue;
private transient Object _origVarStatus;
@@ -115,171 +116,153 @@ public class UIRepeat extends UIComponen
@JSFProperty
public int getOffset()
{
- if (_offset != -1)
- {
- return _offset;
- }
-
- ValueExpression ve = getValueExpression("offset");
- if (ve != null)
- {
- return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
- }
-
- return 0;
+ return (Integer) getStateHelper().eval(PropertyKeys.offset, 0);
}
public void setOffset(int offset)
{
- _offset = offset;
+ getStateHelper().put(PropertyKeys.offset, offset );
}
@JSFProperty
public int getSize()
{
- if (_size != -1)
- {
- return _size;
- }
-
- ValueExpression ve = getValueExpression("size");
- if (ve != null)
- {
- return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
- }
-
- return -1;
+ return (Integer) getStateHelper().eval(PropertyKeys.size, -1);
}
public void setSize(int size)
{
- _size = size;
+ getStateHelper().put(PropertyKeys.size, size );
}
@JSFProperty
public int getStep()
{
- if (_step != -1)
- {
- return _step;
- }
-
- ValueExpression ve = getValueExpression("step");
- if (ve != null)
- {
- return ((Integer)ve.getValue(getFacesContext().getELContext())).intValue();
- }
-
- return 1;
+ return (Integer) getStateHelper().eval(PropertyKeys.step, 1);
}
public void setStep(int step)
{
- _step = step;
+ getStateHelper().put(PropertyKeys.step, step );
}
- @JSFProperty
+ @JSFProperty(literalOnly=true)
public String getVar()
{
- return _var;
+ return (String) getStateHelper().get(PropertyKeys.var);
}
public void setVar(String var)
{
- _var = var;
+ getStateHelper().put(PropertyKeys.var, var );
}
- @JSFProperty
+ @JSFProperty(literalOnly=true)
public String getVarStatus ()
{
- return _varStatus;
+ return (String) getStateHelper().get(PropertyKeys.varStatus);
}
public void setVarStatus (String varStatus)
{
- _varStatus = varStatus;
+ getStateHelper().put(PropertyKeys.varStatus, varStatus );
}
- private synchronized void setDataModel(DataModel<?> model)
+ protected DataModel getDataModel()
{
- _model = model;
+ DataModel dataModel;
+ String clientID = "";
+
+ UIComponent parent = getParent();
+ if (parent != null)
+ {
+ clientID = parent.getContainerClientId(getFacesContext());
+ }
+ dataModel = _dataModelMap.get(clientID);
+ if (dataModel == null)
+ {
+ dataModel = createDataModel();
+ _dataModelMap.put(clientID, dataModel);
+ }
+ return dataModel;
}
+
+ private DataModel createDataModel()
+ {
+ Object value = getValue();
- @SuppressWarnings("unchecked")
- private synchronized DataModel<?> getDataModel()
+ if (value == null)
+ {
+ return EMPTY_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
+ {
+ return new ScalarDataModel(value);
+ }
+ }
+
+ @Override
+ public void setValueExpression(String name, ValueExpression binding)
{
- if (_model == null)
+ if (name == null)
{
- Object val = getValue();
- if (val == null)
- {
- _model = EMPTY_MODEL;
- }
- else if (val instanceof DataModel)
- {
- _model = (DataModel<?>) val;
- }
- else if (val instanceof List)
- {
- _model = new ListDataModel<Object>((List<Object>) val);
- }
- else if (Object[].class.isAssignableFrom(val.getClass()))
- {
- _model = new ArrayDataModel<Object>((Object[]) val);
- }
- else if (val instanceof ResultSet)
- {
- _model = new ResultSetDataModel((ResultSet) val);
- }
- else
- {
- _model = new ScalarDataModel(val);
- }
+ throw new NullPointerException("name");
}
- return _model;
+ else if (name.equals("value"))
+ {
+ _dataModelMap.clear();
+ }
+ else if (name.equals("rowIndex"))
+ {
+ throw new IllegalArgumentException("name " + name);
+ }
+ super.setValueExpression(name, binding);
}
@JSFProperty
public Object getValue()
{
- if (_value == null)
- {
- ValueExpression ve = getValueExpression("value");
- if (ve != null)
- {
- return ve.getValue(getFacesContext().getELContext());
- }
- }
-
- return _value;
+ return getStateHelper().eval(PropertyKeys.value);
}
public void setValue(Object value)
{
- _value = value;
+ getStateHelper().put(PropertyKeys.value, value);
+ _dataModelMap.clear();
+ _rowStates.clear();
+ _isValidChilds = true;
}
- /*
- @Override
- public String getClientId(FacesContext faces)
- {
- String id = super.getClientId(faces);
- if (_index >= 0)
- {
- id = _getBuffer().append(id).append(UINamingContainer.getSeparatorChar(faces)).append(_index).toString();
- }
- return id;
- }*/
-
@Override
- public String getContainerClientId(FacesContext faces)
+ public String getContainerClientId(FacesContext context)
{
- String id = super.getContainerClientId(faces);
- if (_index >= 0)
+ //MYFACES-2744 UIData.getClientId() should not append rowIndex, instead use UIData.getContainerClientId()
+ String clientId = super.getContainerClientId(context);
+
+ int index = getIndex();
+ if (index == -1)
{
- id = _getBuffer().append(id).append(UINamingContainer.getSeparatorChar(faces)).append(_index).toString();
+ return clientId;
}
- return id;
+
+ StringBuilder bld = _getBuffer(); //SharedStringBuilder(context);
+ return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(index).toString();
}
private RepeatStatus _getRepeatStatus()
@@ -290,36 +273,28 @@ public class UIRepeat extends UIComponen
private void _captureScopeValues()
{
- if (_var != null)
+ String var = getVar();
+ if (var != null)
{
- _origValue = getFacesContext().getExternalContext().getRequestMap().get(_var);
+ _origValue = getFacesContext().getExternalContext().getRequestMap().get(var);
}
- if (_varStatus != null)
+ String varStatus = getVarStatus();
+ if (varStatus != null)
{
- _origVarStatus = getFacesContext().getExternalContext().getRequestMap().get(_varStatus);
+ _origVarStatus = getFacesContext().getExternalContext().getRequestMap().get(varStatus);
}
}
- private StringBuffer _getBuffer()
+ private StringBuilder _getBuffer()
{
- if (_buffer == null)
+ if (_clientIdBuffer == null)
{
- _buffer = new StringBuffer();
+ _clientIdBuffer = new StringBuilder();
}
- _buffer.setLength(0);
-
- return _buffer;
- }
-
- private Map<String, SavedState> _getChildState()
- {
- if (_childState == null)
- {
- _childState = new HashMap<String, SavedState>();
- }
+ _clientIdBuffer.setLength(0);
- return _childState;
+ return _clientIdBuffer;
}
private boolean _isIndexAvailable()
@@ -327,176 +302,371 @@ public class UIRepeat extends UIComponen
return getDataModel().isRowAvailable();
}
- private boolean _isNestedInIterator()
- {
- UIComponent parent = getParent();
- while (parent != null)
- {
- if (parent instanceof UIData || parent instanceof UIRepeat)
- {
- return true;
- }
- parent = parent.getParent();
- }
- return false;
- }
-
- private boolean _keepSaved(FacesContext context)
- {
- for (String clientId : _getChildState().keySet())
- {
- // Perf: messages are instances of arrayList (or Collections.emptyList): see Method org.apache.myfaces.context.servlet.FacesContextImpl.addMessage(String, FacesMessage)
- List<FacesMessage> messageList = context.getMessageList(clientId);
- for (int i = 0, size = messageList.size(); i < size; i++)
- {
- FacesMessage message = messageList.get(i);
- if (message.getSeverity().compareTo(FacesMessage.SEVERITY_ERROR) >= 0)
- {
- return true;
- }
- }
- }
-
- return _isNestedInIterator();
- }
-
- private void _resetDataModel()
- {
- if (_isNestedInIterator())
- {
- setDataModel(null);
- }
- }
-
private void _restoreScopeValues()
{
- if (_var != null)
+ String var = getVar();
+ if (var != null)
{
Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
if (_origValue != null)
{
- attrs.put(_var, _origValue);
+ attrs.put(var, _origValue);
_origValue = null;
}
else
{
- attrs.remove(_var);
+ attrs.remove(var);
}
}
- if (_varStatus != null)
+ String varStatus = getVarStatus();
+ if (getVarStatus() != null)
{
Map<String, Object> attrs = getFacesContext().getExternalContext().getRequestMap();
if (_origVarStatus != null)
{
- attrs.put(_varStatus, _origVarStatus);
+ attrs.put(varStatus, _origVarStatus);
_origVarStatus = null;
}
else
{
- attrs.remove(_varStatus);
+ attrs.remove(varStatus);
}
}
}
- private void _restoreChildState()
+ /**
+ * Overwrite the state of the child components of this component with data previously saved by method
+ * saveDescendantComponentStates.
+ * <p>
+ * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
+ * not modified.
+ */
+ @SuppressWarnings("unchecked")
+ private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
+ boolean restoreChildFacets)
{
- if (getChildCount() > 0)
+ int descendantStateIndex = -1;
+ List<? extends Object[]> stateCollection = null;
+
+ if (iterateFacets && parent.getFacetCount() > 0)
{
- FacesContext context = getFacesContext();
- for (int i = 0, childCount = getChildCount(); i < childCount; i++)
+ Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
+
+ while (childIterator.hasNext())
{
- UIComponent child = getChildren().get(i);
- _restoreChildState(context, child);
+ UIComponent component = childIterator.next();
+
+ // reset the client id (see spec 3.1.6)
+ component.setId(component.getId());
+ if (!component.isTransient())
+ {
+ if (descendantStateIndex == -1)
+ {
+ stateCollection = ((List<? extends Object[]>) state);
+ descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
+ }
+
+ if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
+ {
+ Object[] object = stateCollection.get(descendantStateIndex);
+ if (object[0] != null && component instanceof EditableValueHolder)
+ {
+ ((SavedState) object[0]).restoreState((EditableValueHolder) component);
+ }
+ // If there is descendant state to restore, call it recursively, otherwise
+ // it is safe to skip iteration.
+ if (object[1] != null)
+ {
+ restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
+ descendantStateIndex++;
+ }
}
}
- }
-
- private void _restoreChildState(FacesContext faces, UIComponent c)
- {
- // reset id
- String id = c.getId();
- c.setId(id);
-
- // hack
- if (c instanceof EditableValueHolder)
+
+ if (parent.getChildCount() > 0)
{
- EditableValueHolder evh = (EditableValueHolder) c;
- String clientId = c.getClientId(faces);
- SavedState ss = _getChildState().get(clientId);
- if (ss != null)
+ for (int i = 0; i < parent.getChildCount(); i++)
{
- ss.apply(evh);
- }
- else
- {
- NULL_STATE.apply(evh);
+ UIComponent component = parent.getChildren().get(i);
+
+ // reset the client id (see spec 3.1.6)
+ component.setId(component.getId());
+ if (!component.isTransient())
+ {
+ if (descendantStateIndex == -1)
+ {
+ stateCollection = ((List<? extends Object[]>) state);
+ descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
+ }
+
+ if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
+ {
+ Object[] object = stateCollection.get(descendantStateIndex);
+ if (object[0] != null && component instanceof EditableValueHolder)
+ {
+ ((SavedState) object[0]).restoreState((EditableValueHolder) component);
+ }
+ // If there is descendant state to restore, call it recursively, otherwise
+ // it is safe to skip iteration.
+ if (object[1] != null)
+ {
+ restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
+ descendantStateIndex++;
+ }
}
}
+ }
- // continue hack
- if (c.getFacetCount() > 0)
+ /**
+ * Just call component.setId(component.getId()) to reset all client ids and
+ * ensure they will be calculated for the current row, but do not waste time
+ * dealing with row state code.
+ *
+ * @param parent
+ * @param iterateFacets
+ * @param restoreChildFacets
+ */
+ private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets,
+ boolean restoreChildFacets)
+ {
+ if (iterateFacets && parent.getFacetCount() > 0)
{
- for (UIComponent facet : c.getFacets().values())
+ Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
+
+ while (childIterator.hasNext())
{
- _restoreChildState(faces, facet);
+ UIComponent component = childIterator.next();
+
+ // reset the client id (see spec 3.1.6)
+ component.setId(component.getId());
+ if (!component.isTransient())
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
}
}
- int childCount = c.getChildCount();
- if (childCount > 0)
+
+ if (parent.getChildCount() > 0)
{
- for (int i = 0; i < childCount; i++)
+ for (int i = 0; i < parent.getChildCount(); i++)
{
- UIComponent child = c.getChildren().get(i);
- _restoreChildState(faces, child);
- }
- }
- }
+ UIComponent component = parent.getChildren().get(i);
- private void _saveChildState()
- {
- if (getChildCount() > 0)
- {
- FacesContext context = getFacesContext();
- for (int i = 0, childCount = getChildCount(); i < childCount; i++)
- {
- UIComponent child = getChildren().get(i);
- _saveChildState(context, child);
+ // reset the client id (see spec 3.1.6)
+ component.setId(component.getId());
+ if (!component.isTransient())
+ {
+ restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
+ }
}
}
}
- private void _saveChildState(FacesContext faces, UIComponent c)
+ /**
+ * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
+ * <p>
+ * This is very similar to the process that occurs for normal components when the view is serialized. Transient
+ * components are skipped (no state is saved for them).
+ * <p>
+ * If there are no children then null is returned. If there are one or more children, and all children are transient
+ * then an empty collection is returned; this will happen whenever a table contains only read-only components.
+ * <p>
+ * Otherwise a collection is returned which contains an object for every non-transient child component; that object
+ * may itself contain a collection of the state of that child's child components.
+ */
+ private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
+ boolean saveChildFacets)
{
- if (c instanceof EditableValueHolder && !c.isTransient())
+ Collection<Object[]> childStates = null;
+ // Index to indicate how many components has been passed without state to save.
+ int childEmptyIndex = 0;
+ int totalChildCount = 0;
+
+ if (iterateFacets && parent.getFacetCount() > 0)
{
- String clientId = c.getClientId(faces);
- SavedState ss = (SavedState) _getChildState().get(clientId);
- if (ss == null)
- {
- ss = new SavedState();
- _getChildState().put(clientId, ss);
- }
-
- ss.populate((EditableValueHolder) c);
- }
+ Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
- // continue hack
- if (c.getFacetCount() > 0)
- {
- for (UIComponent facet : c.getFacets().values())
+ while (childIterator.hasNext())
{
- _saveChildState(faces, facet);
+ UIComponent child = childIterator.next();
+ if (!child.isTransient())
+ {
+ // Add an entry to the collection, being an array of two
+ // elements. The first element is the state of the children
+ // of this component; the second is the state of the current
+ // child itself.
+
+ if (child instanceof EditableValueHolder)
+ {
+ if (childStates == null)
+ {
+ childStates = new ArrayList<Object[]>(
+ parent.getFacetCount()
+ + parent.getChildCount()
+ - totalChildCount
+ + childEmptyIndex);
+ for (int ci = 0; ci < childEmptyIndex; ci++)
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+
+ childStates.add(child.getChildCount() > 0 ?
+ new Object[]{new SavedState((EditableValueHolder) child),
+ saveDescendantComponentStates(child, saveChildFacets, true)} :
+ new Object[]{new SavedState((EditableValueHolder) child),
+ null});
+ }
+ else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
+ {
+ Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
+
+ if (descendantSavedState == null)
+ {
+ if (childStates == null)
+ {
+ childEmptyIndex++;
+ }
+ else
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ else
+ {
+ if (childStates == null)
+ {
+ childStates = new ArrayList<Object[]>(
+ parent.getFacetCount()
+ + parent.getChildCount()
+ - totalChildCount
+ + childEmptyIndex);
+ for (int ci = 0; ci < childEmptyIndex; ci++)
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ childStates.add(new Object[]{null, descendantSavedState});
+ }
+ }
+ else
+ {
+ if (childStates == null)
+ {
+ childEmptyIndex++;
+ }
+ else
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ }
+ totalChildCount++;
}
}
- int childCount = c.getChildCount();
- if (childCount > 0)
+
+ if (parent.getChildCount() > 0)
{
- for (int i = 0; i < childCount; i++)
+ for (int i = 0; i < parent.getChildCount(); i++)
{
- UIComponent child = c.getChildren().get(i);
- _saveChildState(faces, child);
+ UIComponent child = parent.getChildren().get(i);
+ if (!child.isTransient())
+ {
+ // Add an entry to the collection, being an array of two
+ // elements. The first element is the state of the children
+ // of this component; the second is the state of the current
+ // child itself.
+
+ if (child instanceof EditableValueHolder)
+ {
+ if (childStates == null)
+ {
+ childStates = new ArrayList<Object[]>(
+ parent.getFacetCount()
+ + parent.getChildCount()
+ - totalChildCount
+ + childEmptyIndex);
+ for (int ci = 0; ci < childEmptyIndex; ci++)
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+
+ childStates.add(child.getChildCount() > 0 ?
+ new Object[]{new SavedState((EditableValueHolder) child),
+ saveDescendantComponentStates(child, saveChildFacets, true)} :
+ new Object[]{new SavedState((EditableValueHolder) child),
+ null});
+ }
+ else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
+ {
+ Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
+
+ if (descendantSavedState == null)
+ {
+ if (childStates == null)
+ {
+ childEmptyIndex++;
+ }
+ else
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ else
+ {
+ if (childStates == null)
+ {
+ childStates = new ArrayList<Object[]>(
+ parent.getFacetCount()
+ + parent.getChildCount()
+ - totalChildCount
+ + childEmptyIndex);
+ for (int ci = 0; ci < childEmptyIndex; ci++)
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ childStates.add(new Object[]{null, descendantSavedState});
+ }
+ }
+ else
+ {
+ if (childStates == null)
+ {
+ childEmptyIndex++;
+ }
+ else
+ {
+ childStates.add(LEAF_NO_STATE);
+ }
+ }
+ }
+ totalChildCount++;
}
}
+
+ return childStates;
}
/**
@@ -519,7 +689,48 @@ public class UIRepeat extends UIComponen
private void _setIndex(int index)
{
// save child state
- _saveChildState();
+ //_saveChildState();
+ if (index < -1)
+ {
+ throw new IllegalArgumentException("rowIndex is less than -1");
+ }
+
+ if (_index == index)
+ {
+ return;
+ }
+
+ FacesContext facesContext = getFacesContext();
+
+ if (_index == -1)
+ {
+ if (_initialDescendantComponentState == null)
+ {
+ // Create a template that can be used to initialise any row
+ // that we haven't visited before, ie a "saved state" that can
+ // be pushed to the "restoreState" method of all the child
+ // components to set them up to represent a clean row.
+ _initialDescendantComponentState = saveDescendantComponentStates(this, true, true);
+ }
+ }
+ else
+ {
+ // If no initial component state, there are no EditableValueHolder instances,
+ // and that means there is no state to be saved for the current row, so we can
+ // skip row state saving code safely.
+ if (_initialDescendantComponentState != null)
+ {
+ // We are currently positioned on some row, and are about to
+ // move off it, so save the (partial) state of the components
+ // representing the current row. Later if this row is revisited
+ // then we can restore this state.
+ Collection<Object[]> savedRowState = saveDescendantComponentStates(this, false, false);
+ if (savedRowState != null)
+ {
+ _rowStates.put(getContainerClientId(facesContext), savedRowState);
+ }
+ }
+ }
_index = index;
@@ -528,20 +739,63 @@ public class UIRepeat extends UIComponen
if (_index != -1)
{
- if (_var != null && localModel.isRowAvailable())
+ String var = getVar();
+ if (var != null && localModel.isRowAvailable())
{
getFacesContext().getExternalContext().getRequestMap()
- .put(_var, localModel.getRowData());
+ .put(var, localModel.getRowData());
}
- if (_varStatus != null)
+ String varStatus = getVarStatus();
+ if (varStatus != null)
{
getFacesContext().getExternalContext().getRequestMap()
- .put(_varStatus, _getRepeatStatus());
+ .put(varStatus, _getRepeatStatus());
}
}
// restore child state
- _restoreChildState();
+ //_restoreChildState();
+
+ if (_index == -1)
+ {
+ // reset components to initial state
+ // If no initial state, skip row restore state code
+ if (_initialDescendantComponentState != null)
+ {
+ restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true);
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(this, true, true);
+ }
+ }
+ else
+ {
+ Object rowState = _rowStates.get(getContainerClientId(facesContext));
+ if (rowState == null)
+ {
+ // We haven't been positioned on this row before, so just
+ // configure the child components of this component with
+ // the standard "initial" state
+ // If no initial state, skip row restore state code
+ if (_initialDescendantComponentState != null)
+ {
+ restoreDescendantComponentStates(this, true, _initialDescendantComponentState, true);
+ }
+ else
+ {
+ restoreDescendantComponentWithoutRestoreState(this, true, true);
+ }
+ }
+ else
+ {
+ // We have been positioned on this row before, so configure
+ // the child components of this component with the (partial)
+ // state that was previously saved. Fields not in the
+ // partial saved state are left with their original values.
+ restoreDescendantComponentStates(this, true, rowState, true);
+ }
+ }
}
/**
@@ -591,7 +845,7 @@ public class UIRepeat extends UIComponen
if (step == -1)
{
- step = 1;
+ setStep(1);
}
if (step < 0)
@@ -607,7 +861,7 @@ public class UIRepeat extends UIComponen
}
_end = size;
- _step = step;
+ //_step = step;
}
public void process(FacesContext faces, PhaseId phase)
@@ -621,9 +875,6 @@ public class UIRepeat extends UIComponen
// validate attributes
_validateAttributes();
- // clear datamodel
- _resetDataModel();
-
// reset index
_captureScopeValues();
_setIndex(-1);
@@ -873,9 +1124,6 @@ public class UIRepeat extends UIComponen
// validate attributes
_validateAttributes();
- // clear datamodel
- _resetDataModel();
-
// reset index and save scope values
_captureScopeValues();
_setIndex(-1);
@@ -967,12 +1215,6 @@ public class UIRepeat extends UIComponen
return;
}
- setDataModel(null);
- if (!_keepSaved(faces))
- {
- _childState = null;
- }
-
process(faces, PhaseId.APPLY_REQUEST_VALUES);
decode(faces);
}
@@ -985,8 +1227,12 @@ public class UIRepeat extends UIComponen
return;
}
- _resetDataModel();
process(faces, PhaseId.UPDATE_MODEL_VALUES);
+
+ if (faces.getRenderResponse())
+ {
+ _isValidChilds = false;
+ }
}
@Override
@@ -997,8 +1243,13 @@ public class UIRepeat extends UIComponen
return;
}
- _resetDataModel();
process(faces, PhaseId.PROCESS_VALIDATIONS);
+
+ // check if an validation error forces the render response for our data
+ if (faces.getRenderResponse())
+ {
+ _isValidChilds = false;
+ }
}
// from RI
@@ -1010,6 +1261,14 @@ public class UIRepeat extends UIComponen
private Object _value;
private static final long serialVersionUID = 2920252657338389849L;
+
+ public SavedState(EditableValueHolder evh)
+ {
+ _value = evh.getLocalValue();
+ _localValueSet = evh.isLocalValueSet();
+ _valid = evh.isValid();
+ _submittedValue = evh.getSubmittedValue();
+ }
Object getSubmittedValue()
{
@@ -1056,6 +1315,14 @@ public class UIRepeat extends UIComponen
{
return ("submittedValue: " + _submittedValue + " value: " + _value + " localValueSet: " + _localValueSet);
}
+
+ public void restoreState(EditableValueHolder evh)
+ {
+ evh.setValue(_value);
+ evh.setValid(_valid);
+ evh.setSubmittedValue(_submittedValue);
+ evh.setLocalValueSet(_localValueSet);
+ }
public void populate(EditableValueHolder evh)
{
@@ -1153,7 +1420,6 @@ public class UIRepeat extends UIComponen
if (event instanceof IndexedEvent)
{
IndexedEvent idxEvent = (IndexedEvent) event;
- _resetDataModel();
// safe the current index, count aside
final int prevIndex = _index;
@@ -1223,32 +1489,89 @@ public class UIRepeat extends UIComponen
super.queueEvent(new IndexedEvent(this, event, _index));
}
+ // -=Leonardo Uribe=- At the moment I haven't found any use case that
+ // require to store the rowStates in the component state, mostly
+ // because EditableValueHolder instances render the value into the
+ // client and then this value are taken back at the beginning of the
+ // next request. So, I just let this code in comments just in case
+ // somebody founds an issue with this.
+ /*
@SuppressWarnings("unchecked")
@Override
- public void restoreState(FacesContext faces, Object object)
+ public void restoreState(FacesContext facesContext, Object state)
{
- Object[] state = (Object[]) object;
- super.restoreState(faces, state[0]);
- _childState = (Map<String, SavedState>) state[1];
- _offset = ((Integer) state[2]).intValue();
- _size = ((Integer) state[3]).intValue();
- _var = (String) state[4];
- _value = state[5];
- _varStatus = (String) state[6];
+ if (state == null)
+ {
+ return;
+ }
+
+ Object[] values = (Object[])state;
+ super.restoreState(facesContext,values[0]);
+ if (values[1] == null)
+ {
+ _rowStates.clear();
+ }
+ else
+ {
+ _rowStates = (Map<String, Collection<Object[]>>) restoreAttachedState(facesContext, values[1]);
+ }
}
@Override
- public Object saveState(FacesContext faces)
+ public Object saveState(FacesContext facesContext)
+ {
+ if (initialStateMarked())
+ {
+ Object parentSaved = super.saveState(facesContext);
+ if (parentSaved == null && _rowStates.isEmpty())
+ {
+ //No values
+ return null;
+ }
+ return new Object[]{parentSaved, saveAttachedState(facesContext, _rowStates)};
+ }
+ else
+ {
+ Object[] values = new Object[2];
+ values[0] = super.saveState(facesContext);
+ values[1] = saveAttachedState(facesContext, _rowStates);
+ return values;
+ }
+ }
+ */
+
+ @Override
+ public void encodeBegin(FacesContext context) throws IOException
{
- Object[] state = new Object[7];
- state[0] = super.saveState(faces);
- state[1] = _childState;
- state[2] = Integer.valueOf(_offset);
- state[3] = Integer.valueOf(_size);
- state[4] = _var;
- state[5] = _value;
- state[6] = _varStatus;
- return state;
+ _initialDescendantComponentState = null;
+ if (_isValidChilds && !hasErrorMessages(context))
+ {
+ // Clear the data model so that when rendering code calls
+ // getDataModel a fresh model is fetched from the backing
+ // bean via the value-binding.
+ _dataModelMap.clear();
+
+ // When the data model is cleared it is also necessary to
+ // clear the saved row state, as there is an implicit 1:1
+ // relation between objects in the _rowStates and the
+ // corresponding DataModel element.
+ _rowStates.clear();
+ }
+ // TODO Auto-generated method stub
+ super.encodeBegin(context);
+ }
+
+ private boolean hasErrorMessages(FacesContext context)
+ {
+ for (Iterator<FacesMessage> iter = context.getMessages(); iter.hasNext();)
+ {
+ FacesMessage message = iter.next();
+ if (FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
+ {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -1259,13 +1582,6 @@ public class UIRepeat extends UIComponen
return;
}
- setDataModel(null);
-
- if (!_keepSaved(faces))
- {
- _childState = null;
- }
-
process(faces, PhaseId.RENDER_RESPONSE);
}
@@ -1283,4 +1599,14 @@ public class UIRepeat extends UIComponen
return true;
}
+
+ enum PropertyKeys
+ {
+ value
+ , var
+ , size
+ , varStatus
+ , offset
+ , step
+ }
}