Questo problema non è specificamente correlato alle enumerazioni. Si avrebbe lo stesso problema con altri tipi di List
per i quali JSF ha convertitori incorporati, ad es. List<Integer>
, List<Double>
, ecc.
Il problema è che EL gestisce il runtime e che le informazioni di tipo generico vengono perse durante il runtime. Quindi, in sostanza, JSF/EL non sa nulla del tipo parametrizzato di List
e di default su String
se non diversamente specificato da un esplicito Converter
. In teoria, sarebbe stato possibile utilizzare gli hacker di riflessione con l'aiuto di ParameterizedType#getActualTypeArguments()
, ma gli sviluppatori JSF/EL potrebbero avere le loro ragioni per non farlo.
È davvero necessario definire un convertitore per questo. Dal momento che JSF già fornito con un builtin EnumConverter
(che non è standalone utilizzabile in questo caso particolare perché si deve specificare il tipo enum durante il runtime), si può solo estendere come segue:
package com.example;
import javax.faces.convert.EnumConverter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="securityRoleConverter")
public class SecurityRoleConverter extends EnumConverter {
public SecurityRoleConverter() {
super(SecurityRole.class);
}
}
e usarlo come segue:
<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
o
<h:selectManyCheckbox value="#{userController.roles}">
<f:converter converterId="securityRoleConverter" />
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
Un po 'più generico (e hacky) soluzione sarebbe quella di memorizzare il tipo enum come attributo componente.
package com.example;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="genericEnumConverter")
public class GenericEnumConverter implements Converter {
private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType";
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value instanceof Enum) {
component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass());
return ((Enum<?>) value).name();
} else {
throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass()));
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE);
try {
return Enum.valueOf(enumType, value);
} catch (IllegalArgumentException e) {
throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType));
}
}
}
E 'utilizzabile su tutti i tipi di List<Enum>
utilizzando convertitore ID genericEnumConverter
. Per List<Double>
, List<Integer>
, ecc. Si sarebbero utilizzati i convertitori incorporati javax.faces.Double
, javax.faces.Integer
e così via. Il convertitore Enum incorporato non è adatto a causa dell'incapacità di specificare il tipo di enumerazione target (a Class<Enum>
) dal lato della vista. La libreria di utilità JSF OmniFaces offre esattamente questo convertitore out the box.
Si noti che per una normale proprietà Enum
, il EnumConverter
integrato è già sufficiente. JSF lo istanzia automaticamente con il giusto tipo di enumerazione target.
Non direi che è un brutto riflesso. le strutture lo fanno, più informazioni sul tipo, più felicità. I ragazzi della JSF sono probabilmente sopraffatti dalla complessità della loro creazione, non hanno tempo per questo. – irreputable
+1 per la nuova parola che ho imparato: automagicamente :) – lamostreta
@BalusC, grazie per l'ennesima risposta informativa e utile. Se posso essere così audace, posso chiederti perché hai scelto di estendere 'EnumConverter' invece di delegare a un'istanza di esso? – Nick