简体   繁体   English

在h:selectManyCheckbox中使用enum

[英]Use enum in h:selectManyCheckbox

I want to use enum values in a <h:selectManyCheckbox> . 我想在<h:selectManyCheckbox>使用枚举值。 The checkboxes get populated correctly, however, when selecting some values and submitting them, their runtime type is String , and not enum. 正确填充复选框,但是,在选择某些值并提交它们时,它们的运行时类型是String ,而不是枚举。 My code: 我的代码:

<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection">
     <f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>

UserController class (SecurityRole is an enum type): UserController类(SecurityRole是枚举类型):

public SelectItem[] getRolesSelectMany() {
    SelectItem[] items = new SelectItem[SecurityRole.values().length];

    int i = 0;
    for (SecurityRole role : SecurityRole.values()) {
        items[i++] = new SelectItem(role, role.toString());
    }
    return items;
}     

public List<SecurityRole> getRoles() {
     getCurrent().getRoles();
}

public void setRoles(List<SecurityRole> roles) {
     getCurrent().setRoles(roles);
}

When JSF calls the setRoles method, it contains a list of type String, and not the enum type. 当JSF调用setRoles方法时,它包含String类型的列表,而不是枚举类型。 Any ideas? 有任何想法吗? Thanks! 谢谢!

This problem is not specifically related to enums. 此问题与枚举无关。 You would have the same problem with other List types for which JSF has builtin converters, eg List<Integer> , List<Double> , etcetera. 对于JSF具有内置转换器的其他List类型,您会遇到同样的问题,例如List<Integer>List<Double>等。

The problem is that EL operates runtime and that generic type information is lost during runtime. 问题是EL运行运行时,并且在运行时期间丢失泛型类型信息。 So in essence, JSF/EL doesn't know anything about the parameterized type of the List and defaults to String unless otherwise specified by an explicit Converter . 所以本质上,JSF / EL对List的参数化类型一无所知,默认为String除非显式Converter另有指定。 In theory, it would have been possible using nasty reflection hacks with help of ParameterizedType#getActualTypeArguments() , but the JSF/EL developers may have their reasons for not doing this. 理论上,在ParameterizedType#getActualTypeArguments()帮助下使用讨厌的反射黑客是可能的,但是JSF / EL开发人员可能有他们不这样做的理由。

You really need to explicitly define a converter for this. 您确实需要为此明确定义转换器。 Since JSF already ships with a builtin EnumConverter (which isn't useable standalone in this particular case because you have to specify the enum type during runtime), you could just extend it as follows: 由于JSF已经附带了内置的EnumConverter (在这种特殊情况下不能单独使用,因为你必须在运行时指定枚举类型),你可以按如下方式扩展它:

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);
    }

}

And use it as follows: 并使用如下:

<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
    <f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>

or 要么

<h:selectManyCheckbox value="#{userController.roles}">
    <f:converter converterId="securityRoleConverter" />
    <f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>

A bit more generic (and hacky) solution would be to storing the enum type as component attribute. 更通用(和hacky)的解决方案是将枚举类型存储为组件属性。

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));
        }
    }

}

It's useable on all kinds of List<Enum> using converter ID genericEnumConverter . 它可以在各种List<Enum>使用转换器ID genericEnumConverter For List<Double> , List<Integer> , etc one would have used the builtin converters javax.faces.Double , javax.faces.Integer and so on. 对于List<Double>List<Integer>等,可以使用内置转换器javax.faces.Doublejavax.faces.Integer等。 The builtin Enum converter is by the way unsuitable due to the inability to specify the target enum type (a Class<Enum> ) from the view side on. 由于无法从视图侧指定目标枚举类型( Class<Enum> ),内置的Enum转换器不合适。 The JSF utility library OmniFaces offers exactly this converter out the box . JSF的工具库OmniFaces提供的正是这种转换器开箱

Note that for a normal Enum property, the builtin EnumConverter already suffices. 请注意,对于普通的Enum属性,内置的EnumConverter已经足够了。 JSF will instantiate it automagically with the right target enum type. JSF将使用正确的目标枚举类型自动实例化它。

In some cases the List could just as well be an array SomeType[] , and in this case no explicit converter is needed. 在某些情况下, List也可以是一个数组SomeType [] ,在这种情况下,不需要显式转换器。

Generic erasure was a clever way of putting generics into the language without breaking the old stuff, but now we live forever with the consequences of that decision... 通用擦除是一种巧妙的方法,可以将泛型放入语言而不会破坏旧的东西,但现在我们永远生活在这个决定的后果之下......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM