Hi<br><br>Some days ago, it was mentioned on myfaces dev list that "targets"<br>attribute does not seem to work as expected. After review the available<br>documentation and doing some test, my conclusion was the spec is good,<br>
but this part needs a little bit more documentation (and fix some <br>bugs), because there are use cases that are causing problems to users.<br>Some of these problems has been already mentioned (spec issue 755) but<br>my intention here is do a full and detailed analysis. <br>
<br>The property "targets" is used for these tags:<br><br>composite:actionSource<br>composite:editableValueHolder<br>composite:valueHolder<br>composite:clientBehavior<br>composite:attribute<br><br>For the first four cases, this property is used by :<br>
<br>ViewDeclarationLanguage.retargetAttachedObjects(FacesContext context,<br> UIComponent topLevelComponent,<br> List<AttachedObjectHandler> handlers)<br>
<br>In JSF 2.0 rev A it was made explicit the need to handle nested<br>composite component with this lines:<br><br>"... The implementation must support retargeting attached objects from <br>the top level compsite component to targets that are composite and <br>
non-composite components ...."<br><br>This is ok ;-).<br><br>The problem is the description about how composite:attribute "targets" <br>property works does not match with the algorithm for:<br><br>ViewDeclarationLanguage.retargetMethodExpressions(FacesContext context, <br>
UIComponent topLevelComponent)<br><br>Here is the documentation about composite:attribute "targets":<br><br>"... If this element has a method-signature attribute, <br><br>
the value of the targets attribute must be interpreted as a space <br>(not tab) separated list of client ids (relative to the top level <br>component) of components within the <composite:implementation> section. <br>
<br>Space is used as the delimiter for compatibility with the IDREFS and <br>NMTOKENS data types from the XML Schema. <br><br>Each entry in the list must be interpreted as the id of an inner <br>component to which the MethodExpression from the composite component <br>
tag in the using page must be applied. <br><br>If this element has a method-signature attribute, but no targets <br>attribute, the value of the name attribute is used as the single entry <br>in the list. <br><br>If the value of the name attribute is not one of the special values <br>
listed in the description of the name attribute, targets (or its <br>derived value) need not correspond to the id of an inner component ...".<br><br>At this point everything seems to be ok. But look this documentation<br>
(I'll put the important points):<br><br>".....<br> # Get the value of the targets attribute. If the value is a ValueExpression <br> evaluate it. If there is no targets attribute, let the name of the <br> metadata element be the evaluated value of the targets attribute.<br>
# Interpret targets as a space (not tab) separated list of ids. For each entry <br> in the list:<br> ......<br> # If name is equal to the string "action" without the quotes, call <br> ActionSource2.setActionExpression(javax.el.MethodExpression) on target, <br>
passing attributeMethodExpression.<br> # If name is equal to the string "actionListener" without the quotes, call <br> ActionSource.addActionListener(javax.faces.event.ActionListener) on target, <br>
passing attributeMethodExpression wrapped in a MethodExpressionActionListener.<br> # If name is equal to the string "validator" without the quotes, call <br> EditableValueHolder.addValidator(javax.faces.validator.Validator) on target, <br>
passing attributeMethodExpression wrapped in a MethodExpressionValidator.<br> # If name is equal to the string "valueChangeListener" without the quotes, call <br> EditableValueHolder.addValueChangeListener(javax.faces.event.ValueChangeListener) <br>
on target, passing attributeMethodExpression wrapped in a <br> MethodExpressionValueChangeListener.<br> # Otherwise, assume that the MethodExpression should be placed in the <br> components attribute set. The runtme must create the MethodExpression <br>
instance based on the value of the "method-signature" attribute.<br>....."<br><br>My first interpretation of this javadoc was that "targets" only has sense for "action",<br>"actionListener", "validator" and "valueChangeListener". But note if that's true,<br>
someone could expect something like the description on retargetAttachedObjects, right?<br><br>The current behavior (Mojarra 2.0.3 and Myfaces 2.0.2) is the same, if we have two<br>nested composite components, that will throw a ClassCastException. There is a issue already<br>
open for this one:<br><br><a href="https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=755">https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=755</a><br><br>There are two possible alternatives here:<br>
<br> 1. Implement the algorithm proposed for retargetMethodExpressions and ignore <br> composite:attribute "targets" property says. <br> 2. Implement the expected behavior of composite:attribute "targets" property and make<br>
some changes to retargetMethodExpressions algorithm.<br><br>The intention is take option 2 and include it for JSF 2.0, because in theory should be <br>handled as an implementation detail of the algorithm (anyway it could be good to update <br>
the documentation with the same advice used for retargetAttachedObjects).<br><br>For "action", "actionListener", "validator" and "valueChangeListener" it is clear that<br>the action to take is something like this: <br>
<br> - If target is a composite component and that one "retarget" to other components and <br> additionally does not implements [ActionSource2/EditableValueHolder], call:<br> <br> targetComponent.getAttributes().put(attributeName, attributeNameValueExpression);<br>
<br> and call retargetMethodExpressions recursively. Take care of apply the method twice<br> and if the property pointed by "attributeName" was already set, revert its effects. <br> <br>The tricky part is how to handle generic method expression properties. The javadoc says:<br>
<br>"....<br> # Otherwise, assume that the MethodExpression should be placed in the <br> components attribute set. The runtme must create the MethodExpression <br> instance based on the value of the "method-signature" attribute.<br>
....."<br><br>But I have identified three valid cases:<br><br> 1. testSimpleAttributeMethodExpressionEmpty.xhtml (Using an EL #{cc} reference)<br><br> <testComposite:simpleAttributeMethodExpressionEmpty id="cc1" <br>
customMethod="#{methodExpressionBean.doSomethingFunny}"><br> </testComposite:simpleAttributeMethodExpressionEmpty><br><br> simpleAttributeMethodExpressionEmpty.xhtml<br><br> <composite:interface><br>
<composite:attribute <br> name="customMethod" <br> method-signature="String doSomething(String)"/><br> </composite:interface><br> <composite:implementation><br>
<tc:testComponent id="testComponent" customMethod="#{cc.attrs.customMethod}"/><br> </composite:implementation><br><br> 2. testSimpleAttributeMethodExpressionTarget.xhtml (Using "targets" to call <br>
a property setter directly)<br><br> <testComposite:simpleAttributeMethodExpressionTarget id="cc1"<br> customMethod="#{methodExpressionBean.doSomethingFunny}"><br> </testComposite:simpleAttributeMethodExpressionTarget><br>
<br> simpleAttributeMethodExpressionTarget.xhtml<br> <br> <composite:interface><br> <composite:attribute <br> name="customMethod" <br> method-signature="String doSomething(String)" <br>
targets="testComponent"/><br> </composite:interface><br> <composite:implementation><br> <tc:testComponent id="testComponent"/><br> </composite:implementation><br>
<br> 3. testCompositeAttributeMethodExpressionTarget.xhtml (Using "targets", <br> but the target component is a composite one)<br><br> <testComposite:compositeAttributeMethodExpressionTarget id="cc1" <br>
customMethod="#{methodExpressionBean.doSomethingFunny}"><br> </testComposite:compositeAttributeMethodExpressionTarget><br><br> compositeAttributeMethodExpressionTarget.xhtml<br><br> <composite:interface><br>
<composite:attribute <br> name="customMethod" <br> method-signature="String doSomething(String)" <br> targets="simpleAttributeMethodExpressionTarget"/><br> </composite:interface><br>
<composite:implementation><br> <testComposite:simpleAttributeMethodExpressionTarget <br> id="simpleAttributeMethodExpressionTarget"/><br> </composite:implementation><br> <br>
simpleAttributeMethodExpressionTarget.xhtml (could be like in 1 or 2)<br><br>The case (1) actually works but the case (2) and (3) does not work on both <br>MyFaces 2.0.2 and Mojarra 2.0.3 . <br><br>I have created an issue on MyFaces here:<br>
<br><a href="https://issues.apache.org/jira/browse/MYFACES-2946">https://issues.apache.org/jira/browse/MYFACES-2946</a><br><br>a attached a working patch too if you want to take a look at it:<br><br><a href="https://issues.apache.org/jira/secure/attachment/12457512/MYFACES-2946-1.patch">https://issues.apache.org/jira/secure/attachment/12457512/MYFACES-2946-1.patch</a><br>
<br>Suggestions are welcome<br><br>regards,<br><br>Leonardo Uribe<br>