You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2008/08/10 15:41:44 UTC
svn commit: r684498 -
/myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java
Author: skitching
Date: Sun Aug 10 06:41:43 2008
New Revision: 684498
URL: http://svn.apache.org/viewvc?rev=684498&view=rev
Log:
Clean up code to restore the comments lost during the 1.2.x period where this class was auto-generated via trinidad-faces-plugin.
Also restored sane order of variable and method declarations, cleaned up comments etc.
No logic changes made.
Modified:
myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java
Modified: myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java?rev=684498&r1=684497&r2=684498&view=diff
==============================================================================
--- myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java (original)
+++ myfaces/core/trunk_1.2.x/api/src/main/java/javax/faces/component/UIData.java Sun Aug 10 06:41:43 2008
@@ -35,37 +35,91 @@
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
/**
+ * Represents an abstraction of a component which has multiple "rows" of data.
+ * <p>
+ * The children of this component are expected to be UIColumn components.
+ * <p>
+ * Note that the same set of child components are reused to implement each row of the table in turn during
+ * such phases as apply-request-values and render-response. Altering any of the members of these components
+ * therefore affects the attribute for every row, except for the following members:
+ * <ul>
+ * <li>submittedValue
+ * <li>value (where no EL binding is used)
+ * <li>valid
+ * </ul>
+ * <p>
+ * This reuse of the child components also means that it is not possible to save a reference to a component
+ * during table processing, then access it later and expect it to still represent the same row of the table.
+ * <h1>
+ * Implementation Notes
+ * </h1>
+ * <p>
+ * Each of the UIColumn children of this component has a few component children of its own to render the contents
+ * of the table cell. However there can be a very large number of rows in a table, so it isn't efficient for the
+ * UIColumn and all its child objects to be duplicated for each row in the table. Instead the "flyweight" pattern
+ * is used where a serialized state is held for each row. When setRowIndex is invoked, the UIColumn objects and
+ * their children serialize their current state then reinitialise themselves from the appropriate saved state.
+ * This allows a single set of real objects to represent multiple objects which have the same types but potentially
+ * different internal state. When a row is selected for the first time, its state is set to a clean "initial" state.
+ * Transient components (including any read-only component) do not save their state; they are just reinitialised as
+ * required. The state saved/restored when changing rows is not the complete component state, just the fields that
+ * are expected to vary between rows: "submittedValue", "value", "isValid".
+ * </p>
+ * <p>
+ * Note that a table is a "naming container", so that components within the table have their ids prefixed with the
+ * id of the table. Actually, when setRowIndex has been called on a table with id of "zzz" the table pretends to
+ * its children that its ID is "zzz_n" where n is the row index. This means that renderers for child components which
+ * call component.getClientId automatically get ids of form "zzz_n:childId" thus ensuring that components in
+ * different rows of the table get different ids.
+ * </p>
+ * <p>
+ * When decoding a submitted page, this class iterates over all its possible rowIndex values, restoring the
+ * appropriate serialized row state then calling processDecodes on the child components. Because the child
+ * components (or their renderers) use getClientId to get the request key to look for parameter data, and because
+ * this object pretends to have a different id per row ("zzz_n") a single child component can decode data from each
+ * table row in turn without being aware that it is within a table. The table's data model is updated before each
+ * call to child.processDecodes, so the child decode method can assume that the data model's rowData points to the
+ * model object associated with the row currently being decoded. Exactly the same process applies for the later
+ * validation and updateModel phases.
+ * </p>
+ * <p>
+ * When the data model for the table is bound to a backing bean property, and no validation errors have occured
+ * during processing of a postback, the data model is refetched at the start of the rendering phase (ie after the
+ * update model phase) so that the contents of the data model can be changed as a result of the latest form
+ * submission. Because the saved row state must correspond to the elements within the data model, the row state
+ * must be discarded whenever a new data model is fetched; not doing this would cause all sorts of inconsistency
+ * issues. This does imply that changing the state of any of the members "submittedValue", "value" or "valid" of
+ * a component within the table during the invokeApplication phase has no effect on the rendering of the table.
+ * When a validation error has occurred, a new DataModel is <i>not</i> fetched, and the saved state of the child
+ * components is <i>not</i> discarded.
+ * </p>
+ * see Javadoc of the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
+ * for more information.
*
- * UIData is a base abstraction for components that holds Data.
+ * @JSFComponent
+ * type = "javax.faces.Data"
+ * family = "javax.faces.Data"
+ * desc = "UIData"
+ *
+ * @author Manfred Geiler (latest modification by $Author$)
+ * @version $Revision$ $Date$
*/
-@JSFComponent
-(defaultRendererType = "javax.faces.Table"
-)
+@JSFComponent(defaultRendererType = "javax.faces.Table")
public class UIData extends UIComponentBase
implements NamingContainer
{
+ public static final String COMPONENT_FAMILY = "javax.faces.Data";
+ static final String COMPONENT_TYPE = "javax.faces.Data"; // for unit tests
- static public final String COMPONENT_FAMILY =
- "javax.faces.Data";
- static public final String COMPONENT_TYPE =
- "javax.faces.Data";
-
- /**
- * Construct an instance of the UIData.
- */
- public UIData()
- {
- setRendererType("javax.faces.Table");
- }
- private static final String FOOTER_FACET_NAME = "footer";
+ 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 int _rowIndex = -1;
+ 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
@@ -86,31 +140,190 @@
private Object _initialDescendantComponentState = null;
+ private int _first;
+ private boolean _firstSet;
+ private int _rows;
+ private boolean _rowsSet;
+ private Object _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;
+ }
+
+ @Override
+ public PhaseId getPhaseId()
+ {
+ return _wrappedFacesEvent.getPhaseId();
+ }
+
+ @Override
+ public void setPhaseId(PhaseId phaseId)
+ {
+ _wrappedFacesEvent.setPhaseId(phaseId);
+ }
+
+ @Override
+ public void queue()
+ {
+ _wrappedFacesEvent.queue();
+ }
+
+ @Override
+ public String toString()
+ {
+ return _wrappedFacesEvent.toString();
+ }
+
+ @Override
+ public boolean isAppropriateListener(FacesListener faceslistener)
+ {
+ return _wrappedFacesEvent.isAppropriateListener(faceslistener);
+ }
+
+ @Override
+ 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()
+ {
+ @Override
+ public boolean isRowAvailable()
+ {
+ return false;
+ }
+
+ @Override
+ public int getRowCount()
+ {
+ return 0;
+ }
+
+ @Override
+ public Object getRowData()
+ {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public int getRowIndex()
+ {
+ return -1;
+ }
+
+ @Override
+ public void setRowIndex(int i)
+ {
+ if (i < -1)
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public Object getWrappedData()
+ {
+ return null;
+ }
+
+ @Override
+ public void setWrappedData(Object obj)
+ {
+ if (obj == null)
+ {
+ return; //Clearing is allowed
+ }
+ throw new UnsupportedOperationException(this.getClass().getName()
+ + " UnsupportedOperationException");
+ }
+ };
+
+ 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);
+ }
+ }
+
+ /**
+ * Construct an instance of the UIData.
+ */
+ public UIData()
+ {
+ setRendererType("javax.faces.Table");
+ }
+
@Override
public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException {
- if (context == null || clientId == null || callback == null) {
+ if (context == null || clientId == null || callback == null)
+ {
throw new NullPointerException();
}
//searching for this component?
boolean returnValue = this.getClientId(context).equals(clientId);
- if (returnValue) {
- try {
+ if (returnValue)
+ {
+ try
+ {
callback.invokeContextCallback(context, this);
- } catch (Exception e) {
+ }
+ catch (Exception e)
+ {
throw new FacesException(e);
}
return returnValue;
}
//Now Look throught facets on this UIComponent
- for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();) {
+ for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
+ {
returnValue = it.next().invokeOnComponent(context, clientId, callback);
}
if (returnValue == true)
+ {
return returnValue;
+ }
//Now we have to check if it is searching an inner component
String baseClientId = super.getClientId(context);
@@ -118,7 +331,8 @@
//First check if the clientId starts with the baseClientId of
//this component, to check if continue trying to find the component
//inside the children of this component.
- if (clientId.matches(baseClientId + ":[0-9]+:.*")) {
+ if (clientId.matches(baseClientId + ":[0-9]+:.*"))
+ {
String subId = clientId.substring(baseClientId.length() + 1);
String clientRow = subId.substring(0, subId.indexOf(':'));
@@ -130,7 +344,8 @@
//regular expresion
this.setRowIndex(Integer.parseInt(clientRow));
- for (Iterator<UIComponent> it1 = getChildren().iterator(); !returnValue && it1.hasNext();) {
+ for (Iterator<UIComponent> it1 = getChildren().iterator(); !returnValue && it1.hasNext();)
+ {
//recursive call to find the component
UIComponent child = it1.next();
returnValue = child.invokeOnComponent(context, clientId, callback);
@@ -139,7 +354,9 @@
//Restore the old position. Doing this prevent
//side effects.
this.setRowIndex(oldRow);
- } else {
+ }
+ else
+ {
//The component that matches this clientId must be outside
//of this component
return false;
@@ -428,7 +645,7 @@
{
return clientId;
}
-
+
StringBuilder bld = __getSharedStringBuilder();
return bld.append(clientId).append(NamingContainer.SEPARATOR_CHAR).append(rowIndex).toString();
}
@@ -541,9 +758,13 @@
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);
@@ -564,9 +785,15 @@
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);
@@ -584,9 +811,13 @@
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);
@@ -665,7 +896,9 @@
//scrolled past the last row
if (!isRowAvailable())
+ {
break;
+ }
for (Iterator it = getChildren().iterator(); it.hasNext();)
{
@@ -727,7 +960,8 @@
String clientID = "";
UIComponent parent = getParent();
- if (parent != null) {
+ if (parent != null)
+ {
clientID = parent.getClientId(getFacesContext());
}
dataModel = (DataModel) _dataModelMap.get(clientID);
@@ -791,65 +1025,35 @@
}
}
- private static class FacesEventWrapper extends FacesEvent
+ /**
+ * An EL expression that specifies the data model that backs this table.
+ * <p>
+ * The value referenced by the EL expression can be of any type.
+ * <p>
+ * <ul>
+ * <li>A value of type DataModel is used directly.</li>
+ * <li>Array-like parameters of type array-of-Object, java.util.List, java.sql.ResultSet
+ * or javax.servlet.jsp.jstl.sql.Result are wrapped in a corresponding DataModel that
+ * knows how to iterate over the elements.</li>
+ * <li>Other values are wrapped in a DataModel as a single row.</li>
+ * </ul>
+ * Note in particular that unordered collections, eg Set are not supported. Therefore if the
+ * value expression references such an object then the table will be considered to contain just
+ * one element - the collection itself.
+ */
+ @JSFProperty
+ public Object getValue()
{
- 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;
- }
-
- @Override
- public PhaseId getPhaseId()
- {
- return _wrappedFacesEvent.getPhaseId();
- }
-
- @Override
- public void setPhaseId(PhaseId phaseId)
- {
- _wrappedFacesEvent.setPhaseId(phaseId);
- }
-
- @Override
- public void queue()
- {
- _wrappedFacesEvent.queue();
- }
-
- @Override
- public String toString()
- {
- return _wrappedFacesEvent.toString();
- }
-
- @Override
- public boolean isAppropriateListener(FacesListener faceslistener)
- {
- return _wrappedFacesEvent.isAppropriateListener(faceslistener);
- }
-
- @Override
- public void processListener(FacesListener faceslistener)
- {
- _wrappedFacesEvent.processListener(faceslistener);
- }
-
- public FacesEvent getWrappedFacesEvent()
- {
- return _wrappedFacesEvent;
- }
-
- public int getRowIndex()
- {
- return _rowIndex;
- }
+ if (_value != null)
+ {
+ return _value;
+ }
+ ValueExpression expression = getValueExpression("value");
+ if (expression != null)
+ {
+ return expression.getValue(getFacesContext().getELContext());
+ }
+ return null;
}
public void setValue(Object value)
@@ -861,202 +1065,86 @@
}
/**
- * Set the maximum number of rows displayed in the table.
+ * Defines the index of the first row to be displayed, starting from 0.
*/
- public void setRows(int rows)
+ @JSFProperty
+ public int getFirst()
{
- if (rows < 0){
- throw new IllegalArgumentException("rows: " + rows);
- }
- _rows = rows;
- _rowsSet = true;
+ if (_firstSet)
+ {
+ return _first;
+ }
+ ValueExpression expression = getValueExpression("first");
+ if (expression != null)
+ {
+ return (Integer)expression.getValue(getFacesContext().getELContext());
+ }
+ return 0;
}
- /**
- * Set the index of the first row to be displayed, where 0 is the first row.
- */
public void setFirst(int first)
{
- if (first < 0) {
+ if (first < 0)
+ {
throw new IllegalArgumentException("Illegal value for first row: " + first);
}
_first = first;
_firstSet=true;
}
- private static final DataModel EMPTY_DATA_MODEL = new DataModel()
+ /**
+ * Defines the maximum number of rows of data to be displayed.
+ * <p>
+ * Specify zero to display all rows from the "first" row to the end
+ * of available data.
+ */
+ @JSFProperty
+ public int getRows()
{
- @Override
- public boolean isRowAvailable()
- {
- return false;
- }
-
- @Override
- public int getRowCount()
- {
- return 0;
- }
-
- @Override
- public Object getRowData()
- {
- throw new IllegalArgumentException();
- }
-
- @Override
- public int getRowIndex()
- {
- return -1;
- }
-
- @Override
- public void setRowIndex(int i)
- {
- if (i < -1)
- throw new IllegalArgumentException();
- }
-
- @Override
- public Object getWrappedData()
- {
- return null;
- }
-
- @Override
- public void setWrappedData(Object obj)
- {
- if (obj == null)
- return; //Clearing is allowed
- throw new UnsupportedOperationException(this.getClass().getName()
- + " UnsupportedOperationException");
- }
- };
+ if (_rowsSet)
+ {
+ return _rows;
+ }
+ ValueExpression expression = getValueExpression("rows");
+ if (expression != null)
+ {
+ return (Integer)expression.getValue(getFacesContext().getELContext());
+ }
+ return 0;
+ }
- private class EditableValueHolderState
+ /**
+ * Set the maximum number of rows displayed in the table.
+ */
+ public void setRows(int rows)
{
- private final Object _value;
- private final boolean _localValueSet;
- private final boolean _valid;
- private final Object _submittedValue;
-
- public EditableValueHolderState(EditableValueHolder evh)
+ if (rows < 0)
{
- _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);
+ throw new IllegalArgumentException("rows: " + rows);
}
+ _rows = rows;
+ _rowsSet = true;
}
- // Property: value
- private Object _value;
-
- /**
- * Gets An EL expression that specifies the data model that backs this table. The value can be of any type.
- *
- * A value of type DataModel is used directly. Array-like parameters of type java.util.List, array of Object,
- * java.sql.ResultSet, or javax.servlet.jsp.jstl.sql.Result are wrapped in a DataModel.
- *
- * Other values are wrapped in a DataModel as a single row.
- *
- * @return the new value value
- */
- @JSFProperty
- public Object getValue()
- {
- if (_value != null)
- {
- return _value;
- }
- ValueExpression expression = getValueExpression("value");
- if (expression != null)
- {
- return expression.getValue(getFacesContext().getELContext());
- }
- return null;
- }
-
- // Property: var
- private String _var;
-
/**
- * Gets Defines the name of the request-scope variable that will hold the current row during iteration. This value must be a static value.
- *
- * @return the new var value
+ * Defines the name of the request-scope variable that will hold the current row during iteration.
+ * <p>
+ * During rendering of child components of this UIData, the variable with this name can be read to
+ * learn what the "rowData" object for the row currently being rendered is.
+ * <p>
+ * This value must be a static value, ie an EL expression is not permitted.
*/
- @JSFProperty
- (literalOnly = true)
+ @JSFProperty(literalOnly = true)
public String getVar()
{
return _var;
}
- /**
- * Sets Defines the name of the request-scope variable that will hold the current row during iteration. This value must be a static value.
- *
- * @param var the new var value
- */
public void setVar(String var)
{
this._var = var;
}
- // Property: rows
- private int _rows;
- private boolean _rowsSet;
-
- /**
- * Gets The number of rows to be displayed. Specify zero for all remaining rows in the table.
- *
- * @return the new rows value
- */
- @JSFProperty
- public int getRows()
- {
- if (_rowsSet)
- {
- return _rows;
- }
- ValueExpression expression = getValueExpression("rows");
- if (expression != null)
- {
- return (Integer)expression.getValue(getFacesContext().getELContext());
- }
- return 0;
- }
-
- // Property: first
- private int _first;
- private boolean _firstSet;
-
- /**
- * Gets The index of the first row to be displayed, where 0 is the first row.
- *
- * @return the new first value
- */
- @JSFProperty
- public int getFirst()
- {
- if (_firstSet)
- {
- return _first;
- }
- ValueExpression expression = getValueExpression("first");
- if (expression != null)
- {
- return (Integer)expression.getValue(getFacesContext().getELContext());
- }
- return 0;
- }
@Override
public Object saveState(FacesContext facesContext)