<p:selectManyMenu id="colourList"
var="color"
value="#{testBean.selectedColours}"
converter="#{colourConverter}"
showCheckbox="true"
required="true"
label="Colour"
style="overflow: auto; width: 317px; background-color: white; max-height: 200px;">
<f:selectItems var="colour"
value="#{testBean.colours}"
itemLabel="#{colour.colourHex}"
itemValue="#{colour}"/>
<p:column>
<span style="display: inline-block; width: 275px; height: 20px; background-color:\##{color.colourHex}; border: 1px solid black;"
title="Name: #{color.colourName} | Hex: #{color.colourHex}" />
</p:column>
</p:selectManyMenu>
<p:commandButton value="Submit" actionListener="#{testBean.action}"/>
CSS is left intact, if someone may want to put the example into practice. It will display three basic colours (RGB) with check boxes in front of them as follows.
The managed bean :
@Named
@ViewScoped
public class TestBean implements Serializable {
@Inject
private DataStore dataStore;
private List<Colour> colours; //Getter & setter.
private List<Colour> selectedColours; //Getter & setter.
private static final long serialVersionUID = 1L;
public TestBean() {}
@PostConstruct
private void init() {
colours = dataStore.getColours();
}
public void action() {
for (Colour colour : selectedColours) {
System.out.println("colourName : "
+ colour.getColourName()
+ " : colourHex : "
+ colour.getColourHex());
}
}
}
If the converter="#{colourConverter}"
attribute is removed from <p:selectManyMenu>
, then it causes a java.lang.ClassCastException
to be thrown in the action()
method even though the converter is decorated with @FacesConverter(forClass = Colour.class)
.
java.lang.ClassCastException: java.lang.String cannot be cast to com.example.Colour
It appears that it is the generic type eraser problem (the generic type parameter of List<Colour>
is removed at run time so that it turns into an untyped List
).
Colour[]
should then work but the action()
method itself was not invoked, when attempted.
What is the exact reason why it requires an explicit mention of a converter?
Additional :
The converter :
@Named
@ApplicationScoped
@FacesConverter(forClass = Colour.class)
public class ColourConverter implements Converter {
@Inject
private DataStore dataStore;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
long parsedValue = Long.parseLong(value);
if (parsedValue <= 0) {
throw new ConverterException("FacesMessage");
}
Colour entity = dataStore.findColourById(parsedValue);
if (entity == null) {
throw new ConverterException("FacesMessage");
}
return entity;
} catch (NumberFormatException e) {
throw new ConverterException("FacesMessage", e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (!(value instanceof Colour)) {
throw new ConverterException("Message");
}
Long id = ((Colour) value).getColourId();
return id != null ? id.toString() : "";
}
}
The application scoped bean where the List<Colour>
is maintained.
@Named
@ApplicationScoped
public class DataStore {
private List<Colour> colours;
public DataStore() {}
@PostConstruct
private void init() {
colours = new ArrayList<>();
Colour colour = new Colour();
colour.setColourId(1L);
colour.setColourName("Red");
colour.setColourHex("FF0000");
colours.add(colour);
colour = new Colour();
colour.setColourId(3L);
colour.setColourName("Green");
colour.setColourHex("008000");
colours.add(colour);
colour = new Colour();
colour.setColourId(2L);
colour.setColourName("Blue");
colour.setColourHex("0000FF");
colours.add(colour);
}
public Colour findColourById(Long id) {
for (Colour colour : colours) {
if (colour.getColourId().equals(id)) {
return colour;
}
}
return null;
}
public List<Colour> getColours() {
return colours;
}
}
The domain model class :
public class Colour implements Serializable {
private Long colourId;
private String colourName;
private String colourHex;
private static final long serialVersionUID = 1L;
public Colour() {}
public Long getColourId() {
return colourId;
}
public void setColourId(Long colourId) {
this.colourId = colourId;
}
public String getColourName() {
return colourName;
}
public void setColourName(String colourName) {
this.colourName = colourName;
}
public String getColourHex() {
return colourHex;
}
public void setColourHex(String colourHex) {
this.colourHex = colourHex;
}
@Override
public int hashCode() {
int hash = 7;
hash = 47 * hash + Objects.hashCode(getColourId());
return hash;
}
@Override
public boolean equals(Object that) {
if (!(that instanceof Colour)) {
return false;
}
return this == that || Objects.equals(getColourId(), ((Colour) that).getColourId());
}
@Override
public String toString() {
return String.format("%s[colourId=%d]", getClass().getCanonicalName(), getColourId());
}
}