You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mb...@apache.org on 2005/07/19 12:17:51 UTC

svn commit: r219640 - in /myfaces: api/trunk/src/java/javax/faces/component/ tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/ tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/

Author: mbr
Date: Tue Jul 19 03:17:49 2005
New Revision: 219640

URL: http://svn.apache.org/viewcvs?rev=219640&view=rev
Log:
fixes several issues (JIRA-337, 224, 115) of the new the uidata implementation:

- uidata holds all datamodels in a map to support nested uidatas 
  (clientid of uidata´s parent is used as a key)

- rowstate is stored in a map and is identified by the current 
  client id of the uidata

- an inital rowstate is used to initialize each row with its start values

- it is not necessary to call comp.setId(comp.getId()) to reset the client id.

Modified:
    myfaces/api/trunk/src/java/javax/faces/component/UIData.java
    myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTable.java
    myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
    myfaces/tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/UIColumns.java

Modified: myfaces/api/trunk/src/java/javax/faces/component/UIData.java
URL: http://svn.apache.org/viewcvs/myfaces/api/trunk/src/java/javax/faces/component/UIData.java?rev=219640&r1=219639&r2=219640&view=diff
==============================================================================
--- myfaces/api/trunk/src/java/javax/faces/component/UIData.java (original)
+++ myfaces/api/trunk/src/java/javax/faces/component/UIData.java Tue Jul 19 03:17:49 2005
@@ -16,7 +16,6 @@
 package javax.faces.component;
 
 import java.io.IOException;
-import java.io.Serializable;
 import java.sql.ResultSet;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -45,15 +44,13 @@
  */
 public class UIData extends UIComponentBase implements NamingContainer
 {
-	private static final int STATE_SIZE = 8;
+	private static final int STATE_SIZE = 6;
 	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 int ROW_STATE_INDEX = 5;
-	private static final int DATAMODEL_STATE_INDEX = 6;
-	private static final int VALID_CHILDS_STATE_INDEX = 7;
+	private static final int VALID_CHILDS_STATE_INDEX = 5;
 
 	private static final String FOOTER_FACET_NAME = "footer";
 	private static final String HEADER_FACET_NAME = "header";
@@ -67,17 +64,17 @@
 	private int _rowIndex = -1;
 	private String _var = null;
 
-	// holds the states of the child components of this UIData 
-	private Map _rowState = new HashMap();
+	// holds for each row the states of the child components of this UIData 
+	private Map _rowStates = null;
 
-	private DataModel _dataModel = null;
+	// contains the initial row state which is used to initialize each row
+	private Object _initialRowState = null;
+
+	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;
 
-	// will be set to false if the _rowstate and _datamodel should not be saved
-	private boolean _processUIDataState = true;
-
 	public void setFooter(UIComponent footer)
 	{
 		getFacets().put(FOOTER_FACET_NAME, footer);
@@ -127,7 +124,18 @@
 
 		FacesContext facesContext = getFacesContext();
 
-		saveDescendantComponentStates(facesContext, getFacetsAndChildren());
+		if (_rowIndex != -1)
+		{
+			if (_rowStates == null)
+			{
+				_rowStates = new HashMap();
+			}
+			_rowStates.put(getClientId(facesContext), processSaveRowState(facesContext));
+		}
+		else if (_initialRowState == null)
+		{
+			_initialRowState = processSaveRowState(facesContext);
+		}
 
 		_rowIndex = rowIndex;
 
@@ -158,43 +166,128 @@
 			}
 		}
 
-		restoreDescendantComponentStates(facesContext, getFacetsAndChildren());
+		if (_rowIndex != -1 && _rowStates != null)
+		{
+			Object state = _rowStates.get(getClientId(facesContext));
+			if (state == null)
+			{
+				state = _initialRowState;
+			}
+			processRestoreRowState(facesContext, state);
+		}
+		else
+		{
+			processRestoreRowState(facesContext, _initialRowState);
+		}
+	}
+
+	private Object processSaveRowState(FacesContext context)
+	{
+		if (isTransient())
+			return null;
+		List childStates = null;
+		int columnCount = getChildCount();
+		if (columnCount > 0)
+		{
+			for (Iterator colIt = getChildren().iterator(); colIt.hasNext();)
+			{
+				UIComponent child = (UIComponent) colIt.next();
+				if (!child.isTransient())
+				{
+					if (childStates == null)
+					{
+						childStates = new ArrayList(columnCount);
+					}
+					if (child instanceof UIColumn)
+					{
+						childStates.add(new Object[] {child.saveState(context),
+										getColumnChildsState(context, child.getChildren().iterator())});
+					}
+					else
+					{
+						childStates.add(child.processSaveState(context));
+					}
+				}
+			}
+		}
+		return childStates;
 	}
 
-	private void saveDescendantComponentStates(FacesContext context, Iterator childIterator)
+	private Object getColumnChildsState(FacesContext context, Iterator compIterator)
 	{
-		while (childIterator.hasNext())
+		List result = null;
+		while (compIterator.hasNext())
 		{
-			UIComponent child = (UIComponent) childIterator.next();
-			if (!child.isTransient())
+			UIComponent colChild = (UIComponent) compIterator.next();
+			if (!colChild.isTransient())
 			{
-				_rowState.put(child.getClientId(context), new ChildStateHolder(context, child));
-				if (!(child instanceof UIData))
+				if (result == null)
 				{
-					saveDescendantComponentStates(context, child.getFacetsAndChildren());
+					result = new ArrayList();
 				}
+				result.add(colChild.processSaveState(context));
 			}
 		}
+		return result;
 	}
 
-	private void restoreDescendantComponentStates(FacesContext context, Iterator childIterator)
+	public void processRestoreRowState(FacesContext context, Object state)
 	{
-		while (childIterator.hasNext())
+		List childrenStates = (List) state;
+		int childCount = getChildCount();
+		if (childrenStates != null && childCount > 0)
 		{
-			UIComponent child = (UIComponent) childIterator.next();
-			// the spec 1.1 (3.1.6) says: setting the id will recalculate the clientid  
-			child.setId(child.getId());
-			if (!child.isTransient())
+			int idx = 0;
+			for (Iterator it = getChildren().iterator(); it.hasNext();)
 			{
-				String clientId = child.getClientId(context);
-				ChildStateHolder childStateHolder = (ChildStateHolder) _rowState.get(clientId);
-				if (childStateHolder != null)
+				UIComponent child = (UIComponent) it.next();
+				if (!child.isTransient())
 				{
-					childStateHolder.restoreState(context, child);
+					Object childState = childrenStates.get(idx++);
+					if (childState != null)
+					{
+						if (child instanceof UIColumn)
+						{
+							Object[] columnState = (Object[]) childState;
+							setColumnChildsState(context, child.getChildren().iterator(), columnState[1]);
+							child.restoreState(context, columnState[0]);
+						}
+						else
+						{
+							child.processRestoreState(context, childState);
+						}
+					}
+					else
+					{
+						context.getExternalContext().log(
+										"No state found to restore child of component " + getId());
+					}
 				}
-				if (!(child instanceof UIData))
+			}
+		}
+	}
+
+	private void setColumnChildsState(FacesContext context, Iterator compIterator, Object state)
+	{
+		List childrenList = (List) state;
+		if (childrenList != null)
+		{
+			int idx = 0;
+			while (compIterator.hasNext())
+			{
+				UIComponent child = (UIComponent) compIterator.next();
+				if (!child.isTransient())
 				{
-					restoreDescendantComponentStates(context, child.getFacetsAndChildren());
+					Object childState = childrenList.get(idx++);
+					if (childState != null)
+					{
+						child.processRestoreState(context, childState);
+					}
+					else
+					{
+						context.getExternalContext().log(
+										"No state found to restore child of component " + getId());
+					}
 				}
 			}
 		}
@@ -225,7 +318,8 @@
 		}
 		else if (name.equals("value"))
 		{
-			_dataModel = null;
+			_dataModelMap.clear();
+			_rowStates = null;
 		}
 		else if (name.equals("var") || name.equals("rowIndex"))
 		{
@@ -272,11 +366,15 @@
 		if (_isValidChilds)
 		{
 			//Refresh DataModel for rendering:
-			_dataModel = null;
+			_dataModelMap.clear();
+			_rowStates = null;
 		}
 		super.encodeBegin(context);
 	}
 
+	/**
+	 * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
+	 */
 	public void encodeEnd(FacesContext context) throws IOException
 	{
 		setRowIndex(-1);
@@ -363,8 +461,7 @@
 					//Column is not visible
 					continue;
 				}
-				for (Iterator facetsIter = child.getFacets().values().iterator(); facetsIter
-								.hasNext();)
+				for (Iterator facetsIter = child.getFacets().values().iterator(); facetsIter.hasNext();)
 				{
 					UIComponent facet = (UIComponent) facetsIter.next();
 					process(context, facet, processAction);
@@ -431,11 +528,14 @@
 
 	private DataModel getDataModel()
 	{
-		if (_dataModel == null)
+		String clientID = getParent().getClientId(getFacesContext());
+		DataModel dataModel = (DataModel) _dataModelMap.get(clientID);
+		if (dataModel == null)
 		{
-			_dataModel = createDataModel();
+			dataModel = createDataModel();
+			_dataModelMap.put(clientID, dataModel);
 		}
-		return _dataModel;
+		return dataModel;
 	}
 
 	/**
@@ -573,47 +673,12 @@
 		}
 	};
 
-	private static class ChildStateHolder implements Serializable
-	{
-		private final Object _state;
-
-		public ChildStateHolder(FacesContext context, StateHolder stateHolder)
-		{
-			_state = stateHolder.saveState(context);
-		}
-
-		public void restoreState(FacesContext context, StateHolder stateHolder)
-		{
-			stateHolder.restoreState(context, _state);
-		}
-	}
-
 	public void setValue(Object value)
 	{
 		_value = value;
-		_dataModel = null;
-	}
-
-	/**
-	 * @see javax.faces.component.UIComponentBase#processSaveState(javax.faces.context.FacesContext)
-	 */
-	public Object processSaveState(FacesContext context)
-	{
-		// state of uidata will not be processed
-		_processUIDataState = false;
-		Object state = super.processSaveState(context);
-		_processUIDataState = true;
-		return state;
-	}
-
-	/**
-	 * @see javax.faces.component.UIComponentBase#processRestoreState(javax.faces.context.FacesContext, java.lang.Object)
-	 */
-	public void processRestoreState(FacesContext context, Object state)
-	{
-		_processUIDataState = false;
-		super.processRestoreState(context, state);
-		_processUIDataState = true;
+		_dataModelMap.clear();
+		_rowStates = null;
+		_isValidChilds = true;
 	}
 
 	public Object saveState(FacesContext context)
@@ -624,13 +689,7 @@
 		values[ROWS_STATE_INDEX] = _rows;
 		values[VALUE_STATE_INDEX] = _value;
 		values[VAR_STATE_INDEX] = _var;
-
-		if (_processUIDataState)
-		{
-			values[ROW_STATE_INDEX] = _rowState;
-			values[DATAMODEL_STATE_INDEX] = _dataModel;
-			values[VALID_CHILDS_STATE_INDEX] = Boolean.valueOf(_isValidChilds);
-		}
+		values[VALID_CHILDS_STATE_INDEX] = Boolean.valueOf(_isValidChilds);
 		return values;
 	}
 
@@ -640,33 +699,17 @@
 		super.restoreState(context, values[0]);
 		_first = (Integer) values[FIRST_STATE_INDEX];
 		_rows = (Integer) values[ROWS_STATE_INDEX];
-		_value = (Object) values[VALUE_STATE_INDEX];
+		_value = values[VALUE_STATE_INDEX];
 		_var = (String) values[VAR_STATE_INDEX];
 
-		if (_processUIDataState)
+		Boolean validChilds = (Boolean) values[VALID_CHILDS_STATE_INDEX];
+		if (validChilds != null)
 		{
-			Map rowState = (Map) values[ROW_STATE_INDEX];
-			if (rowState != null)
-			{
-				_rowState = rowState;
-			}
-			else
-			{
-				_rowState.clear();
-			}
-
-			_dataModel = (DataModel) values[DATAMODEL_STATE_INDEX];
-
-			Boolean validChilds = (Boolean) values[VALID_CHILDS_STATE_INDEX];
-			if (validChilds != null)
-			{
-				_isValidChilds = validChilds.booleanValue();
-			}
-			else
-			{
-				// defaults to true
-				_isValidChilds = true;
-			}
+			_isValidChilds = validChilds.booleanValue();
+		}
+		else
+		{
+			_isValidChilds = true;
 		}
 	}
 

Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTable.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTable.java?rev=219640&r1=219639&r2=219640&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTable.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTable.java Tue Jul 19 03:17:49 2005
@@ -24,8 +24,6 @@
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import javax.faces.el.ValueBinding;
-import javax.faces.event.AbortProcessingException;
-import javax.faces.event.FacesEvent;
 import javax.faces.model.DataModel;
 
 import org.apache.commons.logging.Log;
@@ -50,7 +48,7 @@
 	private static final boolean DEFAULT_SORTASCENDING = true;
 	private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
 
-	private transient boolean _isDataModelRestored = false;
+	private _SerializableDataModel _preservedDataModel;
 
 	private String _sortColumn = null;
 	private Boolean _sortAscending = null;
@@ -59,13 +57,6 @@
 
 	private boolean _isValidChilds = true;
 
-	public void setValue(Object value)
-	{
-		_dataModel = null;
-		_isDataModelRestored = false;
-		super.setValue(value);
-	}
-
 	public void setRowIndex(int rowIndex)
 	{
 		String rowIndexVar = getRowIndexVar();
@@ -130,11 +121,6 @@
 		}
 	}
 
-	public void processRestoreState(FacesContext context, Object state)
-	{
-		super.processRestoreState(context, state);
-	}
-
 	public void processDecodes(FacesContext context)
 	{
 		if (!isRendered())
@@ -143,8 +129,7 @@
 		}
 		super.processDecodes(context);
 		setRowIndex(-1);
-		processColumnsFacets(context, PROCESS_DECODES);
-		processColumnsChildren(context, PROCESS_DECODES);
+		processColumns(context, PROCESS_DECODES);
 		setRowIndex(-1);
 	}
 
@@ -152,76 +137,14 @@
 	 * @param context
 	 * @param processAction
 	 */
-	private void processColumnsChildren(FacesContext context, int processAction)
+	private void processColumns(FacesContext context, int processAction)
 	{
-		int first = getFirst();
-		int rows = getRows();
-		int last;
-		if (rows == 0)
+		for (Iterator it = getChildren().iterator(); it.hasNext();)
 		{
-			last = getRowCount();
-		}
-		else
-		{
-			last = first + rows;
-		}
-		for (int rowIndex = first; rowIndex < last; rowIndex++)
-		{
-			setRowIndex(rowIndex);
-			if (isRowAvailable())
-			{
-				for (Iterator it = getChildren().iterator(); it.hasNext();)
-				{
-					UIComponent child = (UIComponent) it.next();
-					if (child instanceof UIColumns)
-					{
-						if (child.isRendered())
-						{
-							UIColumns columns = (UIColumns) child;
-							for (int colIndex = 0, size = columns.getRowCount(); colIndex < size; colIndex++)
-							{
-								columns.setRowIndex(colIndex);
-								for (Iterator columnChildIter = child.getChildren().iterator(); columnChildIter
-												.hasNext();)
-								{
-									UIComponent columnChild = (UIComponent) columnChildIter.next();
-									process(context, columnChild, processAction);
-								}
-							}
-							columns.setRowIndex(-1);
-						}
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * @param context
-	 * @param processAction
-	 */
-	private void processColumnsFacets(FacesContext context, int processAction)
-	{
-		for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
-		{
-			UIComponent child = (UIComponent) childIter.next();
+			UIComponent child = (UIComponent) it.next();
 			if (child instanceof UIColumns)
 			{
-				if (child.isRendered())
-				{
-					UIColumns columns = (UIColumns) child;
-					for (int i = 0, size = columns.getRowCount(); i < size; i++)
-					{
-						columns.setRowIndex(i);
-						for (Iterator facetsIter = child.getFacets().values().iterator(); facetsIter
-										.hasNext();)
-						{
-							UIComponent facet = (UIComponent) facetsIter.next();
-							process(context, facet, processAction);
-						}
-					}
-					columns.setRowIndex(-1);
-				}
+				process(context, child, processAction);
 			}
 		}
 	}
@@ -248,11 +171,9 @@
 		{
 			return;
 		}
-		setRowIndex(-1);
-		processColumnsFacets(context, PROCESS_VALIDATORS);
-		processColumnsChildren(context, PROCESS_VALIDATORS);
-		setRowIndex(-1);
 		super.processValidators(context);
+		processColumns(context, PROCESS_VALIDATORS);
+		setRowIndex(-1);
 
 		if (context.getRenderResponse())
 		{
@@ -260,37 +181,24 @@
 		}
 	}
 
-	public Object processSaveState(FacesContext context)
-	{
-		return super.processSaveState(context);
-	}
-
-	public void broadcast(FacesEvent event) throws AbortProcessingException
-	{
-		super.broadcast(event);
-	}
-
 	public void processUpdates(FacesContext context)
 	{
 		if (!isRendered())
 		{
 			return;
 		}
-		setRowIndex(-1);
-		processColumnsFacets(context, PROCESS_UPDATES);
-		processColumnsChildren(context, PROCESS_UPDATES);
-		setRowIndex(-1);
-
 		super.processUpdates(context);
+		processColumns(context, PROCESS_UPDATES);
+		setRowIndex(-1);
 
-		if (_isDataModelRestored)
+		if (isPreserveDataModel())
 		{
 			updateModelFromPreservedDataModel(context);
 		}
 
 		if (context.getRenderResponse())
 		{
-                    _isValidChilds = false;
+			_isValidChilds = false;
 		}
 	}
 
@@ -299,7 +207,7 @@
 		ValueBinding vb = getValueBinding("value");
 		if (vb != null && !vb.isReadOnly(context))
 		{
-			_SerializableDataModel dm = (_SerializableDataModel) _dataModel;
+			_SerializableDataModel dm = (_SerializableDataModel) getDataModel();
 			Class type = vb.getType(context);
 			if (DataModel.class.isAssignableFrom(type))
 			{
@@ -333,67 +241,9 @@
 				}
 			}
 		}
-		_dataModel = null;
-		_isDataModelRestored = false;
+		_preservedDataModel = null;
 	}
 
-//        (this is not called from anywhere)
-//	/**
-//	 * TODO: We could perhaps optimize this if we know we are derived from MyFaces UIData implementation
-//	 */
-//	private boolean isAllChildrenAndFacetsValid()
-//	{
-//		int first = getFirst();
-//		int rows = getRows();
-//		int last;
-//		if (rows == 0)
-//		{
-//			last = getRowCount();
-//		}
-//		else
-//		{
-//			last = first + rows;
-//		}
-//		try
-//		{
-//			for (int rowIndex = first; rowIndex < last; rowIndex++)
-//			{
-//				setRowIndex(rowIndex);
-//				if (isRowAvailable())
-//				{
-//					if (!isAllEditableValueHoldersValidRecursive(getFacetsAndChildren()))
-//					{
-//						return false;
-//					}
-//				}
-//			}
-//		}
-//		finally
-//		{
-//			setRowIndex(-1);
-//		}
-//		return true;
-//	}
-//
-	
-//  (this is not called from anywhere)
-//	private boolean isAllEditableValueHoldersValidRecursive(Iterator facetsAndChildrenIterator)
-//	{
-//		while (facetsAndChildrenIterator.hasNext())
-//		{
-//			UIComponent c = (UIComponent) facetsAndChildrenIterator.next();
-//			if (c instanceof EditableValueHolder && !((EditableValueHolder) c).isValid())
-//			{
-//				return false;
-//			}
-//			if (!isAllEditableValueHoldersValidRecursive(c.getFacetsAndChildren()))
-//			{
-//				return false;
-//			}
-//		}
-//		return true;
-//	}
-
 	public void encodeBegin(FacesContext context) throws IOException
 	{
 		if (!isRendered())
@@ -401,12 +251,20 @@
 
 		if (_isValidChilds)
 		{
-			_isDataModelRestored = false;
+			_preservedDataModel = null;
 		}
 
 		if (isRenderedIfEmpty() || getRowCount() > 0)
 		{
 			super.encodeBegin(context);
+			for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+			{
+				UIComponent component = (UIComponent) iter.next();
+				if (component instanceof UIColumns)
+				{
+					((UIColumns) component).encodeTableBegin(context);
+				}
+			}
 		}
 	}
 
@@ -429,15 +287,23 @@
 		if (isRenderedIfEmpty() || getRowCount() > 0)
 		{
 			super.encodeEnd(context);
+			for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+			{
+				UIComponent component = (UIComponent) iter.next();
+				if (component instanceof UIColumns)
+				{
+					((UIColumns) component).encodeTableEnd(context);
+				}
+			}
 		}
 	}
 
 	public int getFirst()
 	{
-		if (_isDataModelRestored)
+		if (_preservedDataModel != null)
 		{
 			//Rather get the currently restored DataModel attribute
-			return ((_SerializableDataModel) _dataModel).getFirst();
+			return ((_SerializableDataModel) _preservedDataModel).getFirst();
 		}
 		else
 		{
@@ -447,20 +313,20 @@
 
 	public void setFirst(int first)
 	{
-		if (_isDataModelRestored)
+		if (_preservedDataModel != null)
 		{
 			//Also change the currently restored DataModel attribute
-			((_SerializableDataModel) _dataModel).setFirst(first);
+			((_SerializableDataModel) _preservedDataModel).setFirst(first);
 		}
 		super.setFirst(first);
 	}
 
 	public int getRows()
 	{
-		if (_isDataModelRestored)
+		if (_preservedDataModel != null)
 		{
 			//Rather get the currently restored DataModel attribute
-			return ((_SerializableDataModel) _dataModel).getRows();
+			return ((_SerializableDataModel) _preservedDataModel).getRows();
 		}
 		else
 		{
@@ -470,10 +336,10 @@
 
 	public void setRows(int rows)
 	{
-		if (_isDataModelRestored)
+		if (_preservedDataModel != null)
 		{
 			//Also change the currently restored DataModel attribute
-			((_SerializableDataModel) _dataModel).setRows(rows);
+			((_SerializableDataModel) _preservedDataModel).setRows(rows);
 		}
 		super.setRows(rows);
 	}
@@ -493,19 +359,32 @@
 			values[2] = null;
 		}
 		values[3] = _preserveSort;
-                values[4] = _sortColumn;
+		values[4] = _sortColumn;
 		values[5] = _sortAscending;
 		values[6] = _renderedIfEmpty;
 		values[7] = _rowCountVar;
 		values[8] = _rowIndexVar;
 		values[9] = _rowOnMouseOver;
 		values[10] = _rowOnMouseOut;
-                values[11] = preserveSort ? getSortColumn() : null;
-                values[12] = preserveSort ? Boolean.valueOf(isSortAscending()) : null;
 
+		values[11] = preserveSort ? getSortColumn() : null;
+		values[12] = preserveSort ? Boolean.valueOf(isSortAscending()) : null;
 		return values;
 	}
 
+	/**
+	 * @see org.apache.myfaces.component.html.ext.HtmlDataTableHack#getDataModel()
+	 */
+	protected DataModel getDataModel()
+	{
+		if (_preservedDataModel != null)
+		{
+			setDataModel(_preservedDataModel);
+			_preservedDataModel = null;
+		}
+		return super.getDataModel();
+	}
+
 	public void restoreState(FacesContext context, Object state)
 	{
 		Object values[] = (Object[]) state;
@@ -513,13 +392,11 @@
 		_preserveDataModel = (Boolean) values[1];
 		if (isPreserveDataModel())
 		{
-			_dataModel = (_SerializableDataModel) restoreAttachedState(context, values[2]);
-			_isDataModelRestored = true;
+			_preservedDataModel = (_SerializableDataModel) restoreAttachedState(context, values[2]);
 		}
 		else
 		{
-			_dataModel = null;
-			_isDataModelRestored = false;
+			_preservedDataModel = null;
 		}
 		_preserveSort = (Boolean) values[3];
 		_sortColumn = (String) values[4];
@@ -530,41 +407,42 @@
 		_rowOnMouseOver = (String) values[9];
 		_rowOnMouseOut = (String) values[10];
 
-                if (isPreserveSort())
-                {
-                    String sortColumn = (String) values[11];
-                    Boolean sortAscending = (Boolean) values[12];
-                    if (sortColumn != null && sortAscending != null)
-                    {
-                        ValueBinding vb = getValueBinding("sortColumn");
-                        if (vb != null && !vb.isReadOnly(context))
-                        {
-                            vb.setValue(context, sortColumn);
-                        }
-
-                        vb = getValueBinding("sortAscending");
-                        if (vb != null && !vb.isReadOnly(context))
-                        {
-                            vb.setValue(context, sortAscending);
-                        }
-                    }
+		if (isPreserveSort())
+		{
+			String sortColumn = (String) values[11];
+			Boolean sortAscending = (Boolean) values[12];
+			if (sortColumn != null && sortAscending != null)
+			{
+				ValueBinding vb = getValueBinding("sortColumn");
+				if (vb != null && !vb.isReadOnly(context))
+				{
+					vb.setValue(context, sortColumn);
+				}
+
+				vb = getValueBinding("sortAscending");
+				if (vb != null && !vb.isReadOnly(context))
+				{
+					vb.setValue(context, sortAscending);
+				}
+			}
 		}
 	}
 
 	public _SerializableDataModel getSerializableDataModel()
 	{
-		if (_dataModel != null)
+		DataModel dm = getDataModel();
+		if (dm instanceof _SerializableDataModel)
 		{
-			if (_dataModel instanceof _SerializableDataModel)
-			{
-				return (_SerializableDataModel) _dataModel;
-			}
-			else
-			{
-				return new _SerializableDataModel(getFirst(), getRows(), _dataModel);
-			}
+			return (_SerializableDataModel) dm;
 		}
+		return createSerializableDataModel();
+	}
 
+	/**
+	 * @return
+	 */
+	private _SerializableDataModel createSerializableDataModel()
+	{
 		Object value = getValue();
 		if (value == null)
 		{

Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java?rev=219640&r1=219639&r2=219640&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java Tue Jul 19 03:17:49 2005
@@ -1,16 +1,30 @@
+/*
+ * 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 org.apache.myfaces.component.html.ext;
 
 import java.io.IOException;
-import java.io.Serializable;
 import java.sql.ResultSet;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import javax.faces.component.StateHolder;
+import javax.faces.component.UIColumn;
 import javax.faces.component.UIComponent;
-import javax.faces.component.UIData;
 import javax.faces.context.FacesContext;
 import javax.faces.el.ValueBinding;
 import javax.faces.model.ArrayDataModel;
@@ -30,358 +44,407 @@
  */
 abstract class HtmlDataTableHack extends javax.faces.component.html.HtmlDataTable
 {
-	protected DataModel _dataModel = null;
+  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;
+  // will be set to false if the data should not be refreshed at the beginning of the encode phase
+  private boolean _isValidChilds = true;
 
-	private Map _rowState = new HashMap();
+  // holds for each row the states of the child components of this UIData 
+  private Map _rowStates = null;
 
-	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-	// Every field and method from here is identical to UIData !!!!!!!!!
-
-	private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
-
-	private int _rowIndex = -1;
-
-	// will be set to false if the _rowstate and _datamodel should not be saved
-	private boolean _processUIDataState = true;
-
-	public boolean isRowAvailable()
-	{
-		return getDataModel().isRowAvailable();
-	}
-
-	public int getRowCount()
-	{
-		return getDataModel().getRowCount();
-	}
-
-	public Object getRowData()
-	{
-		return getDataModel().getRowData();
-	}
-
-	public int getRowIndex()
-	{
-		return _rowIndex;
-	}
-
-	/**
-	 * @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext)
-	 */
-	public void processUpdates(FacesContext context)
-	{
-		super.processUpdates(context);
-		// check if a update model error forces the render response for our data
-		if (context.getRenderResponse())
-		{
-			_isValidChilds = false;
-		}
-	}
-
-	/**
-	 * @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext)
-	 */
-	public void processValidators(FacesContext context)
-	{
-		super.processValidators(context);
-		// check if a validation error forces the render response for our data
-		if (context.getRenderResponse())
-		{
-			_isValidChilds = false;
-		}
-	}
-
-	/**
-	 * @see javax.faces.component.UIData#encodeBegin(javax.faces.context.FacesContext)
-	 */
-	public void encodeBegin(FacesContext context) throws IOException
-	{
-		if (_isValidChilds)
-		{
-			//Refresh DataModel for rendering:
-			_dataModel = null;
-		}
-		super.encodeBegin(context);
-	}
-
-	/**
-	 * @see javax.faces.component.UIData#encodeEnd(javax.faces.context.FacesContext)
-	 */
-	public void encodeEnd(FacesContext context) throws IOException
-	{
-		setRowIndex(-1);
-		super.encodeEnd(context);
-	}
-
-	public void setRowIndex(int rowIndex)
-	{
-		if (_rowIndex == rowIndex)
-		{
-			return;
-		}
-
-		FacesContext context = getFacesContext();
-
-		saveDescendantComponentStates(context, getFacetsAndChildren());
-
-		_rowIndex = rowIndex;
-
-		DataModel dataModel = getDataModel();
-		dataModel.setRowIndex(rowIndex);
-
-		String var = getVar();
-		if (rowIndex == -1)
-		{
-			if (var != null)
-			{
-				context.getExternalContext().getRequestMap().remove(var);
-			}
-		}
-		else
-		{
-			if (var != null)
-			{
-				if (isRowAvailable())
-				{
-					Object rowData = dataModel.getRowData();
-					context.getExternalContext().getRequestMap().put(var, rowData);
-				}
-				else
-				{
-					context.getExternalContext().getRequestMap().remove(var);
-				}
-			}
-		}
-
-		restoreDescendantComponentStates(context, getFacetsAndChildren());
-	}
-
-	private void saveDescendantComponentStates(FacesContext context, Iterator childIterator)
-	{
-		while (childIterator.hasNext())
-		{
-			UIComponent child = (UIComponent) childIterator.next();
-			if (!child.isTransient())
-			{
-				_rowState.put(child.getClientId(context), new ChildStateHolder(context, child));
-				if (!(child instanceof UIData))
-				{
-					saveDescendantComponentStates(context, child.getFacetsAndChildren());
-				}
-			}
-		}
-	}
-
-	private void restoreDescendantComponentStates(FacesContext context, Iterator childIterator)
-	{
-		while (childIterator.hasNext())
-		{
-			UIComponent child = (UIComponent) childIterator.next();
-			// the spec 1.1 (3.1.6) says: setting the id will recalculate the clientid  
-			child.setId(child.getId());
-			if (!child.isTransient())
-			{
-				String clientId = child.getClientId(context);
-				ChildStateHolder childStateHolder = (ChildStateHolder) _rowState.get(clientId);
-				if (childStateHolder != null)
-				{
-					childStateHolder.restoreState(context, child);
-				}
-				if (!(child instanceof UIData))
-				{
-					restoreDescendantComponentStates(context, child.getFacetsAndChildren());
-				}
-			}
-		}
-	}
-
-	public void setValueBinding(String name, ValueBinding binding)
-	{
-		if (name == null)
-		{
-			throw new NullPointerException("name");
-		}
-		else if (name.equals("value"))
-		{
-			_dataModel = null;
-		}
-		else if (name.equals("var") || name.equals("rowIndex"))
-		{
-			throw new IllegalArgumentException("name " + name);
-		}
-		super.setValueBinding(name, binding);
-	}
-
-	private DataModel getDataModel()
-	{
-		if (_dataModel == null)
-		{
-			_dataModel = createDataModel();
-		}
-		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 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 IndexOutOfBoundsException("Index < 0 : " + i);
-		}
-
-		public Object getWrappedData()
-		{
-			return null;
-		}
-
-		public void setWrappedData(Object obj)
-		{
-			if (obj == null)
-				return; //Clearing is allowed
-			throw new UnsupportedOperationException(this.getClass().getName()
-							+ " UnsupportedOperationException");
-		}
-	};
-
-	private static class ChildStateHolder implements Serializable
-	{
-		private final Object _state;
-
-		public ChildStateHolder(FacesContext context, StateHolder stateHolder)
-		{
-			_state = stateHolder.saveState(context);
-		}
-
-		public void restoreState(FacesContext context, StateHolder stateHolder)
-		{
-			stateHolder.restoreState(context, _state);
-		}
-	}
-
-	/**
-	 * @see javax.faces.component.UIComponentBase#processSaveState(javax.faces.context.FacesContext)
-	 */
-	public Object processSaveState(FacesContext context)
-	{
-		// state of uidata will not be processed
-		_processUIDataState = false;
-		Object state = super.processSaveState(context);
-		_processUIDataState = true;
-		return state;
-	}
-
-	/**
-	 * @see javax.faces.component.UIComponentBase#processRestoreState(javax.faces.context.FacesContext, java.lang.Object)
-	 */
-	public void processRestoreState(FacesContext context, Object state)
-	{
-		_processUIDataState = false;
-		super.processRestoreState(context, state);
-		_processUIDataState = true;
-	}
-
-	public Object saveState(FacesContext context)
-	{
-		Object values[] = new Object[4];
-		values[0] = super.saveState(context);
-
-		if (_processUIDataState)
-		{
-			values[1] = _rowState;
-			values[2] = _dataModel;
-			values[3] = Boolean.valueOf(_isValidChilds);
-		}
-		return values;
-	}
-
-	public void restoreState(FacesContext context, Object state)
-	{
-		Object values[] = (Object[]) state;
-		super.restoreState(context, values[0]);
-
-		if (_processUIDataState)
-		{
-			Map rowState = (Map) values[1];
-			if (rowState != null)
-			{
-				_rowState = rowState;
-			}
-			else
-			{
-				_rowState.clear();
-			}
-
-			_dataModel = (DataModel) values[2];
-
-			Boolean validChilds = (Boolean) values[3];
-			if (validChilds != null)
-			{
-				_isValidChilds = validChilds.booleanValue();
-			}
-			else
-			{
-				// defaults to true
-				_isValidChilds = true;
-			}
-		}
-	}
+  // contains the initial row state which is used to initialize each row
+  private Object _initialRowState = null;
+
+  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+  // Every field and method from here is identical to UIData !!!!!!!!!
+
+  private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
+
+  private int _rowIndex = -1;
+
+  public boolean isRowAvailable()
+  {
+    return getDataModel().isRowAvailable();
+  }
+
+  public int getRowCount()
+  {
+    return getDataModel().getRowCount();
+  }
+
+  public Object getRowData()
+  {
+    return getDataModel().getRowData();
+  }
+
+  public int getRowIndex()
+  {
+    return _rowIndex;
+  }
+
+  /**
+   * @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext)
+   */
+  public void processUpdates(FacesContext context)
+  {
+    super.processUpdates(context);
+    // check if a update model error forces the render response for our data
+    if (context.getRenderResponse())
+    {
+      _isValidChilds = false;
+    }
+  }
+
+  /**
+   * @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext)
+   */
+  public void processValidators(FacesContext context)
+  {
+    super.processValidators(context);
+    // check if a validation error forces the render response for our data
+    if (context.getRenderResponse())
+    {
+      _isValidChilds = false;
+    }
+  }
+
+  /**
+   * @see javax.faces.component.UIData#encodeBegin(javax.faces.context.FacesContext)
+   */
+  public void encodeBegin(FacesContext context) throws IOException
+  {
+    if (_isValidChilds)
+    {
+      //Refresh DataModel for rendering:
+      _dataModelMap.clear();
+      _rowStates = null;
+    }
+    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 setRowIndex(int rowIndex)
+  {
+    if (_rowIndex == rowIndex)
+    {
+      return;
+    }
+
+    FacesContext context = getFacesContext();
+
+    if (_rowIndex != -1)
+    {
+      if (_rowStates == null)
+      {
+        _rowStates = new HashMap();
+      }
+      _rowStates.put(getClientId(context), processSaveRowState(context));
+    }
+    else if (_initialRowState == null)
+    {
+      _initialRowState = processSaveRowState(context);
+    }
+
+    _rowIndex = rowIndex;
+
+    DataModel dataModel = getDataModel();
+    dataModel.setRowIndex(rowIndex);
+
+    String var = getVar();
+    if (rowIndex == -1)
+    {
+      if (var != null)
+      {
+        context.getExternalContext().getRequestMap().remove(var);
+      }
+    }
+    else
+    {
+      if (var != null)
+      {
+        if (isRowAvailable())
+        {
+          Object rowData = dataModel.getRowData();
+          context.getExternalContext().getRequestMap().put(var, rowData);
+        }
+        else
+        {
+          context.getExternalContext().getRequestMap().remove(var);
+        }
+      }
+    }
+
+    if (_rowIndex != -1 && _rowStates != null)
+    {
+      Object state = _rowStates.get(getClientId(context));
+      if (state == null)
+      {
+        state = _initialRowState;
+      }
+      processRestoreRowState(context, state);
+    }
+    else
+    {
+      processRestoreRowState(context, _initialRowState);
+    }
+  }
+
+  private Object processSaveRowState(FacesContext context)
+  {
+    if (isTransient())
+      return null;
+    List childStates = null;
+    int columnCount = getChildCount();
+    if (columnCount > 0)
+    {
+      for (Iterator colIt = getChildren().iterator(); colIt.hasNext();)
+      {
+        UIComponent child = (UIComponent) colIt.next();
+        if (!child.isTransient())
+        {
+          if (childStates == null)
+          {
+            childStates = new ArrayList(columnCount);
+          }
+          if (child instanceof UIColumn)
+          {
+            childStates.add(new Object[] {child.saveState(context),
+                getColumnChildsState(context, child.getChildren().iterator())});
+          }
+          else
+          {
+            childStates.add(child.processSaveState(context));
+          }
+        }
+      }
+    }
+    return childStates;
+  }
+
+  private Object getColumnChildsState(FacesContext context, Iterator compIterator)
+  {
+    List result = null;
+    while (compIterator.hasNext())
+    {
+      UIComponent colChild = (UIComponent) compIterator.next();
+      if (!colChild.isTransient())
+      {
+        if (result == null)
+        {
+          result = new ArrayList();
+        }
+        result.add(colChild.processSaveState(context));
+      }
+    }
+    return result;
+  }
+
+  public void processRestoreRowState(FacesContext context, Object state)
+  {
+    List childrenStates = (List) state;
+    int childCount = getChildCount();
+    if (childCount > 0)
+    {
+      int idx = 0;
+      for (Iterator it = getChildren().iterator(); it.hasNext();)
+      {
+        UIComponent child = (UIComponent) it.next();
+        if (!child.isTransient())
+        {
+          Object childState = childrenStates.get(idx++);
+          if (childState != null)
+          {
+            if (child instanceof UIColumn)
+            {
+              Object[] columnState = (Object[]) childState;
+              setColumnChildsState(context, child.getChildren().iterator(), columnState[1]);
+              child.restoreState(context, columnState[0]);
+            }
+            else
+            {
+              child.processRestoreState(context, childState);
+            }
+          }
+          else
+          {
+            context.getExternalContext().log(
+                "No state found to restore child of component " + getId());
+          }
+        }
+      }
+    }
+  }
+
+  private void setColumnChildsState(FacesContext context, Iterator compIterator, Object state)
+  {
+    List childrenList = (List) state;
+    if (childrenList != null)
+    {
+      int idx = 0;
+      while (compIterator.hasNext())
+      {
+        UIComponent child = (UIComponent) compIterator.next();
+        if (!child.isTransient())
+        {
+          Object childState = childrenList.get(idx++);
+          if (childState != null)
+          {
+            child.processRestoreState(context, childState);
+          }
+          else
+          {
+            context.getExternalContext().log(
+                "No state found to restore child of component " + getId());
+          }
+        }
+      }
+    }
+  }
+
+  public void setValueBinding(String name, ValueBinding binding)
+  {
+    if (name == null)
+    {
+      throw new NullPointerException("name");
+    }
+    else if (name.equals("value"))
+    {
+      _dataModelMap.clear();
+      _rowStates = null;
+    }
+    else if (name.equals("var") || name.equals("rowIndex"))
+    {
+      throw new IllegalArgumentException("name " + name);
+    }
+    super.setValueBinding(name, binding);
+  }
+
+  protected DataModel getDataModel()
+  {
+    String clientID = getParent().getClientId(getFacesContext());
+    DataModel dataModel = (DataModel) _dataModelMap.get(clientID);
+    if (dataModel == null)
+    {
+      dataModel = createDataModel();
+      _dataModelMap.put(clientID, dataModel);
+    }
+    return dataModel;
+  }
+
+  protected void setDataModel(DataModel datamodel)
+  {
+    _dataModelMap.put(getParent().getClientId(getFacesContext()), datamodel);
+  }
+
+  /**
+   * Creates a new DataModel around the current value.
+   */
+  protected 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 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 IndexOutOfBoundsException("Index < 0 : " + i);
+    }
+
+    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 Object saveState(FacesContext context)
+  {
+    Object values[] = new Object[2];
+    values[0] = super.saveState(context);
+    values[1] = Boolean.valueOf(_isValidChilds);
+
+    return values;
+  }
+
+  public void restoreState(FacesContext context, Object state)
+  {
+    Object values[] = (Object[]) state;
+    super.restoreState(context, values[0]);
+
+    Boolean validChilds = (Boolean) values[1];
+    if (validChilds != null)
+    {
+      _isValidChilds = validChilds.booleanValue();
+    }
+    else
+    {
+      _isValidChilds = true;
+    }
+  }
 
 }

Modified: myfaces/tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/UIColumns.java
URL: http://svn.apache.org/viewcvs/myfaces/tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/UIColumns.java?rev=219640&r1=219639&r2=219640&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/UIColumns.java (original)
+++ myfaces/tomahawk/trunk/src/java/org/apache/myfaces/custom/crosstable/UIColumns.java Tue Jul 19 03:17:49 2005
@@ -15,7 +15,23 @@
  */
 package org.apache.myfaces.custom.crosstable;
 
+import java.sql.ResultSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
 import javax.faces.component.UIData;
+import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
+import javax.faces.model.ArrayDataModel;
+import javax.faces.model.DataModel;
+import javax.faces.model.ListDataModel;
+import javax.faces.model.ResultDataModel;
+import javax.faces.model.ResultSetDataModel;
+import javax.faces.model.ScalarDataModel;
+import javax.servlet.jsp.jstl.sql.Result;
 
 /**
  * @author Mathias Broekelmann (latest modification by $Author$)
@@ -26,6 +42,24 @@
   public static final String COMPONENT_TYPE = "org.apache.myfaces.Columns";
   public static final String COMPONENT_FAMILY = UIData.COMPONENT_FAMILY;
 
+  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 boolean _isValidChilds = true;
+
+  // holds the the state for each cell of the child components of this UIColumns
+  private Map _cellStates = null;
+
+  // contains the initial cell state which is used to initialize each cell
+  private Object _initialCellState = null;
+
+  private int _colIndex = -1;
+  private UIData _parentUIData;
+
+  private Map _dataModelMap = new HashMap();
+
   /**
    *
    */
@@ -48,5 +82,437 @@
   public String getFamily()
   {
     return COMPONENT_FAMILY;
+  }
+
+  public boolean isRowAvailable()
+  {
+    return getDataModel().isRowAvailable();
+  }
+
+  public int getRowCount()
+  {
+    return getDataModel().getRowCount();
+  }
+
+  public Object getRowData()
+  {
+    return getDataModel().getRowData();
+  }
+
+  public int getRowIndex()
+  {
+    return _colIndex;
+  }
+
+  public void setRowIndex(int colIndex)
+  {
+    if (_colIndex == colIndex)
+    {
+      return;
+    }
+
+    FacesContext facesContext = getFacesContext();
+
+    int firstColIndex = getFirst();
+
+    if (_colIndex != -1)
+    {
+      if (_cellStates == null)
+      {
+        _cellStates = new HashMap();
+      }
+      _cellStates.put(getClientId(facesContext), processSaveState(facesContext));
+    }
+    else if (_initialCellState == null)
+    {
+      _initialCellState = processSaveState(facesContext);
+    }
+
+    _colIndex = colIndex;
+
+    DataModel dataModel = getDataModel();
+    dataModel.setRowIndex(colIndex);
+
+    String var = getVar();
+    if (colIndex == -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 (_colIndex != -1 && _cellStates != null)
+    {
+      Object state = _cellStates.get(getClientId(facesContext));
+      if (state == null)
+      {
+        state = _initialCellState;
+      }
+      processRestoreState(facesContext, state);
+    }
+    else
+    {
+      processRestoreState(facesContext, _initialCellState);
+    }
+  }
+
+  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);
+  }
+
+  protected DataModel getDataModel()
+  {
+    String clientID = getParentUIData().getParent().getClientId(getFacesContext());
+    DataModel dataModel = (DataModel) _dataModelMap.get(clientID);
+    if (dataModel == null)
+    {
+      dataModel = createDataModel();
+      _dataModelMap.put(clientID, dataModel);
+    }
+    return dataModel;
+  }
+
+  protected void setDataModel(DataModel dataModel)
+  {
+    _dataModelMap.put(getParentUIData().getParent().getClientId(getFacesContext()), dataModel);
+  }
+
+  /**
+   * Creates a new DataModel around the current value.
+   */
+  protected 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 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 IndexOutOfBoundsException("Index < 0 : " + i);
+    }
+
+    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 Object saveState(FacesContext context)
+  {
+    Object values[] = new Object[3];
+    values[0] = super.saveState(context);
+    values[1] = Boolean.valueOf(_isValidChilds);
+
+    return values;
+  }
+
+  public void restoreState(FacesContext context, Object state)
+  {
+    Object values[] = (Object[]) state;
+    super.restoreState(context, values[0]);
+
+    Boolean validChilds = (Boolean) values[2];
+    if (validChilds != null)
+    {
+      _isValidChilds = validChilds.booleanValue();
+    }
+    else
+    {
+      _isValidChilds = true;
+    }
+  }
+
+  /**
+   * @see javax.faces.component.UIData#processDecodes(javax.faces.context.FacesContext)
+   */
+  public void processDecodes(FacesContext context)
+  {
+    if (context == null)
+      throw new NullPointerException("context");
+    if (!isRendered())
+      return;
+
+    setRowIndex(-1);
+    processColumnsFacets(context, PROCESS_DECODES);
+    processRows(context, PROCESS_DECODES);
+    setRowIndex(-1);
+
+    try
+    {
+      decode(context);
+    }
+    catch (RuntimeException e)
+    {
+      context.renderResponse();
+      throw e;
+    }
+  }
+
+  /**
+   * @param context
+   * @param process_decodes2
+   */
+  private void processColumnsFacets(FacesContext context, int processAction)
+  {
+    int first = getFirst();
+    int cols = getRows();
+    int last;
+    if (cols == 0)
+    {
+      last = getRowCount();
+    }
+    else
+    {
+      last = first + cols;
+    }
+
+    for (int colIndex = first; colIndex < last; colIndex++)
+    {
+      setRowIndex(colIndex);
+      if (isRowAvailable())
+      {
+        for (Iterator facetsIter = getFacets().values().iterator(); facetsIter.hasNext();)
+        {
+          UIComponent facet = (UIComponent) facetsIter.next();
+          process(context, facet, processAction);
+        }
+      }
+    }
+    setRowIndex(-1);
+  }
+
+  /**
+   * @param context
+   * @param process_decodes2
+   */
+  private void processRows(FacesContext context, int processAction)
+  {
+    UIData parentUIData = getParentUIData();
+    int first = parentUIData.getFirst();
+    int rows = parentUIData.getRows();
+    int last;
+    if (rows == 0)
+    {
+      last = parentUIData.getRowCount();
+    }
+    else
+    {
+      last = first + rows;
+    }
+
+    for (int rowIndex = first; rowIndex < last; rowIndex++)
+    {
+      parentUIData.setRowIndex(rowIndex);
+      if (parentUIData.isRowAvailable())
+      {
+        processColumns(context, processAction);
+      }
+    }
+  }
+
+  /**
+   * 
+   */
+  private UIData getParentUIData()
+  {
+    if (_parentUIData == null)
+    {
+      UIComponent parent = getParent();
+      if (!(parent instanceof UIData))
+      {
+        throw new IllegalStateException("UIColumns component must be a child of a UIData component");
+      }
+      _parentUIData = (UIData) parent;
+    }
+    return _parentUIData;
+  }
+
+  private void processColumns(FacesContext context, int processAction)
+  {
+    int first = getFirst();
+    int cols = getRows();
+    int last;
+    if (cols == 0)
+    {
+      last = getRowCount();
+    }
+    else
+    {
+      last = first + cols;
+    }
+
+    for (int colIndex = first; colIndex < last; colIndex++)
+    {
+      setRowIndex(colIndex);
+      if (isRowAvailable())
+      {
+        for (Iterator columnChildIter = getChildren().iterator(); columnChildIter.hasNext();)
+        {
+          UIComponent columnChild = (UIComponent) columnChildIter.next();
+          process(context, columnChild, processAction);
+        }
+      }
+    }
+    setRowIndex(-1);
+  }
+
+  /**
+   * @see javax.faces.component.UIData#processValidators(javax.faces.context.FacesContext)
+   */
+  public void processValidators(FacesContext context)
+  {
+    if (context == null)
+      throw new NullPointerException("context");
+    if (!isRendered())
+      return;
+    setRowIndex(-1);
+    processColumnsFacets(context, PROCESS_VALIDATORS);
+    processRows(context, PROCESS_VALIDATORS);
+    setRowIndex(-1);
+
+    // check if an validation error forces the render response for our data
+    if (context.getRenderResponse())
+    {
+      _isValidChilds = false;
+    }
+  }
+
+  /**
+   * @see javax.faces.component.UIData#processUpdates(javax.faces.context.FacesContext)
+   */
+  public void processUpdates(FacesContext context)
+  {
+    if (context == null)
+      throw new NullPointerException("context");
+    if (!isRendered())
+      return;
+    setRowIndex(-1);
+    processColumnsFacets(context, PROCESS_UPDATES);
+    processRows(context, PROCESS_UPDATES);
+    setRowIndex(-1);
+
+    // check if an validation error forces the render response for our data
+    if (context.getRenderResponse())
+    {
+      _isValidChilds = false;
+    }
+  }
+
+  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;
+    }
+  }
+
+  public void encodeTableBegin(FacesContext context)
+  {
+    if (_isValidChilds)
+    {
+      //Refresh DataModel for rendering:
+      _dataModelMap.clear();
+      _cellStates = null;
+    }
+  }
+
+  public void encodeTableEnd(FacesContext context)
+  {
+    setRowIndex(-1);
   }
 }