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:38:07 UTC
svn commit: r1082309 - in
/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext:
AbstractHtmlDataTable.java HtmlDataTableHack.java _SubIdConverter.java
Author: lu4242
Date: Wed Mar 16 21:38:06 2011
New Revision: 1082309
URL: http://svn.apache.org/viewvc?rev=1082309&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/core/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java
Modified:
myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java
myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
Modified: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java?rev=1082309&r1=1082308&r2=1082309&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/AbstractHtmlDataTable.java Wed Mar 16 21:38:06 2011
@@ -33,7 +33,6 @@ import javax.faces.component.EditableVal
import javax.faces.component.NamingContainer;
import javax.faces.component.UIColumn;
import javax.faces.component.UIComponent;
-import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.model.DataModel;
@@ -1485,7 +1484,7 @@ public abstract class AbstractHtmlDataTa
public boolean isCurrentDetailExpanded()
{
- Boolean expanded = (Boolean) _expandedNodes.get(new Integer(getRowIndex()));
+ Boolean expanded = (Boolean) _expandedNodes.get(getClientId(getFacesContext()));
if (expanded != null)
{
return expanded.booleanValue();
@@ -1554,7 +1553,7 @@ public abstract class AbstractHtmlDataTa
*/
public void toggleDetail()
{
- Integer rowIndex = new Integer(getRowIndex());
+ String derivedRowKey = getClientId(getFacesContext());
// get the current expanded state of the row
boolean expanded = isDetailExpanded();
@@ -1565,12 +1564,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
@@ -1580,12 +1579,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);
}
}
}
@@ -1597,9 +1596,7 @@ public abstract class AbstractHtmlDataTa
*/
public boolean isDetailExpanded()
{
- Integer rowIndex = new Integer(getRowIndex());
-
- Boolean expanded = (Boolean) _expandedNodes.get(rowIndex);
+ Boolean expanded = (Boolean) _expandedNodes.get(getClientId(getFacesContext()));
if (expanded == null)
{
return isDetailStampExpandedDefault();
@@ -1664,9 +1661,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(getClientId(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/core/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java?rev=1082309&r1=1082308&r2=1082309&view=diff
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java (original)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/HtmlDataTableHack.java Wed Mar 16 21:38:06 2011
@@ -77,6 +77,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;
@@ -117,6 +119,7 @@ public abstract class HtmlDataTableHack
{
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)
@@ -126,15 +129,15 @@ public abstract class HtmlDataTableHack
{
if(Integer.parseInt(rowIndexString) == rowIndex)
{
- return clientId;
+ return clientId.substring(0, index+1) + getDerivedSubClientId();
}
}
catch(NumberFormatException e)
{
- return clientId + NamingContainer.SEPARATOR_CHAR + rowIndex;
+ return clientId + NamingContainer.SEPARATOR_CHAR + getDerivedSubClientId();
}
}
- return clientId + NamingContainer.SEPARATOR_CHAR + rowIndex;
+ return clientId + NamingContainer.SEPARATOR_CHAR + getDerivedSubClientId();
}
/**
@@ -521,11 +524,12 @@ public abstract class HtmlDataTableHack
public Object saveState(FacesContext context)
{
- Object[] values = new Object[4];
+ Object[] values = new Object[5];
values[0] = super.saveState(context);
values[1] = _preserveRowStates;
values[2] = _forceId;
values[3] = _forceIdIndex;
+ values[4] = _derivedRowKeyPrefix;
return values;
}
@@ -536,6 +540,7 @@ public abstract class HtmlDataTableHack
_preserveRowStates = (Boolean) values[1];
_forceId = (Boolean) values[2];
_forceIdIndex = (Boolean) values[3];
+ _derivedRowKeyPrefix = (String) values[4];
}
private static final DataModel EMPTY_DATA_MODEL = new _SerializableDataModel()
@@ -682,34 +687,148 @@ public abstract class HtmlDataTableHack
// save row index
int savedRowIndex = getRowIndex();
- FacesContext facesContext = FacesContext.getCurrentInstance();
+ FacesContext facesContext = getFacesContext();
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)
+ Object rowKey = getRowKey();
+ if (rowKey != null)
+ {
+ setRowIndex(deletedIndex);
+ _rowStates.remove(currentRowStateKey);
+ setRowIndex(savedRowIndex);
+ }
+ else
{
- setRowIndex(index);
- String nextRowStateKey = 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;
+ }
- Object nextRowState = _rowStates.get(nextRowStateKey);
- if (nextRowState == null)
+ // Remove last row
+ _rowStates.remove(currentRowStateKey);
+
+ // restore saved row index
+ setRowIndex(savedRowIndex);
+ }
+ }
+
+ //Since it should be unique, no need to store it as a local var
+ //private Object _rowKey;
+
+ /**
+ * 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.
+ *
+ * @JSFProperty
+ * @return
+ */
+ public Object getRowKey()
+ {
+ //if (_rowKey != null)
+ //{
+ // return _rowKey;
+ //}
+ ValueBinding vb = getValueBinding("rowKey");
+ if (vb != null)
+ {
+ Object value = vb.getValue(getFacesContext());
+ if (value == null)
{
- _rowStates.remove(currentRowStateKey);
+ return null;
}
else
{
- _rowStates.put(currentRowStateKey, nextRowState);
+ return (Object) value;
}
- currentRowStateKey = nextRowStateKey;
}
+ return null;
+ }
+
+ public void setRowKey(Object rowKey)
+ {
+ //_rowKey = rowKey;
+ }
+
+ private String _derivedRowKeyPrefix;
+
+ /**
+ * 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).
+ *
+ * @JSFProperty defaultValue="r_id_"
+ * @return
+ */
+ public String getDerivedRowKeyPrefix()
+ {
+ if (_derivedRowKeyPrefix != null)
+ {
+ return _derivedRowKeyPrefix;
+ }
+ ValueBinding vb = getValueBinding("derivedRowKeyPrefix");
+ if (vb != null)
+ {
+ Object value = vb.getValue(getFacesContext());
+ if (value == null)
+ {
+ return UNIQUE_ROW_ID_PREFIX;
+ }
+ else
+ {
+ return (String) value.toString();
+ }
+ }
+ return UNIQUE_ROW_ID_PREFIX;
+ }
- // Remove last row
- _rowStates.remove(currentRowStateKey);
+ public void setDerivedRowKeyPrefix(String derivedRowKeyPrefix)
+ {
+ this._derivedRowKeyPrefix = derivedRowKeyPrefix;
+ }
- // restore saved row index
- setRowIndex(savedRowIndex);
+ /**
+ * 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());
+ }
}
-
+
}
Added: myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java?rev=1082309&view=auto
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java (added)
+++ myfaces/tomahawk/trunk/core/src/main/java/org/apache/myfaces/component/html/ext/_SubIdConverter.java Wed Mar 16 21:38:06 2011
@@ -0,0 +1,170 @@
+/*
+ * 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;
+import java.lang.StringBuffer;
+
+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)
+ {
+ StringBuffer 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 StringBuffer(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)
+ {
+ StringBuffer 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 StringBuffer(string.substring(0, i));
+ }
+ i += 4;
+ app = ""+((char)value);
+ }
+ else
+ {
+ //No decoding
+ }
+
+ if (app != null)
+ {
+ if (sb == null)
+ {
+ sb = new StringBuffer(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;
+ }
+}