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
.
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.