As per Mark's request, I did a quick and dirty analysis of the essential code delta between Mojarra and MyFaces - particularly in how they implement getClientId(), getContainerClientId() in UIComponent / UIComponentBase / UIData. In short, in UIData and UIRepeat the implementations diverge on overriding and using getClientId (Mojarra) and getContainerClientId (MyFaces) to hold the logic for determining row indexes and such.
---------------------------------------------------------------------------------------------------------------------------------------------------
UIComponent.getClientId and getContainerClientId
Mojarra/MyFaces do the same basic things with these methods:
public String getClientId()
{
return getClientId(getFacesContext());
}
public abstract String getClientId(FacesContext context);
public String getContainerClientId(FacesContext context) {
if (context == null)
{
throw new NullPointerException();
}
return this.getClientId(context);
}
UIComponentBase.getClientId
Mojarra/MyFaces basic client id determination work is done:
- return client id if already known, otherwise calculate it
- they both rely on finding UniqueIdVendor and the parent NamingContainer to get this done but the way they go about it is different
- it's not obvious to me that the differences would cause the problem we are seeing
UIComponentBase.getContainerClientId
Mojarra/MyFaces defer to UIComponent implementation.
UIData.getClientId
Mojarra: overrides UIComponentBase
This is a lengthy method which builds up the client id by taking into account the row index and whether or not the component is nested withing another UIData component.
MyFaces: defers to UIComponentBase however it used to override it:
/*
@Override
public String getClientId(FacesContext context)
{
String clientId = super.getClientId(context);
int rowIndex = getRowIndex();
if (rowIndex == -1)
{
return clientId;
}
StringBuilder bld = __getSharedStringBuilder();
return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();
}*/
but has now been commented out - the responsibility is now with the getContainerClientId method (see next section)
UIData.getContainerClientId
Mojarra: defers to UIComponent
MyFaces: overrides UIComponent
The method is short and has a potentially relevant comment:
@Override
public String getContainerClientId(FacesContext context)
{
//MYFACES-2744 UIData.getClientId() should not append rowIndex, instead use UIData.getContainerClientId()
String clientId = super.getContainerClientId(context);
int rowIndex = getRowIndex();
if (rowIndex == -1)
{
return clientId;
}
StringBuilder bld = __getSharedStringBuilder(context);
return bld.append(clientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString();
}
UIRepeat.getClientId
Mojarra: overrides UIComponentBase
public String getClientId(FacesContext faces) {
String id = super.getClientId(faces);
if (this.index >= 0)
{
id = this.getBuffer().append(id).append(
getSeparatorChar(faces)).append(this.index)
.toString();
}
return id;
}
MyFaces: defers to UIComponentBase
UIRepeat.getContainerClientId
Mojarra: defers to UIComponent
MyFaces: overrides UIComponent
@Override
public String getContainerClientId(FacesContext faces)
{
String id = super.getContainerClientId(faces);
if (_index >= 0)
{
id = _getBuffer().append(id).append(UINamingContainer.getSeparatorChar(faces)).append(_index).toString();
}
return id;
}
Added some logging to the OutputTextRender for a small table. It clearly shows that the call to getClientId() from the component is returning the row index twice:
MyFaces logging:
OutputTextRenderer.encodeBegin: iceform:datTbl:0:0:number from com.icesoft.faces.component.ext.HtmlOutputText@f57e027
OutputTextRenderer.encodeBegin: iceform:datTbl:1:1:number from com.icesoft.faces.component.ext.HtmlOutputText@f57e027
OutputTextRenderer.encodeBegin: iceform:datTbl:2:2:number from com.icesoft.faces.component.ext.HtmlOutputText@f57e027
Mojarra logging:
OutputTextRenderer.encodeBegin: iceform:datTbl:0:number from com.icesoft.faces.component.ext.HtmlOutputText@3154bfa8
OutputTextRenderer.encodeBegin: iceform:datTbl:1:number from com.icesoft.faces.component.ext.HtmlOutputText@3154bfa8
OutputTextRenderer.encodeBegin: iceform:datTbl:2:number from com.icesoft.faces.component.ext.HtmlOutputText@3154bfa8