Wednesday, August 08, 2007

Data Converters for JSF Components

It is inevitable that when users fill forms in their web browsers, data arrives at the server as a bunch of strings. Then it's up to the server to convert the string to appropriate objects. To facilitate this conversion in JSF, you can write your own data converters.

Supposing we have an input field in which a user is expected to enter time, such as "08:45 am", and we need to convert this data to a Date object. Begin by defining the converter class:

public class TimeConverter implements Converter{
public TimeConverter() {}
public Object getAsObject(FacesContext context, UIComponent uiComp, String timeStr) {
try {
String displayFormat = "hh:mm a";
return new SimpleDateFormat(displayFormat).parse(timeStr);
} catch (ParseException ex) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "ParsingError", "");
throw new ConverterException(message);
}
}
public String getAsString(FacesContext context, UIComponent uiComp, Object date) {
String displayForma = "hh:mm a";
return new SimpleDateFormat(displayFormat).format((Date)date);
}
}

The class must implement the javax.faces.convert.Converter interface, and its getAsObject() method must throw a javax.faces.convert.ConverterException. getAsObject() returns a Date, while getAsString() returns a string. Then you must register the converter with JSF:
<converter>
<converter-id>timeConverter</converter-id>
<converter-class>com.strive.research.timecard.converters.TimeConverter</converter-class>
</converter>

The JSP/JSF page should be able to utilize the converter when you attach an instance to any component, in this case, an input field:
<h:inputText styleClass="timeentrytext" value="#{timecardbean.tue_lunchout}">
<f:converter converterId="timeConverter"/>
</h:inputText>

The component above uses values from a managed bean. In that bean, the getter and setter look like this:
public Date getTue_lunchout() {
// return a date object;
}
public void setTue_lunchout(Date tue_lunchout) {
// some code
}
As shown, the task of conversion is no longer the managed bean's reponsibity. Earlier we had a utility class who's job was to convert dates; we don't really need (most of) it in this scenario.

Potential issues you might run into:
java.lang.ArrayStoreException: java.lang.Float = just means wherever you are storing objects must be generic enough to take casts of what you are storing. I got a similar exception when my double-subscripted Object array is instantialized as a String[][]. Dumb - I just forgot that's how I was doing it.
java.lang.IllegalArgumentException: Illegal pattern character 'o' = the pattern you are sending SimpleDateFormat is not correct. My patterns are generated based on browser language, and I had a bug in the algorithm. Simply fixed.