0
votes

I'm struggling to find why this don't works.

I have a selectManyCheckbox

<h:form>

<p:selectManyCheckbox id="listCars" value="#{controller.listSelectedCarTypes}" converter="genericEnumConverter">
    <f:selectItems value="#{controller.listCarTypeValues}" />
</p:selectManyCheckbox>

<p:commandButton value="Pesquisar" action="#{controller.filter}" process="@this, @form" update="panelResults" icon="ui-icon-search"/>

</h:form>

The controller has getters/setters and the Lists are declared and initialized as follows:

private List<CarType> listSelectedCarTypes = null;
private List<CarType> listCarTypeValues = null;

@PostConstruct
public void init(){
    listSelectedCarTypes = new ArrayList<CarType>();
    listCarTypeValues = new ArrayList<CarType>();
    listCarTypeValues.add(CarType.OFF_ROAD);
    listCarTypeValues.add(CarType.CONVERSIBLE);
    listCarTypeValues.add(CarType.TRUCK);
}

The Enum is basic stuff:

public enum CarType  {
    OFF_ROAD {
        @Override
        public String getDescription() {
            return "Cool but Hot Off the Road"
        }
    },
    TRUCK {
        @Override
        public String getDescription() {
            return "Holy moly a Monster Truck"
        }
    }

    // lots of enums...
    ;

    public abstract String getDescription();    
    @Override
    public String toString() {
        return getDescription();
    }
}

And finally, here is the converter I got from another stackoverflow reference...

@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) {
    System.out.println("value: "+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 {
        System.out.println("enumType: "+enumType+ " value: "+value);
        System.out.println("CarType.valueOf(value): "+CarType.valueOf(value));
        System.out.println("Enum.valueOf(enumType, value): "+Enum.valueOf(enumType, value));
        return Enum.valueOf(enumType, value);
    } catch (IllegalArgumentException e) {
        throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType));
    }
}

}

So as the view and the controller inits, it do as expected and getAsString is called for each value of the component, prints normally. Output:

value: Cool but Hot Off the Road
value: Holy moly a Monster Truck
...

But, when I submit the form, and getAsObject is called, it throws IllegalArgumentException on Enum.valueOf(enumType, value).

enumType: class my.project.enums.CarType$120 value: TRUCK
CarType.valueOf(value): Holy moly a Monster Truck
Value is not an enum of type: class my.project.enums.CarType$120

The weird thing is... this: CarType.valueOf(value) Works...

But this: Enum.valueOf(enumType, value) Don't.

I had to make an explicit converter for my enum CarType instead of the Generic one in order for the selectManyCheckbox to works.

Can anyone explain that to me? Thanks in advance.

1
Does it work a plain jsf component instead of a primefaces one? What if you isolate this code and just use it in a class with a 'main'? Same errors then? - Kukeltje
Off-Topic tip: Start using OmniFaces:showcase.omnifaces.org/converters/GenericEnumConverter (and lots of other useful tools) - Kukeltje
with jsf-html h:selectManyCheckbox, same error... same behavior... I'm running a web application with tomcat, hibernate, jsf and primefaces. - Bruno Tacca
Sot it is not PrimeFaces related then but most likely plain jsf or other related (using spring?). Normally it is common practice then to make a plain jsf xhtml and remove the PrimeFaces tags (since tags are for where the problem is, not what is used). Off Topic: in process="@this, @form" , @this is superfluous - Kukeltje
true dat, I forgot to remove that since I copied from another button that had a f:setPropertyActionListener... thanks for that note... I'm not using spring, gonna try an isolated jsf experiment... thanks. - Bruno Tacca

1 Answers

1
votes

This is core Java problem. Enum class CarType has abstract method and each enum value is instance of CarType subclass. See: Java Enum getDeclaringClass vs getClass

Solution is to set ((Enum)value).getDeclaringClass() instead of value.getClass() as attribute ATTRIBUTE_ENUM_TYPE in getAsString(...) method.

Tip: I assume that is problem with class immediately because I notice a number in class name my.project.enums.CarType$120.