At first I got some ClassCastExceptions. When I went to the source, I found out that the values in my Map<Integer,Integer>
were in fact Strings.
I did the following experiment to check if the use of PrimeFaces was my problem:
<h:form>
<p:spinner value="#{testBean.integer}" />
<h:inputText value="#{testBean.integer}" />
<p:spinner value="#{testBean.mapInt[0]}" />
<h:inputText pt:type="number" value="#{testBean.mapInt[1]}" />
<p:commandButton value="Read Map Values" action="#{testBean.checkTypes}" update="@form" />
<p:messages />
</h:form>
My TestBean:
@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
private HashMap<Integer, Integer> map;
private Integer integer;
@PostConstruct
public void init() {
map = new HashMap<>();
}
public void checkTypes() {
addMsg(null, "integer - Class: " + integer.getClass().getSimpleName());
for (Object key : map.keySet()) {
Object o = map.get(key);
addMsg(null, "map[" + key.toString() + "] - Class: " + o.getClass().getSimpleName());
}
}
private static void addMsg(String client, String msg) {
FacesContext.getCurrentInstance().addMessage(client, new FacesMessage(msg));
System.out.println("msg [" + client + "]: " + msg);
}
//... getters/setters ...
}
And the messages show:
integer - Class: Integer
map[0] - Class: String
map[1] - Class: String
The first <h:inputText>
didn't even need a passthrough to enforce a number input.
I guess JSF uses reflection internally to convert the input string of the field to the right type. If so, then maybe type erasure of generics allows it to put a String
where an Integer
should be. This is probably why the problem does not happen with integer
, which is of type Integer
, not a generic type.
Am I right on this?
My question is then: how can I work around it easily?
I'm pretty new to JSF, I heard about converters while looking for solutions. Do I have to create a custom converter to enforce a call to Integer.valueOf(String)
on the input field? Where can I find how to do that? Is there a simpler solution?