2

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?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Joffrey
  • 32,348
  • 6
  • 68
  • 100

1 Answers1

5

Your concrete problem is caused by the nature of Java generic type information being present during compiletime only and thus completely absent during runtime, and EL expressions being evaluated during runtime only and thus not during compiletime. In effects, EL doesn't see any generic type information.

All EL sees during runtime is basically a Map, not a Map<Integer, Integer>. So, unless you explicitly specify a Converter, JSF/EL will assume it to be the same standard type as the submitted value which is extracted as HTTP request parameter: a String.

The solution is relatively simple: explicitly specify a converter. For the Integer class, you can use JSF builtin IntegerConverter which has the converter ID javax.faces.Integer.

<p:spinner value="#{testBean.mapInt[0]}" converter="javax.faces.Integer" />
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555