4.1) Introduction
In most of the situations, the Converter Implementations that are bundled with JSF implementation is often enough. However, for application specific purposes, there may be a need to customize and convert the user entered string into some other data-type. This can be achieved with ease by using JSF Pluggable Converter architecture.
4.2) Phone Number
Let us assume that our Web Application prompts the user to enter phone number which is of the format 'CountryCode-AreaCode-Number'. Note that whatever user enters is just a string and we want this string to be converted into some kind of format so that it can be stored in the model.
PhoneNumber.java
package net.javabeat.articles.jsf.converters;
import java.io.Serializable;
public class PhoneNumber implements Serializable
{
private int countryCode;
private int areaCode;
private long number;
public PhoneNumber()
{
}
public PhoneNumber(int countryCode, int areaCode, long number)
{
this.setCountryCode(countryCode);
this.setAreaCode(areaCode);
this.setNumber(number);
}
public int getCountryCode()
{
return countryCode;
}
public void setCountryCode(int countryCode)
{
this.countryCode = countryCode;
}
public int getAreaCode()
{
return areaCode;
}
public void setAreaCode(int areaCode)
{
this.areaCode = areaCode;
}
public long getNumber()
{
return number;
}
public void setNumber(long number)
{
this.number = number;
}
}
The above model class for Phone Number encapsulates 'countryCode', 'areaCode' and 'number' properties. The rest of the code merely contains setters and getters for setting and getting the appropriate values.
4.3) Phone Number Converter
All the Converter classes in JSF must implement the Converter interface and they should override the getAsObject() and getAsString() methods. The method getAsObject() will be called as soon as the user enters the data and conforms it by clicking the submit button. This triggers the Apply Request Values Phase and this method will be called with the user-entered value as one of its arguments. Here we can parse and format the data entered by the user and store it in some model object and return it so that it will be set to the appropriate UI Component.
The getAsString() method will be called during Render Response Phase. Now, it's time to display the data in the view from the model object. And usually this method will contain logic for extracting the data from the model, format it and return it to the view.
PhoneNumberConverter.java
package net.javabeat.articles.jsf.converters;
import java.util.StringTokenizer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
public class PhoneNumberConverter implements Converter
{
public PhoneNumberConverter()
{
}
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
if (value == null || (value.trim().length() == 0))
{
return value;
}
PhoneNumber phoneNumber = new PhoneNumber();
boolean conversionError = false;
int hyphenCount = 0;
StringTokenizer hyphenTokenizer = new StringTokenizer(value, "-");
while (hyphenTokenizer.hasMoreTokens())
{
String token = hyphenTokenizer.nextToken();
try
{
if (hyphenCount == 0)
{
phoneNumber.setCountryCode(Integer.parseInt(token));
}
if (hyphenCount == 1)
{
phoneNumber.setAreaCode(Integer.parseInt(token));
}
if (hyphenCount == 2)
{
phoneNumber.setNumber(Long.parseLong(token));
}
hyphenCount ++;
}
catch (Exception exception)
{
conversionError = true;
}
}
if (conversionError || (hyphenCount != 3))
{
throw new ConverterException();
}
return phoneNumber;
}
public String getAsString(FacesContext context, UIComponent component, Object value)
{
PhoneNumber phoneNumber = null;
if (value instanceof PhoneNumber)
{
phoneNumber = (PhoneNumber)value;
StringBuilder phoneNumberAsString = new StringBuilder();
phoneNumberAsString.append(phoneNumber.getCountryCode() + "-");
phoneNumberAsString.append(phoneNumber.getAreaCode() + "-");
phoneNumberAsString.append(phoneNumber.getNumber());
return phoneNumberAsString.toString();
}
return "";
}
}
4.4) Registering the Custom Converter
To make the Custom Converter visible to JSF Application, we have to register the Converter class by making an entry in the Faces Configuration file. The following is the xml code snippet for the same,
<converter>
<description>A Converter for phone number</description>
<converter-id>PhoneNumberConverter</converter-id>
<converter-class>
net.javabeat.articles.jsf.converters.PhoneNumberConverter
</converter-class>
</converter>
We have given an identifier 'PhoneNumberConverter' for the PhoneNumberConverter class and this identifier should be referenced elsewhere in the Application.
4.5) Using the Custom Converter
To set this Phone Number Converter to a component, make use of the 'converter' tag as the following listing does,
<h:inputText id="phoneNumberInput"
value="#{phoneNumberBean.phoneNumber}" required="true">
<f:converter converterId="PhoneNumberConverter"></f:converter>
</h:inputText>