Pergunta

Eu quero usar os valores enum em um <h:selectManyCheckbox>.As caixas de verificação preenchida corretamente, no entanto, ao selecionar alguns valores e submetê-los, o seu tipo de tempo de execução é String, e não enum.O meu código:

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

UserController classe (SecurityRole é um tipo de enumeração):

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

Quando JSF chama a setRoles método, ele contém uma lista de tipo de Seqüência de caracteres, e não o tipo enum.Qualquer idéias?Obrigado!

Foi útil?

Solução

Este problema não está relacionado especificamente para enums.Você teria o mesmo problema com outros List tipos para os quais JSF tem incorporado a conversores, e.g. List<Integer>, List<Double>, etc.

O problema é que o opera tempo de execução e que tipo genérico de informação é perdida durante o tempo de execução.Então, em essência, JSF/EL não sabe nada sobre o parametrizada tipo de List e padrões para String a menos que especificado de outra forma por uma explícita Converter.Em teoria, teria sido possível utilizar desagradável reflexão hacks com a ajuda de ParameterizedType#getActualTypeArguments(), mas o JSF/EL desenvolvedores podem ter suas razões para não fazer isso.

Você realmente precisa definir explicitamente um conversor para isso.Desde o JSF já vem com um builtin EnumConverter (o que não é utilizável autônomo, neste caso particular, porque você tem que especificar o tipo enum durante o tempo de execução), você pode apenas estender-lo da seguinte forma:

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 usa-o como segue:

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

ou

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

Um pouco mais genérico (e hacky) solução seria armazenar o 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));
        }
    }

}

É usável em todos os tipos de List<Enum> usando o conversor de IDENTIFICAÇÃO genericEnumConverter.Para List<Double>, List<Integer>, etc teria usado o builtin conversores javax.faces.Double, javax.faces.Integer e assim por diante.O grupo builtin Enum converter é a forma inadequada devido à incapacidade para especificar o alvo do tipo enum (um Class<Enum>) a partir da vista de lado.O JSF biblioteca de utilitários OmniFaces oferece exatamente este conversor fora da caixa.

Observe que, para um normal Enum propriedade, o grupo builtin EnumConverter já basta.JSF irá instanciar ele automagicamente com o alvo certo tipo enum.

Outras dicas

Em alguns casos, o Lista também poderia ser uma matriz Algum dia [, e, neste caso, nenhum conversor explícito é necessário.

O apagamento genérico era uma maneira inteligente de colocar genéricos no idioma sem quebrar as coisas antigas, mas agora vivemos para sempre com as consequências dessa decisão ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top