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 2011/03/16 22:39:29 UTC
svn commit: r1082312 - in /myfaces/tomahawk/trunk/core20/src:
main/java/org/apache/myfaces/component/html/ext/
main/java/org/apache/myfaces/custom/datalist/
test/java/org/apache/myfaces/component/html/ext/
Author: lu4242
Date: Wed Mar 16 21:39:29 2011
New Revision: 1082312
URL: http://svn.apache.org/viewvc?rev=1082312&view=rev
Log:
TOMAHAWK-961 Deleting a row when t:dataList/t:dataTable preserveRowStates=true assigns submitted values to wrong row (see TOMAHAWK-1552 for details)
Added:
myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java
Modified:
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/datalist/AbstractHtmlDataList.java
myfaces/tomahawk/trunk/core20/src/test/java/org/apache/myfaces/component/html/ext/HtmlDataTablePreserveRowComponentStateTest.java
Modified: 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=1082312&r1=1082311&r2=1082312&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java (original)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java Wed Mar 16 21:39:29 2011
@@ -120,7 +120,7 @@ public abstract class AbstractHtmlDataTa
private boolean _isValidChildren = true;
- private Map<Integer, Boolean> _expandedNodes = new HashMap<Integer, Boolean>();
+ private Map<String, Boolean> _expandedNodes = new HashMap<String, Boolean>();
//private Map<String, Object> _detailRowStates = new HashMap<String, Object>();
@@ -245,7 +245,49 @@ public abstract class AbstractHtmlDataTa
// Check if the clientId for the component, which we
// are looking for, has a rowIndex attached
char separator = UINamingContainer.getSeparatorChar(context);
- if (clientId.matches(baseClientId + separator+"[0-9]+"+separator+".*"))
+
+ ValueExpression rowKeyVE = getValueExpression("rowKey");
+ boolean rowKeyFound = false;
+
+ if (rowKeyVE != null)
+ {
+ int oldRow = this.getRowIndex();
+ try
+ {
+ // iterate over the rows
+ int rowsToProcess = getRows();
+ // if getRows() returns 0, all rows have to be processed
+ if (rowsToProcess == 0)
+ {
+ rowsToProcess = getRowCount();
+ }
+ int rowIndex = getFirst();
+ for (int rowsProcessed = 0; rowsProcessed < rowsToProcess; rowsProcessed++, rowIndex++)
+ {
+ setRowIndex(rowIndex);
+ if (!isRowAvailable())
+ {
+ break;
+ }
+
+ if (clientId.startsWith(getContainerClientId(context)))
+ {
+ rowKeyFound = true;
+ break;
+ }
+ }
+
+ if (rowKeyFound)
+ {
+ returnValue = invokeOnComponentTraverseRow(context, clientId, callback);
+ }
+ }
+ finally
+ {
+ this.setRowIndex(oldRow);
+ }
+ }
+ if (rowKeyVE == null && clientId.matches(baseClientId + separator+"[0-9]+"+separator+".*"))
{
String subId = clientId.substring(baseClientId.length() + 1);
String clientRow = subId.substring(0, subId.indexOf(separator));
@@ -266,31 +308,7 @@ public abstract class AbstractHtmlDataTa
return false;
}
- for (Iterator<UIComponent> it1 = getChildren().iterator();
- !returnValue && it1.hasNext();)
- {
- //recursive call to find the component
- returnValue = it1.next().invokeOnComponent(context, clientId, callback);
- }
-
- if (!returnValue)
- {
- UIComponent detailStampRowFacet = getFacet(DETAIL_STAMP_ROW_FACET_NAME);
- if (detailStampRowFacet != null)
- {
- returnValue = detailStampRowFacet.invokeOnComponent(context, clientId, callback);
- }
- UIComponent detailStampFacet = getFacet(DETAIL_STAMP_FACET_NAME);
- if (detailStampFacet != null)
- {
- returnValue = detailStampFacet.invokeOnComponent(context, clientId, callback);
- }
- UIComponent tableRowFacet = getFacet(TABLE_ROW_FACET_NAME);
- if (tableRowFacet != null)
- {
- returnValue = tableRowFacet.invokeOnComponent(context, clientId, callback);
- }
- }
+ returnValue = invokeOnComponentTraverseRow(context, clientId, callback);
}
finally
{
@@ -299,7 +317,7 @@ public abstract class AbstractHtmlDataTa
this.setRowIndex(oldRow);
}
}
- else
+ else if (!rowKeyFound) //If rowKeyVE == null --> rowKeyFound = false
{
// MYFACES-2370: search the component in the childrens' facets too.
// We have to check the childrens' facets here, because in MyFaces
@@ -343,6 +361,38 @@ public abstract class AbstractHtmlDataTa
return returnValue;
}
+ private boolean invokeOnComponentTraverseRow(FacesContext context, String clientId,
+ ContextCallback callback)
+ {
+ boolean returnValue = false;
+ for (Iterator<UIComponent> it1 = getChildren().iterator();
+ !returnValue && it1.hasNext();)
+ {
+ //recursive call to find the component
+ returnValue = it1.next().invokeOnComponent(context, clientId, callback);
+ }
+
+ if (!returnValue)
+ {
+ UIComponent detailStampRowFacet = getFacet(DETAIL_STAMP_ROW_FACET_NAME);
+ if (detailStampRowFacet != null)
+ {
+ returnValue = detailStampRowFacet.invokeOnComponent(context, clientId, callback);
+ }
+ UIComponent detailStampFacet = getFacet(DETAIL_STAMP_FACET_NAME);
+ if (detailStampFacet != null)
+ {
+ returnValue = detailStampFacet.invokeOnComponent(context, clientId, callback);
+ }
+ UIComponent tableRowFacet = getFacet(TABLE_ROW_FACET_NAME);
+ if (tableRowFacet != null)
+ {
+ returnValue = tableRowFacet.invokeOnComponent(context, clientId, callback);
+ }
+ }
+ return returnValue;
+ }
+
public boolean visitTree(VisitContext context, VisitCallback callback)
{
if (!isVisitable(context))
@@ -1917,7 +1967,7 @@ public abstract class AbstractHtmlDataTa
public boolean isCurrentDetailExpanded()
{
- Boolean expanded = (Boolean) _expandedNodes.get(new Integer(getRowIndex()));
+ Boolean expanded = (Boolean) _expandedNodes.get(getContainerClientId(getFacesContext()));
if (expanded != null)
{
return expanded.booleanValue();
@@ -1983,7 +2033,7 @@ public abstract class AbstractHtmlDataTa
*/
public void toggleDetail()
{
- Integer rowIndex = new Integer(getRowIndex());
+ String derivedRowKey = getContainerClientId(getFacesContext());
// get the current expanded state of the row
boolean expanded = isDetailExpanded();
@@ -1994,12 +2044,12 @@ public abstract class AbstractHtmlDataTa
if (isDetailStampExpandedDefault())
{
// if default is expanded we have to override with FALSE here
- _expandedNodes.put(rowIndex, Boolean.FALSE);
+ _expandedNodes.put(derivedRowKey, Boolean.FALSE);
}
else
{
// if default is collapsed we can fallback to this default
- _expandedNodes.remove(rowIndex);
+ _expandedNodes.remove(derivedRowKey);
}
}
else
@@ -2009,12 +2059,12 @@ public abstract class AbstractHtmlDataTa
if (isDetailStampExpandedDefault())
{
// if default is expanded we can fallback to this default
- _expandedNodes.remove(rowIndex);
+ _expandedNodes.remove(derivedRowKey);
}
else
{
// if default is collapsed we have to override with TRUE
- _expandedNodes.put(rowIndex, Boolean.TRUE);
+ _expandedNodes.put(derivedRowKey, Boolean.TRUE);
}
}
}
@@ -2026,9 +2076,7 @@ public abstract class AbstractHtmlDataTa
*/
public boolean isDetailExpanded()
{
- Integer rowIndex = new Integer(getRowIndex());
-
- Boolean expanded = (Boolean) _expandedNodes.get(rowIndex);
+ Boolean expanded = (Boolean) _expandedNodes.get(getContainerClientId(getFacesContext()));
if (expanded == null)
{
return isDetailStampExpandedDefault();
@@ -2096,9 +2144,29 @@ public abstract class AbstractHtmlDataTa
int rowCount = getRowCount();
_expandedNodes.clear();
- for (int row = 0; row < rowCount; row++)
+
+ if (getRowKey() != null)
{
- _expandedNodes.put(new Integer(row), Boolean.TRUE);
+ int oldRow = getRowIndex();
+ try
+ {
+ for (int row = 0; row < rowCount; row++)
+ {
+ setRowIndex(row);
+ _expandedNodes.put(getContainerClientId(getFacesContext()), Boolean.TRUE);
+ }
+ }
+ finally
+ {
+ setRowIndex(oldRow);
+ }
+ }
+ else
+ {
+ for (int row = 0; row < rowCount; row++)
+ {
+ _expandedNodes.put(new Integer(row).toString(), Boolean.TRUE);
+ }
}
}
Modified: 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=1082312&r1=1082311&r2=1082312&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java (original)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java Wed Mar 16 21:39:29 2011
@@ -88,6 +88,8 @@ public abstract class HtmlDataTableHack
private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
private static final boolean DEFAULT_PRESERVEROWSTATES = false;
+
+ private static final String UNIQUE_ROW_ID_PREFIX = "r_id_";
private int _rowIndex = -1;
@@ -162,14 +164,17 @@ public abstract class HtmlDataTableHack
if(index != -1)
{
String rowIndexString = clientId.substring(index + 1);
+
if(rowIndexString.length() > 0 &&
StringUtils.isNumeric(rowIndexString) &&
Integer.valueOf(rowIndexString) == rowIndex)
{
- return clientId;
+ //return clientId;
+ return clientId.substring(0,index)+getDerivedSubClientId();
}
}
- return clientId + separator + rowIndex;
+ //return clientId + separator + rowIndex;
+ return clientId + separator + getDerivedSubClientId();
}
/**
@@ -1111,57 +1116,74 @@ public abstract class HtmlDataTableHack
setRowIndex(deletedIndex);
String currentRowStateKey = getContainerClientId(facesContext);
- // copy next rowstate to current row for each row from deleted row onward.
- int rowCount = getRowCount();
- if (isPreserveRowComponentState())
+ Object rowKey = getRowKey();
+ if (rowKey != null)
{
- for (int index = deletedIndex + 1; index < rowCount; ++index)
+ setRowIndex(deletedIndex);
+ if (isPreserveRowComponentState())
{
- setRowIndex(index);
- String nextRowStateKey = getContainerClientId(facesContext);
-
- Map<String, Object> nextRowState = _rowDeltaStates.get(nextRowStateKey);
- if (nextRowState == null)
- {
- _rowDeltaStates.remove(currentRowStateKey);
- }
- else
- {
- _rowDeltaStates.put(currentRowStateKey, nextRowState);
- }
- currentRowStateKey = nextRowStateKey;
+ _rowDeltaStates.remove(currentRowStateKey);
+ }
+ else
+ {
+ _rowStates.remove(currentRowStateKey);
}
-
- // restore saved row index
setRowIndex(savedRowIndex);
-
- // Remove last row
- _rowDeltaStates.remove(currentRowStateKey);
}
else
{
- for (int index = deletedIndex + 1; index < rowCount; ++index)
+ // copy next rowstate to current row for each row from deleted row onward.
+ int rowCount = getRowCount();
+ if (isPreserveRowComponentState())
{
- setRowIndex(index);
- String nextRowStateKey = getContainerClientId(facesContext);
-
- Object nextRowState = _rowStates.get(nextRowStateKey);
- if (nextRowState == null)
+ for (int index = deletedIndex + 1; index < rowCount; ++index)
{
- _rowStates.remove(currentRowStateKey);
+ setRowIndex(index);
+ String nextRowStateKey = getContainerClientId(facesContext);
+
+ Map<String, Object> nextRowState = _rowDeltaStates.get(nextRowStateKey);
+ if (nextRowState == null)
+ {
+ _rowDeltaStates.remove(currentRowStateKey);
+ }
+ else
+ {
+ _rowDeltaStates.put(currentRowStateKey, nextRowState);
+ }
+ currentRowStateKey = nextRowStateKey;
}
- else
+
+ // restore saved row index
+ setRowIndex(savedRowIndex);
+
+ // Remove last row
+ _rowDeltaStates.remove(currentRowStateKey);
+ }
+ else
+ {
+ for (int index = deletedIndex + 1; index < rowCount; ++index)
{
- _rowStates.put(currentRowStateKey, nextRowState);
+ setRowIndex(index);
+ String nextRowStateKey = getContainerClientId(facesContext);
+
+ Object nextRowState = _rowStates.get(nextRowStateKey);
+ if (nextRowState == null)
+ {
+ _rowStates.remove(currentRowStateKey);
+ }
+ else
+ {
+ _rowStates.put(currentRowStateKey, nextRowState);
+ }
+ currentRowStateKey = nextRowStateKey;
}
- currentRowStateKey = nextRowStateKey;
- }
-
- // restore saved row index
- setRowIndex(savedRowIndex);
- // Remove last row
- _rowStates.remove(currentRowStateKey);
+ // restore saved row index
+ setRowIndex(savedRowIndex);
+
+ // Remove last row
+ _rowStates.remove(currentRowStateKey);
+ }
}
}
@@ -1188,13 +1210,80 @@ public abstract class HtmlDataTableHack
public void setPreserveRowComponentState(boolean preserveComponentState)
{
getStateHelper().put(PropertyKeys.preserveRowComponentState, preserveComponentState);
- }
+ }
+
+ /**
+ * Used to assign a value expression that identify in a unique way a row. This value
+ * will be used later instead of rowIndex as a key to be appended to the container
+ * client id using getDerivedSubClientId() method.
+ *
+ * @return
+ */
+ @JSFProperty
+ public Object getRowKey()
+ {
+ return getStateHelper().eval(PropertyKeys.rowKey);
+ }
+
+ public void setRowKey(Object rowKey)
+ {
+ getStateHelper().put(PropertyKeys.rowKey, rowKey);
+ }
+
+ /**
+ * This attribute is used to append an unique prefix when rowKey is not used, to prevent
+ * a key match a existing component id (note two different components can't have the
+ * same unique id).
+ *
+ * @return
+ */
+ @JSFProperty(defaultValue="r_id_")
+ public String getDerivedRowKeyPrefix()
+ {
+ return (String) getStateHelper().eval(PropertyKeys.derivedRowKeyPrefix, UNIQUE_ROW_ID_PREFIX);
+ }
+ public void setDerivedRowKeyPrefix(String derivedRowKeyPrefix)
+ {
+ getStateHelper().put(PropertyKeys.derivedRowKeyPrefix, derivedRowKeyPrefix);
+ }
+
+ /**
+ * Return the fragment to be used on the container client id to
+ * identify a row. As a side effect, it will be used to indicate
+ * a row component state and a datamodel in nested datatable case.
+ *
+ * <p>
+ * The returned value must comply with the following rules:
+ * </p>
+ * <ul>
+ * <li> Can be followed by: letters (A-Za-z), digits (0-9), hyphens ("-"),
+ * underscores ("_"), colons (":"), and periods (".") </li>
+ * <li> Values are case-sensitive </li>
+ * </ul>
+ *
+ * @return
+ */
+ protected String getDerivedSubClientId()
+ {
+ Object key = getRowKey();
+ if (key == null)
+ {
+ return _SubIdConverter.encode(Integer.toString(getRowIndex()));
+ }
+ else
+ {
+ return getDerivedRowKeyPrefix() + _SubIdConverter.encode(key.toString());
+ }
+ }
+
protected enum PropertyKeys
{
preserveRowStates
, forceId
, forceIdIndex
, preserveRowComponentState
+ , rowKey
+ , derivedRowKeyPrefix
}
}
Added: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java?rev=1082312&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java (added)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java Wed Mar 16 21:39:29 2011
@@ -0,0 +1,169 @@
+/*
+ * 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 javax.faces.FacesException;
+
+class _SubIdConverter
+{
+ private static final String HEX_CHARSET = "0123456789ABCDEF";
+
+ /**
+ * Encode the string into an html sub id valid value.
+ *
+ * An html id must comply with the following rules
+ *
+ * 1. Must begin with a letter A-Z or a-z
+ * 2. Can be followed by: letters (A-Za-z), digits (0-9), hyphens ("-"), underscores ("_"), colons (":"), and periods (".")
+ * 3. Values are case-sensitive
+ *
+ * The first rule is warranted because this convert an sub id, so a prefix is always added to
+ * the returning string. The encoder converts all non valid chars into a unicode hex string prefixed with '_' char.
+ * For example _ is converted to _005F, + is converted to _002B and so on.
+ *
+ * @param string
+ * @param characterEncoding
+ * @return
+ */
+ public static String encode(final String string)
+ {
+ StringBuilder sb = null; //create later on demand
+ String app;
+ char c;
+ boolean endLoop = false;
+ for (int i = 0; i < string.length (); ++i)
+ {
+ app = null;
+ c = string.charAt(i);
+
+ if (( c >= '0' && c <='9') || (c >='A' && c <='Z') || (c >='a' && c <='z')
+ || c == ':' || c == '.' ) //|| c == '-' // '-' used to indicate rowIndex
+ {
+ //No encoding, just do nothing, char will be added later.
+ }
+ else
+ {
+
+ app = "_" + HEX_CHARSET.charAt( ((c >> 0x0C) % 0x10)) + HEX_CHARSET.charAt( ((c >> 0x8) % 0x10)) + HEX_CHARSET.charAt( ((c >> 0x4) % 0x10)) +HEX_CHARSET.charAt(c % 0x10);
+ }
+
+ if (app != null)
+ {
+ if (sb == null)
+ {
+ sb = new StringBuilder(string.substring(0, i));
+ }
+ sb.append(app);
+ } else {
+ if (sb != null)
+ {
+ sb.append(c);
+ }
+ }
+ if (endLoop)
+ {
+ break;
+ }
+ }
+ if (sb == null)
+ {
+ return string;
+ }
+ else
+ {
+ return sb.toString();
+ }
+ }
+
+ public static String decode(final String string)
+ {
+ StringBuilder sb = null; //create later on demand
+ String app;
+ char c;
+ boolean endLoop = false;
+ for (int i = 0; i < string.length (); ++i)
+ {
+ app = null;
+ c = string.charAt(i);
+
+ if (c == '_')
+ {
+ int value = (toDigit(string.charAt(i+1)) << 0x0C) + (toDigit(string.charAt(i+2)) << 0x08) + (toDigit(string.charAt(i+3)) << 0x04) + toDigit(string.charAt(i+4));
+
+ if (sb == null)
+ {
+ sb = new StringBuilder(string.substring(0, i));
+ }
+ i += 4;
+ app = ""+((char)value);
+ }
+ else
+ {
+ //No decoding
+ }
+
+ if (app != null)
+ {
+ if (sb == null)
+ {
+ sb = new StringBuilder(string.substring(0, i));
+ }
+ sb.append(app);
+ } else {
+ if (sb != null)
+ {
+ sb.append(c);
+ }
+ }
+ if (endLoop)
+ {
+ break;
+ }
+ }
+ if (sb == null)
+ {
+ return string;
+ }
+ else
+ {
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Converts a hexadecimal character to an integer.
+ *
+ * @param ch
+ * A character to convert to an integer digit
+ * @param index
+ * The index of the character in the source
+ * @return An integer
+ * @throws DecoderException
+ * Thrown if ch is an illegal hex character
+ */
+ protected static int toDigit(char ch)
+ {
+ int digit = Character.digit(ch, 16);
+ if (digit == -1)
+ {
+ throw new FacesException("Illegal hexadecimal charcter ");
+ }
+ return digit;
+ }
+}
Modified: myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datalist/AbstractHtmlDataList.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datalist/AbstractHtmlDataList.java?rev=1082312&r1=1082311&r2=1082312&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datalist/AbstractHtmlDataList.java (original)
+++ myfaces/tomahawk/trunk/core20/src/main/java/org/apache/myfaces/custom/datalist/AbstractHtmlDataList.java Wed Mar 16 21:39:29 2011
@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.component.ContextCallback;
import javax.faces.component.UIComponent;
@@ -268,12 +269,57 @@ public abstract class AbstractHtmlDataLi
// Check if the clientId for the component, which we
// are looking for, has a rowIndex attached
char separator = UINamingContainer.getSeparatorChar(context);
- String subId = clientId.substring(baseClientId.length() + 1);
+ ValueExpression rowKeyVE = getValueExpression("rowKey");
+ boolean rowKeyFound = false;
+
+ if (rowKeyVE != null)
+ {
+ int oldRow = this.getRowIndex();
+ try
+ {
+ // iterate over the rows
+ int rowsToProcess = getRows();
+ // if getRows() returns 0, all rows have to be processed
+ if (rowsToProcess == 0)
+ {
+ rowsToProcess = getRowCount();
+ }
+ int rowIndex = getFirst();
+ for (int rowsProcessed = 0; rowsProcessed < rowsToProcess; rowsProcessed++, rowIndex++)
+ {
+ setRowIndex(rowIndex);
+ if (!isRowAvailable())
+ {
+ break;
+ }
+
+ if (clientId.startsWith(getContainerClientId(context)))
+ {
+ rowKeyFound = true;
+ break;
+ }
+ }
+
+ if (rowKeyFound)
+ {
+ for (Iterator<UIComponent> it1 = getChildren().iterator();
+ !returnValue && it1.hasNext();)
+ {
+ //recursive call to find the component
+ returnValue = it1.next().invokeOnComponent(context, clientId, callback);
+ }
+ }
+ }
+ finally
+ {
+ this.setRowIndex(oldRow);
+ }
+ }
//If the char next to baseClientId is the separator one and
//the subId matches the regular expression
- if (clientId.charAt(baseClientId.length()) == separator &&
- subId.matches("[0-9]+"+separator+".*"))
+ if (rowKeyVE == null && clientId.matches(baseClientId + separator+"[0-9]+"+separator+".*"))
{
+ String subId = clientId.substring(baseClientId.length() + 1);
String clientRow = subId.substring(0, subId.indexOf(separator));
//Now we save the current position
@@ -434,7 +480,7 @@ public abstract class AbstractHtmlDataLi
{
_facesContext = facesContext;
}
-
+
/**
* A parameter name, under which the rowCount is set in request
* scope similar to the var parameter.
Modified: myfaces/tomahawk/trunk/core20/src/test/java/org/apache/myfaces/component/html/ext/HtmlDataTablePreserveRowComponentStateTest.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core20/src/test/java/org/apache/myfaces/component/html/ext/HtmlDataTablePreserveRowComponentStateTest.java?rev=1082312&r1=1082311&r2=1082312&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core20/src/test/java/org/apache/myfaces/component/html/ext/HtmlDataTablePreserveRowComponentStateTest.java (original)
+++ myfaces/tomahawk/trunk/core20/src/test/java/org/apache/myfaces/component/html/ext/HtmlDataTablePreserveRowComponentStateTest.java Wed Mar 16 21:39:29 2011
@@ -21,10 +21,15 @@ package org.apache.myfaces.component.htm
import java.util.ArrayList;
import java.util.List;
+import javax.faces.component.ContextCallback;
import javax.faces.component.UIColumn;
+import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIPanel;
import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+import junit.framework.Assert;
import org.apache.myfaces.test.base.AbstractJsfTestCase;
import org.apache.myfaces.test.utils.TestUtils;
@@ -374,5 +379,181 @@ public class HtmlDataTablePreserveRowCom
assertEquals(model.get(i).getStyle(), detailStampText.getAttributes().get("style"));
}
+ }
+
+ public void testPreserveRowComponentStateDetailStamp3() throws Exception
+ {
+ List<RowData> model = new ArrayList<RowData>();
+ model.add(new RowData("text1","style1"));
+ model.add(new RowData("text2","style2"));
+ model.add(new RowData("text3","style3"));
+ model.add(new RowData("text4","style4"));
+
+ //Put on request map to be resolved later
+ request.setAttribute("list", model);
+
+ UIViewRoot root = facesContext.getViewRoot();
+ HtmlDataTable table = new HtmlDataTable();
+ UIColumn column = new UIColumn();
+ UIPanel detailStampPanel = new UIPanel();
+ UIOutput text = new UIOutput();
+ UIOutput detailStampText = new UIOutput();
+
+ //This is only required if markInitiaState fix is not used
+ root.setId("root");
+ table.setId("table");
+ detailStampPanel.setId("detailStamp");
+ column.setId("column");
+ text.setId("text");
+ detailStampText.setId("detailStampText");
+
+ table.setVar("row");
+ table.setPreserveRowComponentState(true);
+ table.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{list}",List.class));
+
+ table.setValueExpression("rowKey", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ text.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ detailStampText.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ root.getChildren().add(table);
+ table.getChildren().add(column);
+ table.getFacets().put(AbstractHtmlDataTable.DETAIL_STAMP_FACET_NAME, detailStampPanel);
+ column.getChildren().add(text);
+ detailStampPanel.getChildren().add(detailStampText);
+
+ //Simulate markInitialState call.
+ facesContext.getAttributes().put("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE", Boolean.TRUE);
+ root.markInitialState();
+ table.markInitialState();
+ detailStampPanel.markInitialState();
+ detailStampText.markInitialState();
+ column.markInitialState();
+ text.markInitialState();
+ facesContext.getAttributes().remove("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE");
+
+ //Check the value expressions are working and change the component state
+ for (int i = 0; i < model.size(); i++)
+ {
+ RowData rowData = model.get(i);
+ table.setRowIndex(i);
+ assertEquals(rowData.getText(), text.getValue());
+ assertEquals(rowData.getText(), detailStampText.getValue());
+ text.getAttributes().put("style", rowData.getStyle());
+ detailStampText.getAttributes().put("style", rowData.getStyle());
+ }
+
+ //Reset row index
+ table.setRowIndex(-1);
+
+ //Remove a row
+ table.deleteRowStateForRow(1);
+ model.remove(1);
+
+ //Check the values were not lost
+ for (int i = 0; i < model.size(); i++)
+ {
+ table.setRowIndex(i);
+ assertEquals(model.get(i).getStyle(), text.getAttributes().get("style"));
+ assertEquals(model.get(i).getStyle(), detailStampText.getAttributes().get("style"));
+ }
+
}
+
+ public void testEncodeDecodeSubId() throws Exception
+ {
+ String test = "someKey_+"+((char)6789);
+ String resp = _SubIdConverter.encode(test);
+ String test2 = _SubIdConverter.decode(resp);
+ //System.out.println(resp+" "+Integer.toHexString(6789));
+ assertEquals(test, test2);
+ }
+
+ public void testPreserveRowComponentStateDetailStamp4() throws Exception
+ {
+ List<RowData> model = new ArrayList<RowData>();
+ model.add(new RowData("text1","style1"));
+ model.add(new RowData("text2","style2"));
+ model.add(new RowData("text3","style3"));
+ model.add(new RowData("text4","style4"));
+
+ //Put on request map to be resolved later
+ request.setAttribute("list", model);
+
+ UIViewRoot root = facesContext.getViewRoot();
+ HtmlDataTable table = new HtmlDataTable();
+ UIColumn column = new UIColumn();
+ UIPanel detailStampPanel = new UIPanel();
+ UIOutput text = new UIOutput();
+ UIOutput detailStampText = new UIOutput();
+
+ //This is only required if markInitiaState fix is not used
+ root.setId("root");
+ table.setId("table");
+ detailStampPanel.setId("detailStamp");
+ column.setId("column");
+ text.setId("text");
+ detailStampText.setId("detailStampText");
+
+ table.setVar("row");
+ table.setPreserveRowComponentState(true);
+ table.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{list}",List.class));
+
+ table.setValueExpression("rowKey", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ text.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ detailStampText.setValueExpression("value", application.
+ getExpressionFactory().createValueExpression(
+ facesContext.getELContext(),"#{row.text}",String.class));
+
+ root.getChildren().add(table);
+ table.getChildren().add(column);
+ table.getFacets().put(AbstractHtmlDataTable.DETAIL_STAMP_FACET_NAME, detailStampPanel);
+ column.getChildren().add(text);
+ detailStampPanel.getChildren().add(detailStampText);
+
+ //Simulate markInitialState call.
+ facesContext.getAttributes().put("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE", Boolean.TRUE);
+ root.markInitialState();
+ table.markInitialState();
+ detailStampPanel.markInitialState();
+ detailStampText.markInitialState();
+ column.markInitialState();
+ text.markInitialState();
+ facesContext.getAttributes().remove("javax.faces.view.ViewDeclarationLanguage.IS_BUILDING_INITIAL_STATE");
+
+ root.invokeOnComponent(facesContext, "table:r_id_text2:text", new ContextCallback()
+ {
+ public void invokeContextCallback(FacesContext context, UIComponent target)
+ {
+ Assert.assertEquals("text2", target.getAttributes().get("value"));
+ }
+ });
+
+ root.invokeOnComponent(facesContext, "table:r_id_text3:detailStampText", new ContextCallback()
+ {
+ public void invokeContextCallback(FacesContext context, UIComponent target)
+ {
+ Assert.assertEquals("text3", target.getAttributes().get("value"));
+ }
+ });
+
+ }
+
}