We have numerical values in our database, representing a two-value-state. Of course this would perfectly match a boolean, but oracle has no such datatype. The NUMBER(1,0) type from the database is matched to a java.lang.Short type in Java (sometimes they used a NUMBER(*,0) to represent booleans, which are matched to java.math.BigDecimal).
Since it is somehow obvious, I want to offer ice:selectBooleanCheckbox in the view as a value representation and UIComponent to the user. (I use IceFaces as JSF implementation)
Since some people who specified JSF think it is obvious to always match the value of a ice:selectBooleanCheckbox or in JSF h:selectBooleanCheckbox to a boolean in the model, so the renderer of the component never calls any converter, even if you specify one: Issue disscused at java.net
Therefore I tried the following:
I created a converter to specify it in the UIComponent:
public class BooleanBigDecimalConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String str) {
if (StringUtils.isEmptyString(str)) {
return new BigDecimal(0);
}
if (str.equals("true")) {
return new BigDecimal(1);
} else {
return new BigDecimal(0);
}
}
public String getAsString(FacesContext context, UIComponent component, Object obj) {
if (obj != null) {
String str = obj.toString();
if (str.equalsIgnoreCase("1")
|| str.equalsIgnoreCase("yes")
|| str.equalsIgnoreCase("true")
|| str.equalsIgnoreCase("on")) {
return "true";
} else {
return "false";
}
}
return "false";
}
}
The converter works fine for the render phase (the getAsString-method is called correctly), but the getAsObject-method (Ignore that it's not correct at the moment, because it's not called anyway, so it will be fixed if it's called!) is never called, because in the renderer of the UIComponent a converter is not foreseen, like you can see here (snip from com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer):
public Object getConvertedValue(FacesContext facesContext, UIComponent uiComponent, Object submittedValue) throws ConverterException
{
if(!(submittedValue instanceof String))
throw new ConverterException("Expecting submittedValue to be String");
else
return Boolean.valueOf((String)submittedValue);
}
So this results in an IllegalArgumentException, since in the UpdateModelValues phase it is tried to apply a Boolean to a numerical value (please ignore the BigDecimal/Short confusion... it is just a numerical type in any case!).
So I tried to overwrite the renderer with a new one like this:
import com.icesoft.faces.component.ext.renderkit.CheckboxRenderer;
public class CustomHtmlSelectBooleanCheckbox extends CheckboxRenderer {
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
Converter converter = ((ValueHolder) component).getConverter();
return converter.getAsObject(context, component, (String) submittedValue);
}
}
and registered it like this in faces-config.xml:
<render-kit>
<renderer>
<component-family>com.icesoft.faces.HtmlSelectBooleanCheckbox</component-family>
<renderer-type>com.icesoft.faces.Checkbox</renderer-type>
<renderer-class>com.myapp.web.util.CustomHtmlSelectBooleanCheckbox</renderer-class>
</renderer>
</render-kit>
I guess this should be correct, but the overridden method "getConvertedValue" is never called, nor is the getAsObject()-method, so I guess I made a mistake in registering the custom renderer, but I can't find any more documentation or hints how to do this properly and especially how to find the correct component-family (I looked up the one I use in icefaces.taglib.xml) and the correct renderer-type.
I don't want to edit the complete model because of this. Any hints, how this can be resolved?