JSF converter with null value

2019-07-11 07:14发布

I need to convert null property values of type java.math.BigDecimal to 0.00 everywhere with a specified number of decimal places (only for displaying (<h:outputText>) thus, not for input components).

The basic converter expected to do this job is given below (currency, percentage, locale etc have been excluded completely for brevity).

@FacesConverter("bigDecimalConverter")
public final class BigDecimalConverter implements Converter {

    private static final int SCALE = 2;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {

        if (submittedValue == null || submittedValue.isEmpty()) {
            return null;
        }

        try {
            return new BigDecimal(submittedValue).setScale(SCALE, RoundingMode.HALF_UP).stripTrailingZeros();
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object modelValue) {

        BigDecimal value;

        if (modelValue == null) { // This is expected to replace null with 0.
            value = BigDecimal.ZERO;
        } else if (modelValue instanceof Long) {
            value = BigDecimal.valueOf((Long) modelValue);
        } else if (modelValue instanceof Double) {
            value = BigDecimal.valueOf((Double) modelValue);
        } else if (!(modelValue instanceof BigDecimal)) {
            throw new ConverterException("Message");
        } else {
            value = (BigDecimal) modelValue;
        }

        NumberFormat numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setGroupingUsed(true);
        numberFormat.setMinimumFractionDigits(SCALE);
        numberFormat.setMaximumFractionDigits(SCALE);
        return numberFormat.format(value);
    }
}

Referring to a property of the associated model or bean of type java.math.BigDecimal using output components like the following.

<h:outputText value="#{bean.value}">
    <f:converter converterId="bigDecimalConverter"/>
</h:outputText>

bean.value is a type of java.math.BigDecimal in a managed bean. If bean.value is null, then the getAsString() method is not invoked. Hence, an empty output is rendered where a value of zero is expected.

This value="#{bean.value}" needs to be changed to value="#{empty bean.value ? 0 : bean.value}" for the converter to perform its coherent task.

Putting this conditional test in EL everywhere is however, quite maintenance-unfriendly.

Is there a way to invoke the getAsString() method, when a target property is null which is conditionally attempted to be set to 0 in that method (BigDecimal.ZERO)?


Update :

The getAsString() method is invoked with <o:param> (OmniFaces).

<h:outputFormat value="Discount ({0}%)">
    <o:param value="#{bean.value}">
        <f:converter converterId="bigDecimalConverter"/>
    </o:param>
</h:outputFormat>

This displays Discount (0.00%) after has been converted by the converter, when #{bean.value} returns null.

1条回答
Luminary・发光体
2楼-- · 2019-07-11 07:42

This is specific to the Mojarra. The problem occurred because the converter is not called when the value is null (see source here). I tested the same code in the MyFaces implementation and the result is that the convert is always called, no matter if the value is null (as can be seen here, line 378).

So if change JSF implementation is not an option, I think the solution through OmniFaces is good enough. At worst, maintain a function that makes the conversion like #{convert(bean.value)} may leave cleaner your view than an inline comparison.

查看更多
登录 后发表回答