[JAVASERVERFACES_SPEC_PUBLIC-1226] Inconsistent handling of null/undeclared attributes, especially with nested components Created: 07/Oct/13  Updated: 13/Aug/14

Status: Open
Project: javaserverfaces-spec-public
Component/s: Components/Renderers, Facelets/VDL
Affects Version/s: 2.1, 2.2
Fix Version/s: None

Type: Improvement Priority: Minor
Reporter: fabmars Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

GF3 + JSF 2.1.26 or GF4 with JSF 2.2.3 with JDK7.0_45


Tags: composite, composite-component, null

 Description   

The origin is here: https://java.net/jira/browse/JAVASERVERFACES-2614, but it's mainly worth reading the summary further down here.

The reproducer is on git@github.com:fabmars/JSF2614.git .
We have an outer component:

test1.xhtml
<composite:interface>
<composite:attribute name="val1" type="java.lang.String"/>
<composite:attribute name="val2" type="java.lang.String"/>
<composite:attribute name="val3" type="java.lang.String" default="#{null}"/>
<composite:attribute name="val4" type="java.lang.String" default="#{null}"/>
</composite:interface>

<composite:implementation>
<h:panelGrid>
<h:outputText value="Outer1: #{cc.attrs.val1 == null ? 'null' : (empty cc.attrs.val1 ? 'empty' : cc.attrs.val1)}" />
<h:outputText value="Outer2: #{cc.attrs.val2 == null ? 'null' : (empty cc.attrs.val2 ? 'empty' : cc.attrs.val2)}" />
<h:outputText value="Outer3: #{cc.attrs.val3 == null ? 'null' : (empty cc.attrs.val3 ? 'empty' : cc.attrs.val3)}" />
<h:outputText value="Outer4: #{cc.attrs.val4 == null ? 'null' : (empty cc.attrs.val4 ? 'empty' : cc.attrs.val4)}" />
</h:panelGrid>
<sr:test2 val1="#{cc.attrs.val1}" val2="#{cc.attrs.val2}" val3="#{cc.attrs.val3}" val4="#{cc.attrs.val4}" /> 
</composite:implementation>

And an inner component:

test2.xhtml
<composite:interface>
	<composite:attribute name="val1" type="java.lang.String"/>
	<composite:attribute name="val2" type="java.lang.String" default="#{null}"/>
	<composite:attribute name="val3" type="java.lang.String"/>
	<composite:attribute name="val4" type="java.lang.String" default="#{null}"/>
</composite:interface>

<composite:implementation>
	<h:panelGrid>
		<h:outputText value="Inner1: #{cc.attrs.val1 == null ? 'null' : (empty cc.attrs.val1 ? 'empty' : cc.attrs.val1)}" />
		<h:outputText value="Inner2: #{cc.attrs.val2 == null ? 'null' : (empty cc.attrs.val2 ? 'empty' : cc.attrs.val2)}" />
		<h:outputText value="Inner3: #{cc.attrs.val3 == null ? 'null' : (empty cc.attrs.val3 ? 'empty' : cc.attrs.val3)}" />
		<h:outputText value="Inner4: #{cc.attrs.val4 == null ? 'null' : (empty cc.attrs.val4 ? 'empty' : cc.attrs.val4)}" />
	</h:panelGrid>
</composite:implementation>

And the actual page:

jsf2614.xhtml
<sr:test1 val1="AAA" val2="BBB" val3="CCC" val4="DDD"/>
<sr:test1/>
<sr:test1 val1="#{null}" val2="#{null}" val3="#{null}" val4="#{null}"/>
<sr:test2  val1="DDD" val2="EEE" val3="FFF" val4="GGG"/>
<sr:test2/>
<sr:test2 val1="#{null}" val2="#{null}" val3="#{null}" val4="#{null}"/>

When running one can notice a couple of odd things:

1st case:
On <sr:test1 .../>

  • feed <composite:attribute name="val1" type="java.lang.String"/> with null. Result: null
  • feed <composite:attribute name="val3" type="java.lang.String" default="# {null}"/> with null. Result: ""
    The developer, in the second line expects to get String var3 = null in the end. Instead he gets a real value.
    OK this is due to the mechanics of coertion but this is not consistent and imo should be fixed.


    2nd case:
    - Call the OUTER component dry <sr:test1/>. var1 isn't declared, so will be assumed null. In sr:test1 implementation is a call to <sr:test2 val1="#{cc.attrs.val1}"... The INNER(sr:test2) component is awaiting a String as val1 (<composite:attribute name="val1" type="java.lang.String"/>) and our non-declared attribute becomes "" (again) in the end! (see that lines 9 vs 13 of the output). In other words: feed a null to <composite:attribute name="val1" type="java.lang.String"/> directly, it stays null, feed it from a surrounding component, it becomes ""!


    3rd case:
    Calling directly the inner component <sr:test2/> and <sr:test2 val1="#{null}

    " val2="#

    {null}" val3="#{null}

    " val4="#

    {null}

    "/> produce totally different results (see lines 29-32 vs 33-36 of the output). This is confusing for the developer and I'm not even sure this is a "feature".



 Comments   
Comment by Ed Burns [ 01/Aug/14 ]

Set priority to baseline ahead of JSF 2.3 triage. Priorities will be assigned accurately after this exercise.

Generated at Mon Apr 27 14:03:29 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.