Este problema no está específicamente relacionado con enumeraciones. Tendría el mismo problema con otros tipos de List
para los cuales JSF tiene convertidores integrados, p. List<Integer>
, List<Double>
, etcétera.
El problema es que EL opera el tiempo de ejecución y que la información de tipo genérico se pierde durante el tiempo de ejecución. Así que, en esencia, JSF/EL no sabe nada sobre el tipo parametrizado de List
y el valor predeterminado es String
a menos que se especifique lo contrario en un Converter
explícito. En teoría, habría sido posible usar hacks de reflexión desagradables con la ayuda de ParameterizedType#getActualTypeArguments()
, pero los desarrolladores de JSF/EL pueden tener sus razones para no hacerlo.
Realmente necesita definir explícitamente un convertidor para esto. Desde JSF ya envía con una orden interna del EnumConverter
(que no es independiente utilizable en este caso particular, ya que tiene que especificar el tipo de enumeración en tiempo de ejecución), sólo podría extenderse de la siguiente manera:
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);
}
}
y utilizarlo como siguiente:
<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 poco más genérico (y hacky) solución sería almacenar el tipo enum como atributo de 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));
}
}
}
Es utilizable en todo tipo de List<Enum>
utilizando convertidor de Identificación genericEnumConverter
. Para List<Double>
, List<Integer>
, etc. uno habría usado los convertidores integrados javax.faces.Double
, javax.faces.Integer
y así sucesivamente. El convertidor Enum integrado no es adecuado por la incapacidad de especificar el tipo de enumeración objetivo (a Class<Enum>
) desde el lado de la vista. La biblioteca de utilidades JSF OmniFaces ofrece exactamente este convertidor out the box.
Tenga en cuenta que para una propiedad Enum
normal, el EnumConverter
incorporado ya es suficiente. JSF lo instanciará automágicamente con el tipo de enum objetivo correcto.
No diría que es un hack de reflexión desagradable. los marcos hacen eso, más tipo de información, más felicidad. Los chicos de JSF probablemente se sienten abrumados por la complejidad de su creación, no tienen tiempo para esto. – irreputable
+1 por la nueva palabra que aprendí: automágicamente :) – lamostreta
@BalusC, gracias por otra respuesta más informativa y útil. Si puedo ser tan audaz, ¿puedo preguntar por qué eligió extender 'EnumConverter' en lugar de delegar en una instancia de la misma? – Nick