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.