@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
...
}
Java Platform, Enterprise Edition (Java EE) 8 The Java EE Tutorial |
Previous | Next | Contents |
A JavaServer Faces converter class converts strings to objects and objects to strings as required. Several standard converters are provided by JavaServer Faces for this purpose. See Using the Standard Converters for more information on these included converters.
As explained in Conversion Model, if the standard converters included with JavaServer Faces cannot perform the data conversion that you need, you can create a custom converter to perform this specialized conversion. This implementation, at a minimum, must define how to convert data both ways between the two views of the data described in Conversion Model.
All custom converters must implement the javax.faces.convert.Converter
interface. This section explains how to implement this interface to
perform a custom data conversion.
The Duke’s Bookstore case study uses a custom Converter
implementation, located in
tut-install`/examples/case-studies/dukes-bookstore/src/java/dukesbookstore/converters/CreditCardConverter.java`,
to convert the data entered in the Credit Card Number field on the
bookcashier.xhtml
page. It strips blanks and hyphens from the text
string and formats it so that a blank space separates every four
characters.
Another common use case for a custom converter is in a list for a
nonstandard object type. In the Duke’s Tutoring case study, the
Student
and Guardian
entities require a custom converter so that
they can be converted to and from a UISelectItems
input component.
The CreditCardConverter
custom converter class is created as follows:
@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
...
}
The @FacesConverter
annotation registers the custom converter class as
a converter with the name of ccno
with the JavaServer Faces
implementation. Alternatively, you can register the converter with
entries in the application configuration resource file, as shown in
Registering a Custom Converter.
To define how the data is converted from the presentation view to the
model view, the Converter
implementation must implement the
getAsObject(FacesContext, UIComponent, String)
method from the
Converter
interface. Here is the implementation of this method from
CreditCardConverter
:
@Override
public Object getAsObject(FacesContext context,
UIComponent component, String newValue)
throws ConverterException {
if (newValue.isEmpty()) {
return null;
}
// Since this is only a String to String conversion,
// this conversion does not throw ConverterException.
String convertedValue = newValue.trim();
if ( (convertedValue.contains("-")) || (convertedValue.contains(" "))) {
char[] input = convertedValue.toCharArray();
StringBuilder builder = new StringBuilder(input.length);
for (int i = 0; i < input.length; ++i) {
if ((input[i] == '-') || (input[i] == ' ')) {
} else {
builder.append(input[i]);
}
}
convertedValue = builder.toString();
}
return convertedValue;
}
During the Apply Request Values phase, when the components' decode
methods are processed, the JavaServer Faces implementation looks up the
component’s local value in the request and calls the getAsObject
method. When calling this method, the JavaServer Faces implementation
passes in the current FacesContext
instance, the component whose data
needs conversion, and the local value as a String
. The method then
writes the local value to a character array, trims the hyphens and
blanks, adds the rest of the characters to a String
, and returns the
String
.
To define how the data is converted from the model view to the
presentation view, the Converter
implementation must implement the
getAsString(FacesContext, UIComponent, Object)
method from the
Converter
interface. Here is an implementation of this method:
@Override
public String getAsString(FacesContext context,
UIComponent component, Object value)
throws ConverterException {
String inputVal = null;
if ( value == null ) {
return "";
}
// value must be of a type that can be cast to a String.
try {
inputVal = (String)value;
} catch (ClassCastException ce) {
FacesMessage errMsg = new FacesMessage(CONVERSION_ERROR_MESSAGE_ID);
FacesContext.getCurrentInstance().addMessage(null, errMsg);
throw new ConverterException(errMsg.getSummary());
}
// insert spaces after every four characters for better
// readability if they are not already present.
char[] input = inputVal.toCharArray();
StringBuilder builder = new StringBuilder(input.length + 3);
for (int i = 0; i < input.length; ++i) {
if ((i % 4) == 0 && (i != 0)) {
if ({input[i] != ' ') || (input[i] != '-')){
builder.append(" ");
// if there are any "-"'s convert them to blanks.
} else if (input[i] == '-') {
builder.append(" ");
}
}
builder.append(input[i]);
}
String convertedValue = builder.toString();
return convertedValue;
}
During the Render Response phase, in which the components' encode
methods are called, the JavaServer Faces implementation calls the
getAsString
method in order to generate the appropriate output. When
the JavaServer Faces implementation calls this method, it passes in the
current FacesContext
, the UIComponent
whose value needs to be
converted, and the bean value to be converted. Because this converter
does a String
-to-String
conversion, this method can cast the bean
value to a String
.
If the value cannot be converted to a String
, the method throws an
exception, passing an error message from the resource bundle that is
registered with the application.
Registering Application Messages
explains how to register custom error messages with the application.
If the value can be converted to a String
, the method reads the
String
to a character array and loops through the array, adding a
space after every four characters.
You can also create a custom converter with a @FacesConverter
annotation that specifies the forClass
attribute, as shown in the
following example from the Duke’s Tutoring case study:
@FacesConverter(forClass=Guardian.class, value="guardian")
public class GuardianConverter extends EntityConverter implements Converter { ...
The forClass
attribute registers the converter as the default
converter for the Guardian
class. Therefore, whenever that class is
specified by a value
attribute of an input component, the converter is
invoked automatically.
A converter class can be a separate Java POJO class, as in the Duke’s
Bookstore case study. If it needs to access objects defined in a managed
bean class, however, it can be a subclass of a JavaServer Faces managed
bean, as in the address-book
persistence example, in which the
converters use an enterprise bean that is injected into the managed bean
class.
To apply the data conversion performed by a custom converter to a particular component’s value, you must do one of the following.
Reference the converter from the component tag’s converter
attribute.
Nest an f:converter
tag inside the component’s tag and reference the
custom converter from one of the f:converter
tag’s attributes.
If you are using the component tag’s converter
attribute, this
attribute must reference the Converter
implementation’s identifier or
the fully-qualified class name of the converter. Creating
and Using a Custom Converter explains how to implement a custom
converter.
The identifier for the credit card converter class is ccno
, the value
specified in the @FacesConverter
annotation:
@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
...
Therefore, the CreditCardConverter
instance can be registered on the
ccno
component as shown in the following example:
<h:inputText id="ccno"
size="19"
converter="ccno"
value="#{cashierBean.creditCardNumber}"
required="true"
requiredMessage="#{bundle.ReqCreditCard}">
...
</h:inputText>
By setting the converter
attribute of a component’s tag to the
converter’s identifier or its class name, you cause that component’s
local value to be automatically converted according to the rules
specified in the Converter
implementation.
Instead of referencing the converter from the component tag’s
converter
attribute, you can reference the converter from an
f:converter
tag nested inside the component’s tag. To reference the
custom converter using the f:converter
tag, you do one of the
following.
Set the f:converter
tag’s converterId
attribute to the Converter
implementation’s identifier defined in the @FacesConverter
annotation
or in the application configuration resource file. This method is shown
in bookcashier.xhtml
:
<h:inputText id="ccno"
size="19"
value="#{cashierBean.creditCardNumber}"
required="true"
requiredMessage="#{bundle.ReqCreditCard}">
<f:converter converterId="ccno"/>
<f:validateRegex
pattern="\d{16}|\d{4} \d{4} \d{4} \d{4}|\d{4}-\d{4}-\d{4}-\d{4}"/>
</h:inputText>
Bind the Converter
implementation to a managed bean property using
the f:converter
tag’s binding
attribute, as described in
Binding Converters, Listeners, and
Validators to Managed Bean Properties.
The JavaServer Faces implementation calls the converter’s getAsObject
method to strip spaces and hyphens from the input value. The
getAsString
method is called when the bookcashier.xhtml
page is
redisplayed; this happens if the user orders more than $100 worth of
books.
In the Duke’s Tutoring case study, each converter is registered as the
converter for a particular class. The converter is automatically invoked
whenever that class is specified by a value
attribute of an input
component. In the following example, the itemValue
attribute
(highlighted in bold) calls the converter for the Guardian
class:
<h:selectManyListbox id="selectGuardiansMenu"
title="#{bundle['action.add.guardian']}"
value="#{guardianManager.selectedGuardians}"
size="5"
converter="guardian">
<f:selectItems value="#{guardianManager.allGuardians}"
var="selectedGuardian"
itemLabel="#{selectedGuardian.name}"
itemValue="#{selectedGuardian}" />
</h:selectManyListbox>
Previous | Next | Contents |