Dieses Problem bezieht sich nicht speziell auf Enums. Sie würden das gleiche Problem mit anderen List
Typen haben, für die JSF eingebaute Konverter, z.B. List<Integer>
, List<Double>
, und so weiter.
Das Problem ist, dass EL Runtime betreibt und dass generische Informationen während der Laufzeit verloren gehen. Im Wesentlichen weiß JSF/EL also nichts über den parametrisierten Typ der List
und die Standardwerte String
, sofern nicht explizit durch Converter
anders angegeben. Theoretisch wäre es mit Hilfe von ParameterizedType#getActualTypeArguments()
möglich gewesen, widerliche Reflection-Hacks zu verwenden, aber die JSF/EL-Entwickler könnten ihre Gründe dafür haben, dies nicht zu tun.
Sie müssen wirklich explizit einen Konverter dafür definieren. Da JSF bereits Schiffe mit einer eingebauten EnumConverter
(die nicht nutzbare Standalone in diesem besonderen Fall ist, weil Sie die Aufzählungstyp während der Laufzeit angeben müssen), könnten Sie erweitern es wie folgt:
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);
}
}
Und verwenden Sie es als folgt:
<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
oder
<h:selectManyCheckbox value="#{userController.roles}">
<f:converter converterId="securityRoleConverter" />
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
Ein bisschen mehr Generika (und Hacky) Lösung wäre, den Enum-Typ als Komponentenattribut zu speichern.
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 ist einsetzbar auf allen Arten von List<Enum>
Konverter ID mit genericEnumConverter
. Für List<Double>
, List<Integer>
usw. hätte man die eingebauten Konverter javax.faces.Double
, javax.faces.Integer
und so weiter benutzt. Der eingebaute Enum-Konverter ist übrigens ungeeignet, da der Ziel-Enum-Typ (a Class<Enum>
) von der Viewseite aus nicht spezifiziert werden kann. Die JSF-Dienstprogrammbibliothek OmniFaces bietet genau diesen Konverter out the box.
Beachten Sie, dass für eine normale Enum
Eigenschaft der eingebaute EnumConverter
bereits ausreicht. JSF wird es automatisch mit dem richtigen Zielaufzählungstyp instanziieren.
Ich würde nicht sagen, das ist eine böse Reflexion hack. Frameworks tun das, mehr Typ Info, mehr Glück. JSF Jungs sind wahrscheinlich überwältigt von der Komplexität ihrer Kreation, sie haben keine Zeit dafür. – irreputable
+1 für das neue Wort, das ich gelernt habe: automatisch :) – lamostreta
@BalusC, danke für eine weitere informative und hilfreiche Antwort. Wenn ich so fett bin, darf ich fragen, warum Sie sich entschieden haben, 'EnumConverter' zu erweitern, anstatt an eine Instanz davon zu delegieren? – Nick