users@javaserverfaces-spec-public.java.net

[jsr344-experts mirror] Re: HTML5 friendly markup (fix inconsistencies and documentation review)

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Thu, 27 Mar 2014 15:55:02 +0100

Hi

I have confirmed it is possible to implement a solution that comply with the
ideal behavior, avoids the duplicated attributes and in that way minimize the
changes in the spec. The idea is detect the presence of pt:elementName
attribute to enable a new algorithm in MetaRulesetImpl that check when
the attribute has been declared in the underlying component class or not,
and in that way avoid changes in the old algorithm.

The changes requires in the spec are:

- After this text:

"... First, create a new instance of TagAttribute with the following
characteristics: location: from the argument tag's location, namespace:
http://xmlns.jcp.org/jsf/passthrough, local name: value of
Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY, qualified name: same as local name
with the "p:" prefix, value: from the argument tag's local name. Let this
TagAttribute be elementNameTagAttribute. ..."

 add this legend:

"If the tag namespace is http://xmlns.jcp.org/jsf and the tag's local name
is 'element', the created instance for elementNameTagAttribute should
have value of the existing elementName attribute declaration"

That means if the tag to be processed was declared manually as jsf:element,
since elementName is a required attribute, we just make sure to add it
correctly. In practice it will be declared twice, but it doesn't matter because
most of the time the user will never instantiate it in that way. This change
can be omitted, but I would like to let it for completeness of jsf:element
tag, because it was declared in the facelets taglibdoc.

- In this text:

"... If the current attribute's namespace is http://xmlns.jcp.org/jsf,
convertedTagAttribute's qualified name must be the current attribute's local
name and convertedTagAttribute's namespace must be the empty string. This will
have the effect of setting the current attribute as a proper property on the
UIComponent instance represented by this markup. ..."

change to this:

"... If the current attribute's namespace is http://xmlns.jcp.org/jsf,
convertedTagAttribute's qualified name must be the current attribute's local
name and convertedTagAttribute's namespace must be the empty string. The
implementation will check later if the attribute has been declared by the
underlying component class and if that so will have the effect of
setting the current attribute as a proper property on the UIComponent
instance represented by this markup. Otherwise, the attribute will be
set on the passthrough attribute map. ..."

- In this text:

"... If the current attribute's namespace is empty or different from the
argument tag's namespace, let the current attribute be convertedTagAttribute.
This will have the effect of setting the current attribute as an attribute
on the attributes map of the UIComponent instance represented by this markup.
..."

change to this

"... If the current attribute's namespace is empty or different from the
argument tag's namespace, let the current attribute be convertedTagAttribute.
The implementation will check later if the attribute has been declared by the
underlying component class and if that so will have the effect of
setting the current attribute as a proper property on the UIComponent
instance represented by this markup. Otherwise, the attribute will be
set on the passthrough attribute map ..."

If you apply these suggested changes we can close this problem. It is up
to you guys to fix the problem in the spec and in Mojarra implementation.
From my side, I'll fix MyFaces implementation as suggested.

regards,

Leonardo

2014-03-27 12:45 GMT+01:00 Leonardo Uribe <lu4242_at_gmail.com>:
> Hi
>
> Just to summarize the conversation with Frank and Ed in Javaland:
>
> 1.The objective of http://xmlns.jcp.org/jsf/passthrough is different
> from http://xmlns.jcp.org/jsf namespace. So the condition that says
> that only tags with attributes using http://xmlns.jcp.org/jsf
> namespace should be processed by TagDecorator still remains.
>
> 2. Use jsf:binding in Mojarra does not work, but it is not in the
> documentation of jsf:element. But the same is true for id attribute and
> these attributes comes from UIComponentBase. This is a bug in Mojarra and
> should be fixed.
>
> 3. I have explained to Frank that in some cases http://xmlns.jcp.org/jsf
> put attributes into normal component attribute map but sometimes it is
> required to use the passthrough attribute map. "id", "binding", "rendered"
> and "transient" should not be put on passthrough attribute map, but other
> attributes should.
>
>
> I have found new elements of judment about the special behavior that has
> attribute with http://xmlns.jcp.org/jsf that suggest we need a more
> elaborated implementation. Take a look at this example:
>
> <div jsf:binding="#{boxBean.box1}">
> <f:ajax event="click" render="renderMe"/>
> Hello World!
> </div>
>
> <h:panelGroup layout="block" id="renderMe">Hello</h:panelGroup>
>
> I tested it with Frank against Mojarra 2.2.6 and it works. The "onclick"
> attribute is rendered as expected. But before I had some problems with it,
> but I couldn't remember what was wrong. Now let's change it a little bit:
>
> <div jsf:binding="#{boxBean.box1}" onclick="alert('hello')">
> <f:ajax event="click" render="renderMe"/>
> Hello World!
> </div>
>
> It doesn't work, because the ajax code is not chained with the code inside
> onclick property. But this alternative works:
>
> <div jsf:binding="#{boxBean.box1}" jsf:onclick="alert('hello')">
> <f:ajax event="click" render="renderMe"/>
> Hello World!
> </div>
>
> There are some lines in JSF 2.2 spec (RenderKit javadoc) that says:
>
> "... If there is a pass through attribute with the same name as a renderer
> specific attribute, the pass through attribute takes precedence. ..."
>
> In the example using just "onclick", it doesn't work because the passthrough
> attribute takes precedence and ignore the code in the renderer. The code
> is executed but when the attribute is being renderer by ResponseWriter, it
> is ignored.
>
> There is another case that is also related. Take a look at this example:
>
> <input jsf:id="input" type="text" value="#{someBean.someValue}"/>
>
> Should this work?. According to the rules of TagDecorator, this is translated
> into a h:inputText component, but the "value" attribute is copied into the
> passthrough attribute map and in the normal attribute map. By the rule
> previously described in the spec, the renderer is effectively bypassed.
> Thanks to Frank, it was clarified the right syntax for it:
>
> <input jsf:id="input" type="text" jsf:value="#{someBean.someValue}"/>
>
> In conclusion we have these 3 cases:
>
> 1. Attributes with jsf namespace that should be put on passthrough
> attribute map.
>
> <div jsf:style="noprint">
> Hello World!
> </div>
>
> 2. Attributes with jsf namespace that should be put on normal attribute map.
>
> <div jsf:binding="#{boxBean.box1}">
> Hello World!
> </div>
>
> or
>
> <input jsf:id="input" type="text" jsf:value="#{someBean.someValue}"/>
>
> 3. Attributes without any namespace that could overlap attributes in the
> normal component map that requires special treatement.
>
> <div jsf:binding="#{boxBean.box1}" onclick="alert('hello')">
> <f:ajax event="click" render="renderMe"/>
> Hello World!
> </div>
>
>
> What should we do? What's the ideal behavior?
>
> - First of all we need to recognize that in this case attributes marked with
> empty namespace and with jsf namespace behave the same. The only special
> property it has jsf namespace is that its presence in an attribute makes the
> tag susceptible to be converted.
>
> - If the attribute is declared by the component class, it should be added to
> the component attribute map.
>
> - If the attribute is declared by the component class, it should NOT be added
> to the passthrough attribute map.
>
> - If the attribute is NOT declared by the component class, it should be put
> on the passthrough attribute map.
>
> How can we detect if a component class has a property defined? there is no
> standard way to do that, but each JSF implementation has the code deep inside
> UIComponentBase implementation, specifically in the normal component
> attribute map. But that suppose a problem, because in the moment the default
> TagDecorator is active, there is not a component class yet. But the trick
> can be done with a new Rule, but it will not be easy.
>
> This strategy has the big advantage that the attributes are not duplicated, so
> the state size is not doubled. The objective is avoid changes in the spec if
> possible, so let's see what we can do.
>
> regards
>
> Leonardo
>
> 2014-03-25 2:12 GMT+01:00 Edward Burns <edward.burns_at_oracle.com>:
>>>>>>> On Mon, 24 Mar 2014 13:39:21 +0100, Leonardo Uribe <lu4242_at_gmail.com> said:
>>
>> LU> But the user has reported another more troublesome combination:
>>
>> LU> <li jsf:class="toclevel-1 tocsection-2"/>
>>
>> LU> According to the spec it should not work. The javadoc of
>> LU> javax.faces.view.facelets.TagDecorator
>> LU> says this (already mentioned before):
>>
>> LU> "... If the current attribute's namespace is http://xmlns.jcp.org/jsf,
>> LU> convertedTagAttribute's qualified name must be the current attribute's local
>> LU> name and convertedTagAttribute's namespace must be the empty string. This
>> LU> will have the effect of setting the current attribute as a proper property
>> LU> on the UIComponent instance represented by this markup. ..."
>>
>> LU> If the intention of jsf namespace is put the attribute into the
>> LU> attribute map, why that syntax should work, unless the attribute has
>> LU> been explicitly declared on jsf:element component.
>>
>> I haven't checked the code, but I expect the reason it works is due to
>> the old attribute/property transparency. First it looks for a
>> "setClass" method on the UIComponent instance. Failing to find it, it
>> calls UIComponent.getAttributes().put("class", "toclevel-1 tocsection-2").
>>
>> LU> I tried also this
>> LU> combination:
>>
>> LU> <div jsf:style="noprint">
>> LU> Hello World!
>> LU> </div>
>>
>> LU> It works again, but it shouldn't.
>>
>> Again, I expect this causes UIComponent.getAttributes().put("style",
>> "noprint").
>>
>> LU> But it is clear why it is an obvious combination. The same javadoc
>> LU> of TagDecorator detects any attribute with the namespace "jsf" and
>> LU> if it is found the tag is binded to a jsf:element component. The
>> LU> related line says this:
>>
>> LU> "... If one or more of the attributes of the tag argument are in the
>> LU> http://xmlns.jcp.org/jsf namespace, obtain a reference to decoratedTag as
>> LU> described in the following steps and iterate through the list of TagDecorator
>> LU> instances as described in the preceding step ..."
>>
>> Yes, that text is the catch-all that allows the whole passthru elements
>> feature to work.
>>
>> LU> In practice, it seems an attribute using "jsf" namespace should be
>> LU> added in both normal attribute map and passthrough attribute map. I
>> LU> can't detect any problem at the moment, because the logic in the
>> LU> default ResponseWriter keeps track of the rendered attributes, and
>> LU> as long as no logical attributes are copied, things will be
>> LU> fine. Here there is an example of the problem:
>>
>> LU> <div jsf:binding="#{boxBean.box1}">
>> LU> Hello World!
>> LU> </div>
>>
>> LU> In Mojarra this attribute is just ignored, so it doesn't work, but
>> LU> in MyFaces it works, but if the attributes are copied in both maps,
>> LU> this one will be a problem. So the algorithm should duplicate the
>> LU> attributes that are not reserved like "id", "binding", "rendered" or
>> LU> "transient".
>>
>> I would have to step through this in a debugger to authoritatively
>> comment on what the spec intent is here, and therefore to clearly define
>> what "in MyFaces it works" means in this case.
>>
>> LU> There are a couple of attributes that are implementation specific,
>> LU> but since they are not declared in the markup, they do not have the
>> LU> chance to be copied or overriden.
>>
>> I'd like to avoid having to do this.
>>
>> LU> It is curious this syntax does not work:
>>
>> LU> <div pt:style="noprint">
>> LU> Hello World!
>> LU> </div>
>>
>> LU> The tag is not converted into a jsf:element, so the compiler sees it as
>> LU> html markup.
>>
>> I would need to see to that namespace the pt: attribute prefix is
>> bound. If it is the xmlns.jcp.org pass through attribute namespace,
>> then it should work and that is a bug.
>>
>> LU> I do not see any other choice than force the copy to the passthrough
>> LU> attribute map of all attributes declared with jsf namespace. But I have
>> LU> to warn about a side effect over the state. If the attributes are copied
>> LU> twice, that means the state of that component is effectively doubled.
>> LU> If PSS is properly implemented, it will not have a significant effect
>> LU> over the state, because most of the time that part is not part of the
>> LU> "delta" state of the component.
>>
>> I must understand why the copy is necessary.
>>
>> Ed
>>
>> --
>> | edward.burns_at_oracle.com | office: +1 407 458 0017
>> | 0 Work Days Til JavaLand 2014
>> | 30 Work Days til JAX 2014