JSF feeds a Set<String> into a Set<Foo>
Hello,
I am currently learning JSF2.0, and have just encountered a behavior I
do not understand. Basically, I have a test case (see below) where I
have a typed set (Set<Foo>) whose content, automatically populated by
JSF, is made of strings (Set<String>). This should not even compile, yet
this runs. What am I missing?
To provide you with a bit of context, my business scenario is as
follows. I have a web form which will be used to create/update users in
my application, and to assign the user with relevant roles. All roles
will be displayed as checkboxes, the checked checkboxes will represent
the roles that the user possesses. The form will be linked to a backing
bean, whose properties will be automatically populated, using EL
expressions. The bean itself will extend a JPA entity (User), which will
then be copied into a regular User object, then sent to some EJB3.1 for
processing (controls and persistence). Having the backing bean extend
the JPA entity is a simple way, for me, to automatically provide
getters/setters to it.
I could probably achieve my goal using the ui-repeat tag, but it seems
that the selectManyCheckBox tag does precisely that, hence my attempt.
While setting up the scenario though, I encountered a problem with the
way JSF would feed my backing bean - putting String objects where I
expect Role objects. I have trimmed down the problem to the following
test case.
The test case is made of three files:
- an XHTML file, with h:selectManyCheckbox, f:selectItems and
h:commandButton,
- a backing bean whose methods are called by the JSF tags, using EL
expressions. In the application, the bean extends the User class, but
not in the test case,
- a Foo class, with a name, and overriden Object methods (toString,
equals and hashCode). In the application, the Foo class is a Role.
I use JBoss Application Server 6.0.0, and my java -version yields
1.6.0_24-b07.
Since I'm learning JSF, I am trying various approaches while exploring
the technology. After all, the more issues I am faced with, the faster I
progress. The test case could therefore look a bit off to experts, and I
apologize in advance if this is the case. I welcome any comment. Also,
if I am in the wrong group, could you redirect me to the appropriate
group? Thank you.
Kind regards,
Elegie.
===
XHTML file
===
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Test</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
</h:head>
<h:body>
<div>
<h:form>
<h:selectManyCheckbox
value="#{testCase.foos}"
layout="pageDirection">
<f:selectItems
value="#{testCase.allFoos}"
var="f"
itemLabel="#{f.name}" />
</h:selectManyCheckbox>
<h:commandButton value="Test" action="#{testCase.doTest}" />
</h:form>
</div>
</h:body>
</html>
===
Backing bean - TestCase.java
===
package test ;
import java.io.Serializable;
import java.util.ArrayList ;
import java.util.LinkedHashSet ;
import java.util.List ;
import java.util.Set ;
import javax.faces.bean.ManagedBean ;
import javax.faces.bean.RequestScoped ;
@ManagedBean
@RequestScoped
public class TestCase implements Serializable {
private static final long serialVersionUID = 1L ;
private Set<Foo> myFoos ;
public Set<Foo> getFoos() {
this.myFoos = new LinkedHashSet<Foo>() ;
this.myFoos.add(new Foo("Foo1")) ;
this.myFoos.add(new Foo("Foo3")) ;
this.myFoos.add(new Foo("Foo5")) ;
return this.myFoos ;
}
public void setFoos(Set<Foo> foos) {
this.myFoos = foos ;
}
public List<Foo> getAllFoos() {
ArrayList<Foo> myFoos = new ArrayList<Foo>() ;
myFoos.add(new Foo("Foo1")) ;
myFoos.add(new Foo("Foo2")) ;
myFoos.add(new Foo("Foo3")) ;
myFoos.add(new Foo("Foo4")) ;
myFoos.add(new Foo("Foo5")) ;
myFoos.add(new Foo("Foo6")) ;
return myFoos ;
}
public Object doTest() {
System.out.println("myFoos contains " + this.myFoos) ;
if (!this.myFoos.isEmpty()) {
Object[] content = this.myFoos.toArray() ;
Object candidate = content[0] ;
// Candidate is not a Foo, but a String!
System.out.println("Nice to meet you. I am " + candidate) ;
System.out.println("Am I a foo? " + (candidate instanceof Foo)) ;
System.out.println("What am I? " + (candidate.getClass())) ;
// A real Foo should behave like a Foo!
Foo test = new Foo("fooTest") ;
Object fooTest = (Object) test ;
System.out.println("Nice to meet you. I am " + fooTest) ;
System.out.println("Am I a foo? " + (fooTest instanceof Foo)) ;
System.out.println("What am I? " + (fooTest.getClass())) ;
}
return new String("") ;
}
}
===
POJO - Foo.java
===
package test ;
public class Foo {
private String name ;
public String getName() {
return this.name ;
}
public void setName(String name) {
this.name = name ;
}
public Foo(String name) {
this.name = name ;
}
@Override
public boolean equals(Object o) {
if (o==this) return true ;
if (!(o instanceof Foo)) return false ;
Foo f = (Foo) o ;
return f.name == this.name ;
}
@Override
public int hashCode() {
return this.name.hashCode() ;
}
@Override
public String toString() {
return "I'm a Foo, and my name is " + this.name ;
}
}