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 2010/01/29 00:13:18 UTC

svn commit: r904296 [1/3] - in /myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces: component/html/ component/html/ext/ custom/aliasbean/ custom/datascroller/ custom/document/ custom/inputHtml/ custom/schedule/ custom/tabbedpane/ custom/tre...

Author: lu4242
Date: Thu Jan 28 23:13:17 2010
New Revision: 904296

URL: http://svn.apache.org/viewvc?rev=904296&view=rev
Log:
TOMAHAWK-1471 Update components to implement PartialStateHolder interface in jsf 2.0 module

Added:
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datascroller/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datascroller/AbstractHtmlDataScroller.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/document/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/document/AbstractDocument.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/inputHtml/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/inputHtml/InputHtml.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/schedule/UISchedule.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tabbedpane/AbstractHtmlPanelTabbedPane.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTree.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree/HtmlTreeNode.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/AbstractHtmlTree.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/tree2/UITreeData.java
Modified:
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/Alias.java
    myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/AliasBean.java

Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,1702 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.component.NewspaperTable;
+import org.apache.myfaces.component.UserRoleAware;
+import org.apache.myfaces.component.UserRoleUtils;
+import org.apache.myfaces.custom.column.HtmlSimpleColumn;
+import org.apache.myfaces.custom.crosstable.UIColumns;
+import org.apache.myfaces.custom.sortheader.HtmlCommandSortHeader;
+import org.apache.myfaces.renderkit.html.ext.HtmlTableRenderer;
+import org.apache.myfaces.renderkit.html.util.TableContext;
+import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
+
+import javax.faces.application.Application;
+import javax.faces.component.EditableValueHolder;
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIColumn;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.el.ValueBinding;
+import javax.faces.model.DataModel;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * The MyFacesDataTable extends the standard JSF DataTable by two
+ * important features:
+ * <br/>
+ * <ul>
+ *   <li>Possiblity to save the state of the DataModel.</li>
+ * 
+ *   <li>Support for clickable sort headers (see SortHeader
+ *   component).</li>
+ * </ul>
+ * <br/>
+ * Extended data_table that adds some additional features to the 
+ * standard data_table action: see attribute descriptions for 
+ * preserveDataModel, sortColumn, sortAscending and preserveSort. 
+ * <br/>
+ * Unless otherwise specified, all attributes accept static values or EL expressions.
+ * 
+ * @JSFComponent
+ *   name = "t:dataTable"
+ *   class = "org.apache.myfaces.component.html.ext.HtmlDataTable"
+ *   tagClass = "org.apache.myfaces.generated.taglib.html.ext.HtmlDataTableTag"
+ * @since 1.1.7
+ * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
+ * @author Manfred Geiler
+ * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (mié, 03 sep 2008) $
+ */
+public abstract class AbstractHtmlDataTable extends HtmlDataTableHack implements UserRoleAware, NewspaperTable
+{
+    private static final Log log = LogFactory.getLog(AbstractHtmlDataTable.class);
+
+    private static final int PROCESS_DECODES = 1;
+    private static final int PROCESS_VALIDATORS = 2;
+    private static final int PROCESS_UPDATES = 3;
+
+    private static final boolean DEFAULT_SORTASCENDING = true;
+    private static final boolean DEFAULT_SORTABLE = false;
+    private static final boolean DEFAULT_EMBEDDED = false;
+    private static final boolean DEFAULT_DETAILSTAMP_EXPANDED = false;
+    private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
+
+    private static final Integer DEFAULT_NEWSPAPER_COLUMNS = new Integer(1);
+    private static final String DEFAULT_NEWSPAPER_ORIENTATION = "vertical";
+    private static final String DEFAULT_DETAILSTAMP_LOCATION = "after";
+
+    /**
+     * the property names
+     */
+    public static final String NEWSPAPER_COLUMNS_PROPERTY = "newspaperColumns";
+    public static final String SPACER_FACET_NAME = "spacer";
+    public static final String NEWSPAPER_ORIENTATION_PROPERTY = "newspaperOrientation";
+
+    private _SerializableDataModel _preservedDataModel;
+
+    private String _forceIdIndexFormula = null;
+    private String _sortColumn = null;
+    private Boolean _sortAscending = null;
+    private String _sortProperty = null;
+    private String _rowStyleClass = null;
+    private String _rowStyle = null;
+    private String _varDetailToggler = null;
+
+    private int _sortColumnIndex = -1;
+
+    private boolean _isValidChildren = true;
+
+    private Map _expandedNodes = new HashMap();
+
+    private Map _detailRowStates = new HashMap();
+
+    private TableContext _tableContext = null;
+
+    public TableContext getTableContext()
+    {
+        if (_tableContext == null)
+        {
+            _tableContext = new TableContext();
+        }
+        return _tableContext;
+    }
+
+    public String getClientId(FacesContext context)
+    {
+        String standardClientId = super.getClientId(context);
+        int rowIndex = getRowIndex();
+        if (rowIndex == -1)
+        {
+            return standardClientId;
+        }
+
+        String forcedIdIndex = getForceIdIndexFormula();
+        if (forcedIdIndex == null || forcedIdIndex.length() == 0)
+            return standardClientId;
+
+        // Trick : Remove the last part starting with NamingContainer.SEPARATOR_CHAR that contains the rowIndex.
+        // It would be best to not resort to String manipulation,
+        // but we can't get super.super.getClientId() :-(
+        int indexLast_ = standardClientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
+        if (indexLast_ == -1)
+        {
+            log.info("Could not parse super.getClientId. forcedIdIndex will contain the rowIndex.");
+            return standardClientId + NamingContainer.SEPARATOR_CHAR + forcedIdIndex;
+        }
+
+        //noinspection UnnecessaryLocalVariable
+        String parsedForcedClientId = standardClientId.substring(0, indexLast_ + 1) + forcedIdIndex;
+        return parsedForcedClientId;
+    }
+
+    public UIComponent findComponent(String expr)
+    {
+        if (expr.length() > 0 && Character.isDigit(expr.charAt(0)))
+        {
+            int separatorIndex = expr.indexOf(NamingContainer.SEPARATOR_CHAR);
+
+            String rowIndexStr = expr;
+            String remainingPart = null;
+
+            if (separatorIndex != -1)
+            {
+                rowIndexStr = expr.substring(0, separatorIndex);
+                remainingPart = expr.substring(separatorIndex + 1);
+            }
+
+            int rowIndex = Integer.valueOf(rowIndexStr).intValue();
+
+            if (remainingPart == null)
+            {
+                log.error("Wrong syntax of expression : " + expr +
+                        " rowIndex was provided, but no component name.");
+                return null;
+            }
+
+            UIComponent comp = super.findComponent(remainingPart);
+
+            if (comp == null)
+                return null;
+
+            //noinspection UnnecessaryLocalVariable
+            UIComponentPerspective perspective = new UIComponentPerspective(this, comp, rowIndex);
+            return perspective;
+        }
+        else
+        {
+            return super.findComponent(expr);
+        }
+    }
+
+    public void setRowIndex(int rowIndex)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+
+        if (rowIndex < -1)
+        {
+            throw new IllegalArgumentException("rowIndex is less than -1");
+        }
+
+        UIComponent facet = getFacet(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME);
+        /*Just for obtaining an iterator which must be passed to saveDescendantComponentStates()*/
+        Set set = new HashSet();
+        set.add(facet);
+        if (rowIndex != -1 && facet != null)
+        {
+            _detailRowStates.put(getClientId(facesContext), saveDescendantComponentStates(set.iterator(), false));
+        }
+
+        String rowIndexVar = getRowIndexVar();
+        String rowCountVar = getRowCountVar();
+        String previousRowDataVar = getPreviousRowDataVar();
+        if (rowIndexVar != null || rowCountVar != null || previousRowDataVar != null)
+        {
+            Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
+
+            if (previousRowDataVar != null && rowIndex >= 0) //we only need to provide the previousRowDataVar for a valid rowIndex
+            {
+                if (isRowAvailable())
+                {
+                    //previous row is available
+                    requestMap.put(previousRowDataVar, getRowData());
+                }
+                else
+                {
+                    //no previous row available
+                    requestMap.put(previousRowDataVar, null);
+                }
+            }
+
+            super.setRowIndex(rowIndex);
+
+            if (rowIndex >= 0)
+            {
+                //regular row index, update request scope variables
+                if (rowIndexVar != null)
+                {
+                    requestMap.put(rowIndexVar, new Integer(rowIndex));
+                }
+
+                if (rowCountVar != null)
+                {
+                    requestMap.put(rowCountVar, new Integer(getRowCount()));
+                }
+            }
+            else
+            {
+                //rowIndex == -1 means end of loop --> remove request scope variables
+                if (rowIndexVar != null)
+                {
+                    requestMap.remove(rowIndexVar);
+                }
+
+                if (rowCountVar != null)
+                {
+                    requestMap.remove(rowCountVar);
+                }
+
+                if (previousRowDataVar != null)
+                {
+                    requestMap.remove(previousRowDataVar);
+                }
+            }
+        }
+        else
+        {
+            // no extended var attributes defined, no special treatment
+            super.setRowIndex(rowIndex);
+        }
+
+        if (rowIndex != -1 && facet != null)
+        {
+            Object rowState = _detailRowStates.get(getClientId(facesContext));
+
+            restoreDescendantComponentStates(set.iterator(),
+                    rowState, false);
+
+        }
+
+        if (_varDetailToggler != null)
+        {
+            Map requestMap = getFacesContext().getExternalContext().getRequestMap();
+            requestMap.put(_varDetailToggler, this);
+        }
+    }
+
+    public void processDecodes(FacesContext context)
+    {
+        if (!isRendered())
+        {
+            return;
+        }
+
+        // We must remove and then replace the facet so that
+        // it is not processed by default facet handling code
+        //
+        Object facet = getFacets().remove(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME);
+        super.processDecodes(context);
+        if ( facet != null ) getFacets().put(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME, (UIComponent)facet);
+
+        setRowIndex(-1);
+        processColumns(context, PROCESS_DECODES);
+        setRowIndex(-1);
+        processDetails(context, PROCESS_DECODES);
+        setRowIndex(-1);
+    }
+
+    /**
+     * @param context
+     * @param processAction
+     */
+    private void processDetails(FacesContext context, int processAction)
+    {
+        UIComponent facet = getFacet(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME);
+
+        if (facet != null)
+        {
+            int first = getFirst();
+            int rows = getRows();
+            int last;
+            if (rows == 0)
+            {
+                last = getRowCount();
+            }
+            else
+            {
+                last = first + rows;
+            }
+            for (int rowIndex = first; last == -1 || rowIndex < last; rowIndex++)
+            {
+                setRowIndex(rowIndex);
+
+                //scrolled past the last row
+                if (!isRowAvailable())
+                {
+                    break;
+                }
+
+                if (!isCurrentDetailExpanded())
+                {
+                    continue;
+                }
+
+                // If we are in the decode phase, the values restored into our
+                // facet in setRowIndex() may be incorrect. This will happen
+                // if some input fields are rendered in some rows, but not
+                // rendered in others. In this case the input field components
+                // will still contain the _submittedValue from the previous row
+                // that had that input field and _submittedValue will not be set to
+                // null by the process() method if there was no value submitted.
+                // Thus, an invalid component state for that row will be saved in
+                // _detailRowStates. The validation phase will not put a null into
+                // _sumbittedValue either, b/c the component is not rendered, so
+                // validation code doesn't run. This erroneous value will propagate all the way
+                // to the render phase, and result in all rows on the current page being
+                // rendered with the "stuck" _submittedValue, rather than evaluating the
+                // value to render for every row.
+                //
+                // We can fix this by initializing _submittedValue of all input fields in the facet
+                // to null before calling the process() method below during the decode phase.
+                //
+                if (PROCESS_DECODES == processAction)
+                {
+                    resetAllSubmittedValues(facet);
+                }
+
+                process(context, facet, processAction);
+
+                if ( rowIndex == (last - 1) )
+                {
+                    Set set = new HashSet();
+                    set.add(facet);
+                    _detailRowStates.put(
+                            getClientId(FacesContext.getCurrentInstance()),
+                                saveDescendantComponentStates(set.iterator(),false));
+                }
+            }
+        }
+    }
+
+    private void resetAllSubmittedValues(UIComponent component)
+    {
+        if (component instanceof EditableValueHolder)
+        {
+            ((EditableValueHolder) component).setSubmittedValue(null);
+        }
+
+        for (Iterator it = component.getFacetsAndChildren(); it.hasNext();)
+        {
+            resetAllSubmittedValues((UIComponent) it.next());
+        }
+    }
+
+    /**
+     * @param context
+     * @param processAction
+     */
+    private void processColumns(FacesContext context, int processAction)
+    {
+        for (Iterator it = getChildren().iterator(); it.hasNext();)
+        {
+            UIComponent child = (UIComponent) it.next();
+            if (child instanceof UIColumns)
+            {
+                process(context, child, processAction);
+            }
+        }
+    }
+
+    private void process(FacesContext context, UIComponent component, int processAction)
+    {
+        switch (processAction)
+        {
+            case PROCESS_DECODES:
+                component.processDecodes(context);
+                break;
+            case PROCESS_VALIDATORS:
+                component.processValidators(context);
+                break;
+            case PROCESS_UPDATES:
+                component.processUpdates(context);
+                break;
+        }
+    }
+
+    public void processValidators(FacesContext context)
+    {
+        if (!isRendered())
+        {
+            return;
+        }
+        // We must remove and then replace the facet so that
+        // it is not processed by default facet handling code
+        //
+        Object facet = getFacets().remove(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME);
+        super.processValidators(context);
+        if ( facet != null ) getFacets().put(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME,(UIComponent) facet);
+
+        processColumns(context, PROCESS_VALIDATORS);
+        setRowIndex(-1);
+        processDetails(context, PROCESS_VALIDATORS);
+        setRowIndex(-1);
+
+        if (context.getRenderResponse())
+        {
+            _isValidChildren = false;
+        }
+    }
+
+    public void processUpdates(FacesContext context)
+    {
+        if (!isRendered())
+        {
+            return;
+        }
+
+        // We must remove and then replace the facet so that
+        // it is not processed by default facet handling code
+        //
+        Object facet = getFacets().remove(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME);
+        super.processUpdates(context);
+        if ( facet != null ) getFacets().put(HtmlTableRenderer.DETAIL_STAMP_FACET_NAME,(UIComponent) facet);
+
+        processColumns(context, PROCESS_UPDATES);
+        setRowIndex(-1);
+        processDetails(context, PROCESS_UPDATES);
+        setRowIndex(-1);
+
+        if (isPreserveDataModel())
+        {
+            updateModelFromPreservedDataModel(context);
+        }
+
+        if (context.getRenderResponse())
+        {
+            _isValidChildren = false;
+        }
+    }
+
+    private void updateModelFromPreservedDataModel(FacesContext context)
+    {
+        ValueBinding vb = getValueBinding("value");
+        if (vb != null && !vb.isReadOnly(context))
+        {
+            _SerializableDataModel dm = (_SerializableDataModel) getDataModel();
+            Class type = vb.getType(context);
+            if (DataModel.class.isAssignableFrom(type))
+            {
+                vb.setValue(context, dm);
+            }
+            else if (List.class.isAssignableFrom(type))
+            {
+                vb.setValue(context, dm.getWrappedData());
+            }
+            else if (OBJECT_ARRAY_CLASS.isAssignableFrom(type))
+            {
+                List lst = (List) dm.getWrappedData();
+                vb.setValue(context, lst.toArray(new Object[lst.size()]));
+            }
+            else if (ResultSet.class.isAssignableFrom(type))
+            {
+                throw new UnsupportedOperationException(this.getClass().getName()
+                        + " UnsupportedOperationException");
+            }
+            else
+            {
+                //Assume scalar data model
+                List lst = (List) dm.getWrappedData();
+                if (lst!= null && lst.size() > 0)
+                {
+                    vb.setValue(context, lst.get(0));
+                }
+                else
+                {
+                    vb.setValue(context, null);
+                }
+            }
+        }
+        _preservedDataModel = null;
+    }
+
+    public void encodeBegin(FacesContext context) throws IOException
+    {
+        if (!isRendered())
+            return;
+
+        if (_isValidChildren && !hasErrorMessages(context))
+        {
+            _preservedDataModel = null;
+        }
+
+        for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+        {
+            UIComponent component = (UIComponent) iter.next();
+            if (component instanceof UIColumns)
+            {
+                // Merge the columns from the tomahawk dynamic component
+                // into this object.
+                ((UIColumns) component).encodeTableBegin(context);
+            }
+        }
+
+        //replace facet header content component of the columns, with a new command sort header component
+        //if sortable=true, replace it for all, if not just for the columns marked as sortable
+        for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+        {
+            UIComponent component = (UIComponent) iter.next();
+            if (component instanceof UIColumn)
+            {
+                UIColumn aColumn = (UIColumn) component;
+                UIComponent headerFacet = aColumn.getHeader();
+
+                boolean replaceHeaderFacets = isSortable(); //if the table is sortable, all
+                //header facets can be changed if needed
+                String columnName = null;
+                String propertyName = null;
+                boolean defaultSorted = false;
+
+                if (aColumn instanceof HtmlSimpleColumn)
+                {
+                    HtmlSimpleColumn asColumn = (HtmlSimpleColumn) aColumn;
+                    propertyName = asColumn.getSortPropertyName();
+                    defaultSorted = asColumn.isDefaultSorted();
+
+                    if (asColumn.isSortable())
+                        replaceHeaderFacets = true;
+                }
+
+                //replace header facet with a sortable header component if needed
+                if (replaceHeaderFacets && isSortHeaderNeeded(aColumn, headerFacet))
+                {
+                    propertyName = propertyName != null ? propertyName : getSortPropertyFromEL(aColumn);
+                    if (propertyName == null)
+                        log.warn("Couldn't determine sort property for column [" + aColumn.getId() + "].");
+
+                    if (headerFacet != null)
+                    {
+                        HtmlCommandSortHeader sortHeader = createSortHeaderComponent(context, aColumn, headerFacet, propertyName);
+                        columnName = sortHeader.getColumnName();
+
+                        aColumn.setHeader(sortHeader);
+                        sortHeader.setParent(aColumn);
+                    }
+                }
+                else if (headerFacet instanceof HtmlCommandSortHeader)
+                {
+                    //command sort headers are already in place, just store the column name and sort property name
+                    HtmlCommandSortHeader sortHeader = (HtmlCommandSortHeader) headerFacet;
+                    columnName = sortHeader.getColumnName();
+                    propertyName = sortHeader.getPropertyName();
+
+                    //if the command sort header component doesn't specify a sort property, determine it
+                    if (propertyName == null)
+                    {
+                        propertyName = getSortPropertyFromEL(aColumn);
+                        sortHeader.setPropertyName(propertyName);
+                    }
+
+                    if (propertyName == null)
+                        log.warn("Couldn't determine sort property for column [" + aColumn.getId() + "].");
+                }
+
+                //make the column marked as default sorted be the current sorted column
+                if (defaultSorted && getSortColumn() == null)
+                {
+                    setSortColumn(columnName);
+                    setSortProperty(propertyName);
+                }
+            }
+        }
+
+        // Now invoke the superclass encodeBegin, which will eventually
+        // execute the encodeBegin for the associated renderer.
+        super.encodeBegin(context);
+    }
+
+    /**
+     *
+     */
+    protected boolean isSortHeaderNeeded(UIColumn parentColumn, UIComponent headerFacet)
+    {
+        return !(headerFacet instanceof HtmlCommandSortHeader);
+    }
+
+    /**
+     *
+     */
+    protected HtmlCommandSortHeader createSortHeaderComponent(FacesContext context, UIColumn parentColumn, UIComponent initialHeaderFacet, String propertyName)
+    {
+        Application application = context.getApplication();
+
+        HtmlCommandSortHeader sortHeader = (HtmlCommandSortHeader) application.createComponent(HtmlCommandSortHeader.COMPONENT_TYPE);
+        String id = context.getViewRoot().createUniqueId();
+        sortHeader.setId(id);
+        sortHeader.setColumnName(id);
+        sortHeader.setPropertyName(propertyName);
+        sortHeader.setArrow(true);
+        sortHeader.setImmediate(true); //needed to work when dataScroller is present
+        sortHeader.getChildren().add(initialHeaderFacet);
+        initialHeaderFacet.setParent(sortHeader);
+
+        return sortHeader;
+    }
+
+    /**
+     *
+     */
+    protected String getSortPropertyFromEL(UIComponent component)
+    {
+        if (getVar() == null)
+        {
+            log.warn("There is no 'var' specified on the dataTable, sort properties cannot be determined automaticaly.");
+            return null;
+        }
+
+        for (Iterator iter = component.getChildren().iterator(); iter.hasNext();)
+        {
+            UIComponent aChild = (UIComponent) iter.next();
+            if (aChild.isRendered())
+            {
+                ValueBinding vb = aChild.getValueBinding("value");
+                if (vb != null)
+                {
+                    String expressionString = vb.getExpressionString();
+
+                    int varIndex = expressionString.indexOf(getVar() + ".");
+                    if (varIndex != -1)
+                    {
+                        int varEndIndex = varIndex + getVar().length();
+                        String tempProp = expressionString.substring(varEndIndex + 1, expressionString.length());
+
+                        StringTokenizer tokenizer = new StringTokenizer(tempProp, " +[]{}-/*|?:&<>!=()%");
+                        if (tokenizer.hasMoreTokens())
+                            return tokenizer.nextToken();
+                    }
+                }
+                else
+                {
+                    String sortProperty = getSortPropertyFromEL(aChild);
+                    if (sortProperty != null)
+                        return sortProperty;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return the index coresponding to the given column name.
+     */
+    protected int columnNameToIndex(String columnName)
+    {
+        int index = 0;
+        for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+        {
+            UIComponent aChild = (UIComponent) iter.next();
+            if (aChild instanceof UIColumn)
+            {
+                UIComponent headerFacet = ((UIColumn) aChild).getHeader();
+                if (headerFacet != null && headerFacet instanceof HtmlCommandSortHeader)
+                {
+                    HtmlCommandSortHeader sortHeader = (HtmlCommandSortHeader) headerFacet;
+                    if (columnName != null && columnName.equals(sortHeader.getColumnName()))
+                        return index;
+                }
+            }
+
+            index += 1;
+        }
+
+        return -1;
+    }
+
+    /**
+     * @see javax.faces.component.UIData#encodeEnd(javax.faces.context.FacesContext)
+     */
+    public void encodeEnd(FacesContext context) throws IOException
+    {
+        super.encodeEnd(context);
+        for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+        {
+            UIComponent component = (UIComponent) iter.next();
+            if (component instanceof UIColumns)
+            {
+                ((UIColumns) component).encodeTableEnd(context);
+            }
+        }
+    }
+
+    /**
+     * The index of the first row to be displayed, where 0 is the first row.
+     * 
+     * @JSFProperty
+     */
+    public int getFirst()
+    {
+        if (_preservedDataModel != null)
+        {
+            //Rather get the currently restored DataModel attribute
+            return _preservedDataModel.getFirst();
+        }
+        else
+        {
+            return super.getFirst();
+        }
+    }
+
+    public void setFirst(int first)
+    {
+        if (_preservedDataModel != null)
+        {
+            //Also change the currently restored DataModel attribute
+            _preservedDataModel.setFirst(first);
+        }
+        super.setFirst(first);
+    }
+
+    /**
+     *  The number of rows to be displayed. Specify zero for all remaining rows in the table.
+     * 
+     * @JSFProperty
+     */
+    public int getRows()
+    {
+        if (_preservedDataModel != null)
+        {
+            //Rather get the currently restored DataModel attribute
+            return _preservedDataModel.getRows();
+        }
+        else
+        {
+            return super.getRows();
+        }
+    }
+
+    public void setRows(int rows)
+    {
+        if (_preservedDataModel != null)
+        {
+            //Also change the currently restored DataModel attribute
+            _preservedDataModel.setRows(rows);
+        }
+        super.setRows(rows);
+    }
+
+    public Object saveState(FacesContext context)
+    {
+        boolean preserveSort = isPreserveSort();
+
+        Object[] values = new Object[15];
+        values[0] = super.saveState(context);
+        values[1] = _preserveDataModel;
+
+        if (isPreserveDataModel())
+        {
+            _preservedDataModel = getSerializableDataModel();
+            values[2] = saveAttachedState(context, _preservedDataModel);
+        }
+        else
+        {
+            values[2] = null;
+        }
+        values[3] = _preserveSort;
+        values[4] = _forceIdIndexFormula;
+        values[5] = _sortColumn;
+        values[6] = _sortAscending;
+        values[7] = _sortProperty;
+
+        values[8] = _rowStyleClass;
+        values[9] = _rowStyle;
+
+        values[10] = preserveSort ? getSortColumn() : null;
+        values[11] = preserveSort ? Boolean.valueOf(isSortAscending()) : null;
+
+        values[12] = _varDetailToggler;
+        values[13] = _expandedNodes;
+        values[14] = new Integer(_sortColumnIndex);
+
+        return values;
+    }
+
+    /**
+     * @see org.apache.myfaces.component.html.ext.HtmlDataTableHack#getDataModel()
+     */
+    protected DataModel getDataModel()
+    {
+        if (_preservedDataModel != null)
+        {
+            setDataModel(_preservedDataModel);
+            _preservedDataModel = null;
+        }
+
+        return super.getDataModel();
+    }
+
+    /**
+     * @see org.apache.myfaces.component.html.ext.HtmlDataTableHack#createDataModel()
+     */
+    protected DataModel createDataModel()
+    {
+        DataModel dataModel = super.createDataModel();
+
+        boolean isSortable = isSortable();
+
+        if (!(dataModel instanceof SortableModel))
+        {
+            //if sortable=true make each column sortable
+            //if sortable=false, check to see if at least one column sortable case in which
+            //the current model needs to be wrapped by a sortable one.
+            for (Iterator iter = getChildren().iterator(); iter.hasNext();)
+            {
+                UIComponent component = (UIComponent) iter.next();
+                if (component instanceof HtmlSimpleColumn)
+                {
+                    HtmlSimpleColumn aColumn = (HtmlSimpleColumn) component;
+                    if (isSortable())
+                        aColumn.setSortable(true);
+
+                    if (aColumn.isSortable())
+                        isSortable = true;
+
+                    if (aColumn.isDefaultSorted() && getSortColumn() == null)
+                        setSortProperty(aColumn.getSortPropertyName());
+                }
+            }
+
+            if (isSortable)
+                dataModel = new SortableModel(dataModel);
+        }
+
+        if (isSortable && getSortProperty() != null)
+        {
+            SortCriterion criterion = new SortCriterion(getSortProperty(), isSortAscending());
+            List criteria = new ArrayList();
+            criteria.add(criterion);
+
+            ((SortableModel) dataModel).setSortCriteria(criteria);
+        }
+
+        return dataModel;
+    }
+
+    public void restoreState(FacesContext context, Object state)
+    {
+        Object[] values = (Object[]) state;
+        super.restoreState(context, values[0]);
+        _preserveDataModel = (Boolean) values[1];
+        if (isPreserveDataModel())
+        {
+            _preservedDataModel = (_SerializableDataModel) restoreAttachedState(context, values[2]);
+        }
+        else
+        {
+            _preservedDataModel = null;
+        }
+        _preserveSort = (Boolean) values[3];
+        _forceIdIndexFormula = (String) values[4];
+        _sortColumn = (String) values[5];
+        _sortAscending = (Boolean) values[6];
+        _sortProperty = (String) values[7];
+
+        _rowStyleClass = (String) values[8];
+        _rowStyle = (String) values[9];
+
+        if (isPreserveSort())
+        {
+            String sortColumn = (String) values[10];
+            Boolean sortAscending = (Boolean) values[11];
+            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);
+                }
+            }
+        }
+
+        _varDetailToggler = (String) values[12];
+        _expandedNodes = (Map) values[13];
+        _sortColumnIndex = values[14] != null ? ((Integer) values[14]).intValue() : -1;
+    }
+
+    public _SerializableDataModel getSerializableDataModel()
+    {
+        DataModel dm = getDataModel();
+        if (dm instanceof _SerializableDataModel)
+        {
+            return (_SerializableDataModel) dm;
+        }
+        return createSerializableDataModel();
+    }
+
+    /**
+     * @return _SerializableDataModel
+     */
+    private _SerializableDataModel createSerializableDataModel()
+    {
+        Object value = getValue();
+        if (value == null)
+        {
+            return null;
+        }
+        else if (value instanceof DataModel)
+        {
+            return new _SerializableDataModel(getFirst(), getRows(), (DataModel) value);
+        }
+        else if (value instanceof List)
+        {
+            return new _SerializableListDataModel(getFirst(), getRows(), (List) value);
+        }
+        // accept a Collection is not supported in the Spec
+        else if (value instanceof Collection)
+        {
+            return new _SerializableListDataModel(getFirst(), getRows(), new ArrayList((Collection) value));
+        }
+        else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
+        {
+            return new _SerializableArrayDataModel(getFirst(), getRows(), (Object[]) value);
+        }
+        else if (value instanceof ResultSet)
+        {
+            return new _SerializableResultSetDataModel(getFirst(), getRows(), (ResultSet) value);
+        }
+        else if (value instanceof javax.servlet.jsp.jstl.sql.Result)
+        {
+            return new _SerializableResultDataModel(getFirst(), getRows(),
+                    (javax.servlet.jsp.jstl.sql.Result) value);
+        }
+        else
+        {
+            return new _SerializableScalarDataModel(getFirst(), getRows(), value);
+        }
+    }
+
+    public boolean isRendered()
+    {
+        if (!UserRoleUtils.isVisibleOnUserRole(this))
+            return false;
+        return super.isRendered();
+    }
+
+    public void setForceIdIndexFormula(String forceIdIndexFormula)
+    {
+        _forceIdIndexFormula = forceIdIndexFormula;
+        ValueBinding vb = getValueBinding("forceIdIndexFormula");
+        if (vb != null)
+        {
+            vb.setValue(getFacesContext(), _forceIdIndexFormula);
+            _forceIdIndexFormula = null;
+        }
+    }
+
+    /**
+     * A formula that overrides the default row index in the 
+     * construction of table's body components. 
+     * 
+     * Example : #{myRowVar.key} Warning, the EL should 
+     * evaluate to a unique value for each row !
+     * 
+     * @JSFProperty
+     */
+    public String getForceIdIndexFormula()
+    {
+        if (_forceIdIndexFormula != null)
+            return _forceIdIndexFormula;
+        ValueBinding vb = getValueBinding("forceIdIndexFormula");
+        if (vb == null)
+            return null;
+        Object eval = vb.getValue(getFacesContext());
+        return eval == null ? null : eval.toString();
+    }
+
+    /**
+     * Specify what column the data should be sorted on.
+     * <p/>
+     * Note that calling this method <i>immediately</i> stores the value
+     * via any value-binding with name "sortColumn". This is done because
+     * this method is called by the HtmlCommandSortHeader component when
+     * the user has clicked on a column's sort header. In this case, the
+     * the model getter method mapped for name "value" needs to read this
+     * value in able to return the data in the desired order - but the
+     * HtmlCommandSortHeader component is usually "immediate" in order to
+     * avoid validating the enclosing form. Yes, this is rather hacky -
+     * but it works.
+     */
+    public void setSortColumn(String sortColumn)
+    {
+        _sortColumn = sortColumn;
+        // update model is necessary here, because processUpdates is never called
+        // reason: HtmlCommandSortHeader.isImmediate() == true
+        ValueBinding vb = getValueBinding("sortColumn");
+        if (vb != null)
+        {
+            vb.setValue(getFacesContext(), _sortColumn);
+            _sortColumn = null;
+        }
+
+        setSortColumnIndex(columnNameToIndex(sortColumn));
+    }
+
+    /**
+     * Value reference to a model property that gives the current 
+     * sort column name. The target String property is set to 
+     * the "columnName" of whichever column has been chosen 
+     * to sort by, and the method which is bound to the "value" 
+     * attribute of this table (ie which provides the DataModel used) 
+     * is expected to use this property to determine how to sort 
+     * the DataModel's contents.
+     * 
+     * @JSFProperty
+     */
+    public String getSortColumn()
+    {
+        if (_sortColumn != null) return _sortColumn;
+        ValueBinding vb = getValueBinding("sortColumn");
+        return vb != null ? (String) vb.getValue(getFacesContext()) : null;
+    }
+
+    public void setSortAscending(boolean sortAscending)
+    {
+        _sortAscending = Boolean.valueOf(sortAscending);
+        // update model is necessary here, because processUpdates is never called
+        // reason: HtmlCommandSortHeader.isImmediate() == true
+        ValueBinding vb = getValueBinding("sortAscending");
+        if (vb != null)
+        {
+            vb.setValue(getFacesContext(), _sortAscending);
+            _sortAscending = null;
+        }
+    }
+
+    /**
+     * Value reference to a model property that gives the current 
+     * sort direction. The target Boolean property is set to true 
+     * when the selected sortColumn should be sorted in ascending 
+     * order, and false otherwise. The method which is bound to 
+     * the "value" attribute of this table (ie which provides the 
+     * DataModel used) is expected to use this property to 
+     * determine how to sort the DataModel's contents.
+     * 
+     * @JSFProperty
+     *   defaultValue = "true"
+     */
+    public boolean isSortAscending()
+    {
+        if (_sortAscending != null)
+            return _sortAscending.booleanValue();
+        ValueBinding vb = getValueBinding("sortAscending");
+        Boolean v = vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
+        return v != null ? v.booleanValue() : DEFAULT_SORTASCENDING;
+    }
+
+    /**
+     * The name of a javabean property on which the table is sorted.
+     * <p>
+     * The datamodel should contain objects that have this property;
+     * reflection will be used to sort the datamodel on that property
+     * using the default comparator for that type. 
+     * <p>
+     * This value is part of the component state. However it is not
+     * directly settable by users; instead it is set by other components
+     * such as a CommandSortHeader.
+     */
+    public void setSortProperty(String sortProperty)
+    {
+        _sortProperty = sortProperty;
+    }
+
+    /**
+     * @JSFProperty
+     *   literalOnly="true"
+     *   tagExcluded="true"
+     */
+    public String getSortProperty()
+    {
+        return _sortProperty;
+    }
+
+    /**
+     * Define if the table is sortable or not
+     * 
+     * @JSFProperty
+     *   defaultValue="false"
+     */
+    public abstract boolean isSortable();
+
+    /**
+     * Avoids rendering the html table tags, thus, giving you a 
+     * table rendering just rows. You can use this together 
+     * with the detailStamp faces of the parent datatable 
+     * to render child-tables using the same layout as the parent. 
+     * 
+     * Notice: You have to ensure both tables do have the same 
+     * number of columns. Using the colspan attribute of the 
+     * column tag might help alot.
+     * 
+     * @JSFProperty
+     *   defaultValue="false"
+     */
+    public abstract boolean isEmbedded();
+
+    /**
+     * true|false - true if the detailStamp should be expanded by default. default: false
+     * 
+     * @JSFProperty
+     *   defaultValue="false"
+     */
+    public abstract boolean isDetailStampExpandedDefault();
+
+    /**
+     * before|after - where to render the detailStamp, before the 
+     * actual row or after it. default: after
+     * 
+     * @JSFProperty
+     *   defaultValue="after"
+     */
+    public abstract String getDetailStampLocation();
+
+    /**
+     * Defines a JavaScript onmouseover event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnMouseOver();
+
+    /**
+     * Defines a JavaScript onmouseout event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnMouseOut();
+
+    /**
+     * Defines a JavaScript onclick event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnClick();
+
+    /**
+     * Defines a JavaScript ondblclick event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnDblClick();
+
+    /**
+     * Defines a JavaScript onkeydown event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnKeyDown();
+
+    /**
+     * Defines a JavaScript onkeypress event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnKeyPress();
+
+    /**
+     * Defines a JavaScript onkeyup event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnKeyUp();
+
+    /**
+     * Corresponds to the HTML class attribute for the row tr tag.
+     * 
+     * @JSFProperty
+     */
+    public String getRowStyleClass()
+    {
+    if (_rowStyleClass != null)
+        return _rowStyleClass;
+
+    // TODO: temporarily support fully-qualified ext. dataTable attribute names.
+    ValueBinding vb = getValueBinding("org.apache.myfaces.dataTable.ROW_STYLECLASS");
+    if (vb != null)
+        log.warn("org.apache.myfaces.dataTable.ROW_STYLECLASS is deprecated. Please use rowStyleClass instead.");
+    else
+        vb = getValueBinding(JSFAttr.ROW_STYLECLASS_ATTR);
+    if(vb == null)
+        return null;
+    String bindingValue = (String) vb.getValue(getFacesContext());
+    if(bindingValue == "")
+        return null;  // Fix for JSF 1.2 EL coercing nulls to empty string
+    return bindingValue;
+    }
+
+    public void setRowStyleClass(String rowStyleClass)
+    {
+        _rowStyleClass = rowStyleClass;
+    }
+
+    /**
+     * Corresponds to the HTML style attribute for the row tr tag.
+     * 
+     * @JSFProperty
+     */
+    public String getRowStyle()
+    {
+        if (_rowStyle != null)
+            return _rowStyle;
+
+    // TODO: temporarily support fully-qualified ext. dataTable attribute names.
+        ValueBinding vb = getValueBinding("org.apache.myfaces.dataTable.ROW_STYLE");
+    if (vb != null)
+        log.warn("org.apache.myfaces.dataTable.ROW_STYLE is deprecated. Please use rowStyle instead.");
+    else
+        vb = getValueBinding(JSFAttr.ROW_STYLE_ATTR);
+    if(vb == null)
+        return null;
+    String bindingValue = (String) vb.getValue(getFacesContext());
+    if(bindingValue == "")
+        return null;  // Fix for JSF 1.2 EL coercing nulls to empty string
+    return bindingValue;
+    }
+
+    public void setRowStyle(String rowStyle)
+    {
+        _rowStyle = rowStyle;
+    }
+
+    /**
+     * Defines a JavaScript onmpusedown event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnMouseDown();
+
+    /**
+     * Defines a JavaScript onmousemove event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnMouseMove();
+
+    /**
+     * Defines a JavaScript onmouseup event handler for each table row
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowOnMouseUp();
+
+    /**
+     * @JSFProperty
+     *   tagExcluded = "true"
+     */
+    protected boolean isValidChildren()
+    {
+        return _isValidChildren;
+    }
+
+    protected void setIsValidChildren(boolean isValidChildren)
+    {
+        _isValidChildren = isValidChildren;
+    }
+
+    protected _SerializableDataModel getPreservedDataModel()
+    {
+        return _preservedDataModel;
+    }
+
+    protected void setPreservedDataModel(_SerializableDataModel preservedDataModel)
+    {
+        _preservedDataModel = preservedDataModel;
+    }
+
+
+    public boolean isCurrentDetailExpanded()
+    {
+        Boolean expanded = (Boolean) _expandedNodes.get(new Integer(getRowIndex()));
+        if (expanded != null)
+        {
+            return expanded.booleanValue();
+        }
+
+        return isDetailStampExpandedDefault();
+    }
+
+    public void setVarDetailToggler(String varDetailToggler)
+    {
+        _varDetailToggler = varDetailToggler;
+    }
+
+    /**
+     *  This variable has the boolean property "currentdetailExpanded" 
+     *  which is true if the current detail row is expanded and the 
+     *  action method "toggleDetail" which expand/collapse the current 
+     *  detail row.
+     * 
+     * @JSFProperty
+     */
+    public String getVarDetailToggler()
+    {
+        if (_varDetailToggler != null)
+            return _varDetailToggler;
+        ValueBinding vb = getValueBinding("varDetailToggler");
+        return vb != null ? (String) vb.getValue(getFacesContext()) : null;
+    }
+
+    /**
+     * Corresponds to the HTML style attribute for grouped rows.
+     *  
+     * @JSFProperty
+     */
+    public abstract String getRowGroupStyle();
+
+    /**
+     * StyleClass for grouped rows.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowGroupStyleClass();
+    
+    /**
+     * Corresponds to the HTML style attribute for the table body tag
+     * 
+     * @JSFProperty
+     */
+    public abstract String getBodyStyle();
+
+    /**
+     * Corresponds to the HTML class attribute for the table body tag.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getBodyStyleClass();
+
+    public AbstractHtmlDataTable()
+    {
+        setRendererType(DEFAULT_RENDERER_TYPE);
+    }
+
+    /**
+     * Change the status of the current detail row from collapsed to expanded or
+     * viceversa.
+     */
+    public void toggleDetail()
+    {
+        Integer rowIndex = new Integer(getRowIndex());
+
+        // get the current expanded state of the row
+        boolean expanded = isDetailExpanded();
+        if (expanded)
+        {
+            // toggle to "collapsed"
+
+            if (isDetailStampExpandedDefault())
+            {
+                // if default is expanded we have to override with FALSE here
+                _expandedNodes.put(rowIndex, Boolean.FALSE);
+            }
+            else
+            {
+                // if default is collapsed we can fallback to this default
+                _expandedNodes.remove(rowIndex);
+            }
+        }
+        else
+        {
+            // toggle to "expanded"
+
+            if (isDetailStampExpandedDefault())
+            {
+                // if default is expanded we can fallback to this default
+                _expandedNodes.remove(rowIndex);
+            }
+            else
+            {
+                // if default is collapsed we have to override with TRUE
+                _expandedNodes.put(rowIndex, Boolean.TRUE);
+            }
+        }
+    }
+
+    /**
+     * Return true if the current detail row is expanded.
+     *
+     * @return true if the current detail row is expanded.
+     */
+    public boolean isDetailExpanded()
+    {
+        Integer rowIndex = new Integer(getRowIndex());
+
+        Boolean expanded = (Boolean) _expandedNodes.get(rowIndex);
+        if (expanded == null)
+        {
+            return isDetailStampExpandedDefault();
+        }
+
+        return expanded.booleanValue();
+    }
+
+    public int getSortColumnIndex()
+    {
+        if (_sortColumnIndex == -1)
+            _sortColumnIndex = columnNameToIndex(getSortColumn());
+
+        return _sortColumnIndex;
+    }
+
+    public void setSortColumnIndex(int sortColumnIndex)
+    {
+        _sortColumnIndex = sortColumnIndex;
+    }
+
+    /**
+     * The number of columns to wrap the table over. Default: 1
+     * 
+     * Set the number of columns the table will be divided over.
+     * 
+     * @JSFProperty
+     *   defaultValue="1"
+     */
+    public abstract int getNewspaperColumns();
+
+    /**
+     * The orientation of the newspaper columns in the newspaper 
+     * table - "horizontal" or "vertical". Default: vertical
+     * 
+     * @JSFProperty
+     *   defaultValue = "vertical"
+     */
+    public abstract String getNewspaperOrientation();
+
+    /**
+     * Gets the spacer facet, between each pair of newspaper columns.
+     * 
+     * @JSFFacet
+     *   name="spacer"
+     */
+    public UIComponent getSpacer()
+    {
+        return (UIComponent) getFacets().get(SPACER_FACET_NAME);
+    }
+
+    public void setSpacer(UIComponent spacer)
+    {
+        getFacets().put(SPACER_FACET_NAME, spacer);
+    }
+
+    /**
+     * Expand all details
+     */
+    public void expandAllDetails()
+    {
+        int rowCount = getRowCount();
+
+        _expandedNodes.clear();
+        for (int row = 0; row < rowCount; row++)
+        {
+            _expandedNodes.put(new Integer(row), Boolean.TRUE);
+        }
+    }
+
+    /**
+     * Collapse all details
+     */
+    public void collapseAllDetails()
+    {
+        _expandedNodes.clear();
+    }
+
+    /**
+     * @return true is any of the details is expanded
+     */
+    public boolean isExpandedEmpty()
+    {
+        boolean expandedEmpty = true;
+        if (_expandedNodes != null)
+        {
+            expandedEmpty = _expandedNodes.isEmpty();
+        }
+        return expandedEmpty;
+    }
+
+    /**
+     * Clears expanded nodes set if expandedEmpty is true
+     *
+     * @param expandedEmpty
+     */
+    public void setExpandedEmpty(boolean expandedEmpty)
+    {
+        if (expandedEmpty)
+        {
+            if (_expandedNodes != null)
+            {
+                _expandedNodes.clear();
+            }
+        }
+    }
+
+    //------------------ GENERATED CODE BEGIN (do not modify!) --------------------
+
+    public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlDataTable";
+    public static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.Table";
+
+    private static final boolean DEFAULT_PRESERVEDATAMODEL = false;
+    private static final boolean DEFAULT_PRESERVESORT = true;
+    private static final boolean DEFAULT_RENDEREDIFEMPTY = true;
+
+    private Boolean _preserveDataModel = null;
+    private Boolean _preserveSort = null;
+
+    public void setPreserveDataModel(boolean preserveDataModel)
+    {
+        _preserveDataModel = Boolean.valueOf(preserveDataModel);
+    }
+
+    /**
+     * Indicates whether the state of the whole DataModel should 
+     * be saved and restored. When set to false, the value-binding 
+     * for the "value" attribute of this table is executed each 
+     * time the page is rendered. When set to true, that 
+     * value-binding is only executed when the component is first 
+     * created, and the DataModel state is thereafter saved/restored 
+     * automatically by the component. When column sorting is 
+     * used for a table this property needs to be false so that 
+     * the DataModel can be updated to reflect any changes in the 
+     * sort criteria. Default: false
+     * 
+     * @JSFProperty
+     */
+    public boolean isPreserveDataModel()
+    {
+        if (_preserveDataModel != null)
+            return _preserveDataModel.booleanValue();
+        ValueBinding vb = getValueBinding("preserveDataModel");
+        Boolean v = vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
+        return v != null ? v.booleanValue() : DEFAULT_PRESERVEDATAMODEL;
+    }
+
+    public void setPreserveSort(boolean preserveSort)
+    {
+        _preserveSort = Boolean.valueOf(preserveSort);
+    }
+    
+    /**
+     * Indicates whether the state of the sortColumn and sortAscending 
+     * attribute should be saved and restored and written back to the 
+     * model during the update model phase. Default: true
+     * 
+     * @JSFProperty
+     *   defaultValue = "true"
+     */
+    public boolean isPreserveSort()
+    {
+        if (_preserveSort != null)
+            return _preserveSort.booleanValue();
+        ValueBinding vb = getValueBinding("preserveSort");
+        Boolean v = vb != null ? (Boolean) vb.getValue(getFacesContext()) : null;
+        return v != null ? v.booleanValue() : DEFAULT_PRESERVESORT;
+    }
+
+    /**
+     * Indicates whether this table should be rendered if the 
+     * underlying DataModel is empty. You could as well use 
+     * rendered="#{not empty bean.list}", but this one causes 
+     * the getList method of your model bean beeing called up 
+     * to five times per request, which is not optimal when 
+     * the list is backed by a DB table. Using 
+     * renderedIfEmpty="false" solves this problem, because 
+     * the MyFaces extended HtmlDataTable automatically caches 
+     * the DataModel and calles the model getter only once 
+     * per request. Default: true
+     * 
+     * @JSFProperty
+     *   defaultValue = "true"
+     */
+    public abstract boolean isRenderedIfEmpty();
+
+    /**
+     * A parameter name, under which the current rowIndex is set 
+     * in request scope similar to the var parameter.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowIndexVar();
+    
+    /**
+     * A parameter name, under which the rowCount is set in 
+     * request scope similar to the var parameter.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowCountVar();
+
+    /**
+     * A parameter name, under which the previous RowData Object 
+     * is set in request scope similar to the rowIndexVar and 
+     * rowCountVar parameters. Mind that the value of this 
+     * request scope attribute is null in the first row or 
+     * when isRowAvailable returns false for the previous row.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getPreviousRowDataVar();
+
+    /**
+     * A parameter name, under which the a boolean is set in request 
+     * scope similar to the var parameter. TRUE for the column that 
+     * is currently sorted, FALSE otherwise.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getSortedColumnVar();
+    
+    /**
+     * HTML: Specifies the horizontal alignment of this element. 
+     * Deprecated in HTML 4.01.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getAlign();
+
+    /**
+     * The id to use for
+     * 
+     * @JSFProperty
+     */
+    public abstract String getRowId();
+        
+    /**
+     * Reserved for future use.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getDatafld();
+    
+    /**
+     * Reserved for future use.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getDatasrc();
+    
+    /**
+     * Reserved for future use.
+     * 
+     * @JSFProperty
+     */
+    public abstract String getDataformatas();    
+}

Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java?rev=904296&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java Thu Jan 28 23:13:17 2010
@@ -0,0 +1,701 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.component.EditableValueHolder;
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+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;
+
+import org.apache.myfaces.component.ForceIdAware;
+import org.apache.myfaces.component.html.ext.HtmlCommandButton.PropertyKeys;
+import org.apache.myfaces.component.html.util.HtmlComponentUtils;
+import org.apache.myfaces.custom.ExtendedComponentBase;
+
+/**
+ * Reimplement all UIData functionality to be able to have (protected) access
+ * the internal DataModel.
+ *
+ * @JSFComponent
+ *  configExcluded = "true"
+ *
+ * @author Manfred Geiler (latest modification by $Author: lu4242 $)
+ * @version $Revision: 691856 $ $Date: 2008-09-03 21:40:30 -0500 (mié, 03 sep 2008) $
+ */
+public abstract class HtmlDataTableHack extends
+                javax.faces.component.html.HtmlDataTable implements
+                ExtendedComponentBase
+                
+{
+    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;
+
+    // holds for each row the states of the child components of this UIData
+    private Map _rowStates = new HashMap();
+
+    // contains the initial row state which is used to initialize each row
+    private Object _initialDescendantComponentState = null;
+
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    // Every field and method from here is identical to UIData !!!!!!!!!
+    // EXCEPTION: we can create a DataModel from a Collection
+
+    private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
+
+    private static final boolean DEFAULT_PRESERVEROWSTATES = false;
+
+    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;
+    }
+    
+    /**
+     * Hack since RI does not call getRowIndex()
+     */
+    public String getClientId(FacesContext context)
+    {
+        String clientId = HtmlComponentUtils.getClientId(this, getRenderer(context), context);
+        if (clientId == null)
+        {
+            clientId = super.getClientId(context);
+        }
+        int rowIndex = getRowIndex();
+        if (rowIndex == -1)
+        {
+            return clientId;
+        }
+        // the following code tries to avoid rowindex to be twice in the client id
+        int index = clientId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
+        if(index != -1)
+        {
+            String rowIndexString = clientId.substring(index + 1);
+            try
+            {
+                if(Integer.parseInt(rowIndexString) == rowIndex)
+                {
+                    return clientId;
+                }
+            }
+            catch(NumberFormatException e)
+            {
+                return clientId + NamingContainer.SEPARATOR_CHAR + rowIndex;
+            }
+        }
+        return clientId + NamingContainer.SEPARATOR_CHAR + 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;
+        }
+    }
+    
+    /**
+     * This method is used when a custom processUpdates and processValidators
+     * is defined, to check if a update model error forces the render 
+     * response for our data, because _isValidChilds is a private field
+     * and is not available on child components that inherits this 
+     * component class like t:dataList. (TOMAHAWK-1225)
+     */
+    protected void checkUpdateModelError(FacesContext context)
+    {
+        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
+    {
+        _initialDescendantComponentState = null;
+        if (_isValidChilds && !hasErrorMessages(context))
+        {
+            //Refresh DataModel for rendering:
+            _dataModelMap.clear();
+            if (!isPreserveRowStates())
+            {
+                _rowStates.clear();
+            }
+        }
+        super.encodeBegin(context);
+    }
+
+    public void setPreserveRowStates(boolean preserveRowStates)
+    {
+        getStateHelper().put(PropertyKeys.preserveRowStates, preserveRowStates);
+    }
+
+    /**
+     * Indicates whether the state for each row should not be 
+     * discarded before the datatable is rendered again. 
+     * 
+     * Setting this to true might be hepful if an input 
+     * component inside the datatable has no valuebinding and 
+     * the value entered in there should be displayed again.
+     *  
+     * This will only work reliable if the datamodel of the 
+     * datatable did not change either by sorting, removing or 
+     * adding rows. Default: false
+     * 
+     * @JSFProperty
+     *   defaultValue="false"
+     */
+    public boolean isPreserveRowStates()
+    {
+        Object value = getStateHelper().eval(PropertyKeys.preserveRowStates,DEFAULT_PRESERVEROWSTATES);
+        if (value != null)
+        {
+            return (Boolean) value;
+        }
+        return DEFAULT_PRESERVEROWSTATES;
+    }
+
+    protected boolean hasErrorMessages(FacesContext context)
+    {
+        for(Iterator iter = context.getMessages(); iter.hasNext();)
+        {
+            FacesMessage message = (FacesMessage) iter.next();
+            if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @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 < -1)
+        {
+            throw new IllegalArgumentException("rowIndex is less than -1");
+        }
+
+        if (_rowIndex == rowIndex)
+        {
+            return;
+        }
+
+        FacesContext facesContext = getFacesContext();
+
+        if (_rowIndex == -1)
+        {
+            if (_initialDescendantComponentState == null)
+            {
+                _initialDescendantComponentState = saveDescendantComponentStates(getChildren()
+                                .iterator(), false);
+            }
+        }
+        else
+        {
+            _rowStates.put(getClientId(facesContext),
+                            saveDescendantComponentStates(getChildren()
+                                            .iterator(), false));
+        }
+
+        _rowIndex = rowIndex;
+
+        DataModel dataModel = getDataModel();
+        dataModel.setRowIndex(rowIndex);
+
+        String var = getVar();
+        if (rowIndex == -1)
+        {
+            if (var != null)
+            {
+                facesContext.getExternalContext().getRequestMap().remove(var);
+            }
+        }
+        else
+        {
+            if (var != null)
+            {
+                if (isRowAvailable())
+                {
+                    Object rowData = dataModel.getRowData();
+                    facesContext.getExternalContext().getRequestMap().put(var,
+                                    rowData);
+                }
+                else
+                {
+                    facesContext.getExternalContext().getRequestMap().remove(
+                                    var);
+                }
+            }
+        }
+
+        if (_rowIndex == -1)
+        {
+            restoreDescendantComponentStates(getChildren().iterator(),
+                            _initialDescendantComponentState, false);
+        }
+        else
+        {
+            Object rowState = _rowStates.get(getClientId(facesContext));
+            if (rowState == null)
+            {
+                restoreDescendantComponentStates(getChildren().iterator(),
+                                _initialDescendantComponentState, false);
+            }
+            else
+            {
+                restoreDescendantComponentStates(getChildren().iterator(),
+                                rowState, false);
+            }
+        }
+    }
+
+    protected void restoreDescendantComponentStates(Iterator childIterator,
+            Object state, boolean restoreChildFacets)
+    {
+        Iterator descendantStateIterator = null;
+        while (childIterator.hasNext())
+        {
+            if (descendantStateIterator == null && state != null)
+            {
+                descendantStateIterator = ((Collection) state).iterator();
+            }
+            UIComponent component = (UIComponent) childIterator.next();
+            // reset the client id (see spec 3.1.6)
+            component.setId(component.getId());
+            if(!component.isTransient())
+            {
+                Object childState = null;
+                Object descendantState = null;
+                if (descendantStateIterator != null
+                        && descendantStateIterator.hasNext())
+                {
+                    Object[] object = (Object[]) descendantStateIterator.next();
+                    childState = object[0];
+                    descendantState = object[1];
+                }
+                if (childState != null && component instanceof EditableValueHolder)
+                {
+                    ((EditableValueHolderState) childState)
+                            .restoreState((EditableValueHolder) component);
+                }
+                Iterator childsIterator;
+                if (restoreChildFacets)
+                {
+                    childsIterator = component.getFacetsAndChildren();
+                }
+                else
+                {
+                    childsIterator = component.getChildren().iterator();
+                }
+                restoreDescendantComponentStates(childsIterator, descendantState,
+                        true);
+            }
+        }
+    }
+
+    protected Object saveDescendantComponentStates(Iterator childIterator,
+            boolean saveChildFacets)
+    {
+        Collection childStates = null;
+        while (childIterator.hasNext())
+        {
+            if (childStates == null)
+            {
+                childStates = new ArrayList();
+            }
+            UIComponent child = (UIComponent) childIterator.next();
+            if(!child.isTransient())
+            {
+                Iterator childsIterator;
+                if (saveChildFacets)
+                {
+                    childsIterator = child.getFacetsAndChildren();
+                }
+                else
+                {
+                    childsIterator = child.getChildren().iterator();
+                }
+                Object descendantState = saveDescendantComponentStates(
+                        childsIterator, true);
+                Object state = null;
+                if (child instanceof EditableValueHolder)
+                {
+                    state = new EditableValueHolderState(
+                            (EditableValueHolder) child);
+                }
+                childStates.add(new Object[] { state, descendantState });
+            }
+        }
+        return childStates;
+    }
+
+    public void 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(
+                    "You can never set the 'rowIndex' or the 'var' attribute as a value-binding. Set the property directly instead. Name " + name);
+        }
+        super.setValueBinding(name, binding);
+    }
+
+    /**
+     * @see javax.faces.component.UIData#setValue(java.lang.Object)
+     */
+    public void setValue(Object value)
+    {
+        super.setValue(value);
+        _dataModelMap.clear();
+        _rowStates.clear();
+        _isValidChilds = true;
+    }
+
+    protected DataModel getDataModel()
+    {
+        DataModel dataModel = null;
+        String clientID = "";
+        
+        UIComponent parent = getParent();
+        if (parent != null) 
+        {
+            clientID = parent.getClientId(getFacesContext());
+        }
+        dataModel = (DataModel) _dataModelMap.get(clientID);
+        if (dataModel == null)
+        {
+            dataModel = createDataModel();
+            _dataModelMap.put(clientID, dataModel);
+        }               
+        
+        return dataModel;
+    }
+
+    protected void setDataModel(DataModel datamodel)
+    {
+        UIComponent parent = getParent();
+        String clientID = "";
+        if(parent != null)
+        {
+            clientID = parent.getClientId(getFacesContext());
+        }
+        _dataModelMap.put(clientID, 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);
+        }
+        // accept a Collection is not supported in the Spec
+        else if (value instanceof Collection)
+        {
+            return new ListDataModel(new ArrayList((Collection) 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 _SerializableDataModel()
+    {
+        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 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);
+        }
+    }
+    
+    // Property: forceId
+    
+    /**
+     * If true, this component will force the use of the specified id when rendering.
+     * 
+     * @JSFProperty
+     *   literalOnly = "true"
+     *   defaultValue = "false"
+     *   
+     * @return
+     */
+    public boolean isForceId()
+    {
+        Object value = getStateHelper().get(PropertyKeys.forceId);
+        if (value != null)
+        {
+            return (Boolean) value;        
+        }
+        return false;  
+    }
+
+    public void setForceId(boolean forceId)
+    {
+        getStateHelper().put(PropertyKeys.forceId, forceId );
+    }
+    // Property: forceIdIndex
+    
+    /**
+     * If false, this component will not append a '[n]' suffix 
+     * (where 'n' is the row index) to components that are 
+     * contained within a "list." This value will be true by 
+     * default and the value will be ignored if the value of 
+     * forceId is false (or not specified.)
+     * 
+     * @JSFProperty
+     *   literalOnly = "true"
+     *   defaultValue = "true"
+     *   
+     * @return
+     */
+    public boolean isForceIdIndex()
+    {
+        Object value = getStateHelper().get(PropertyKeys.forceIdIndex);
+        if (value != null)
+        {
+            return (Boolean) value;        
+        }
+        return true;
+    }
+
+    public void setForceIdIndex(boolean forceIdIndex)
+    {
+        getStateHelper().put(PropertyKeys.forceIdIndex, forceIdIndex );
+    }
+    
+    private static boolean booleanFromObject(Object obj, boolean defaultValue)
+    {
+        if(obj instanceof Boolean)
+        {
+            return ((Boolean) obj).booleanValue();
+        }
+        else if(obj instanceof String)
+        {
+            return Boolean.valueOf(((String) obj)).booleanValue();
+        }
+
+        return defaultValue;
+    }
+
+    /**
+     * Remove all preserved row state for the dataTable
+     */
+    public void clearRowStates()
+    {
+        _rowStates.clear();
+    }
+    
+    /**
+     * Remove preserved row state for deleted row and adjust row state to reflect deleted row.
+     * @param deletedIndex index of row to delete
+     */
+    public void deleteRowStateForRow(int deletedIndex)
+    {
+        // save row index
+        int savedRowIndex = getRowIndex();
+        
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+         setRowIndex(deletedIndex);
+        String currentRowStateKey = getClientId(facesContext);
+
+        // copy next rowstate to current row for each row from deleted row onward.
+        int rowCount = getRowCount();
+        for (int index = deletedIndex + 1; index < rowCount; ++index)
+        {
+            setRowIndex(index);
+            String nextRowStateKey = getClientId(facesContext);
+
+            Object nextRowState = _rowStates.get(nextRowStateKey);
+            if (nextRowState == null)
+            {
+                _rowStates.remove(currentRowStateKey);
+            }
+            else
+            {
+                _rowStates.put(currentRowStateKey, nextRowState);
+            }
+            currentRowStateKey = nextRowStateKey;
+        }
+
+        // Remove last row
+        _rowStates.remove(currentRowStateKey);
+
+        // restore saved row index
+        setRowIndex(savedRowIndex);
+    }
+    
+    protected enum PropertyKeys
+    {
+        preserveRowStates
+        , forceId
+        , forceIdIndex
+    }
+}

Modified: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/Alias.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/Alias.java?rev=904296&r1=904295&r2=904296&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/Alias.java (original)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/Alias.java Thu Jan 28 23:13:17 2010
@@ -21,7 +21,6 @@
 import javax.el.ValueExpression;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
-import javax.faces.el.ValueBinding;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;

Modified: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/AliasBean.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/AliasBean.java?rev=904296&r1=904295&r2=904296&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/AliasBean.java (original)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/aliasbean/AliasBean.java Thu Jan 28 23:13:17 2010
@@ -42,7 +42,6 @@
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
 import org.apache.myfaces.shared_tomahawk.component.BindingAware;
 import org.apache.myfaces.shared_tomahawk.util.RestoreStateUtils;
-import org.apache.myfaces.shared_tomahawk.util._ComponentUtils;
 
 /**
  * The aliasBean tag allows you to create a temporary name for a real bean.