How to write a ConfigurableFieldHandler Intended Audience When (not) to use ConfigurableFieldHandler Implement your ConfigurableFieldHandler Define and use the handler
Intended Audience
Anyone who wants to write a configurable field hander.
When (not) to use ConfigurableFieldHandler
If you want to have a custom FieldHandler that works in a fixed manner, you
should consider subclassing
GeneralizedFieldHandler
, or implementing
FieldHandler.
However if you want your FieldHandler to operate more flexibly, based on
configuration, ConfigurableFieldHandler may be what you're looking
for.
Implement your ConfigurableFieldHandler
Basically, two approaches exist for implementing a configurable field handler.
The most straightforward one is simply to implement the ConfigurableFieldHandler
interface. This interface extends the FieldHandler interface.
The other approach is to subclass any of the convenience FieldHandler implementations.
These are AbstractFieldHandler and GeneralizedFieldHandler. Both classes
already implement the ConfigurableFieldHandler interface, so you don't have to
implement it yourself explicitly. However the implementation of the single
ConfigurableFieldHandler method (setConfiguration) is an empty method.
So you'd have to override this method yourself in order to do something useful with the
configuration.
The two approaches differ only marginally. If the convenience classes provide useful
functionality for you, you'd probably be better off using them. Otherwise simply implementing
the ConfigurableFieldHandler interface is a good choice.
For this exercise, we choose to implement the ConfigurableFieldHandler,
and decide to convert dates, using a configurable date pattern:
FieldHandlerImpl.java |
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import org.exolab.castor.mapping.ConfigurableFieldHandler;
import org.exolab.castor.mapping.ValidityException;
public class FieldHandlerImpl implements ConfigurableFieldHandler {
private SimpleDateFormat formatter;
public FieldHandlerImpl() {
//
}
/**
* Sets the configuration for this field handler. The config object is supposed to have one
* property, named "date-format", with a date format pattern compatible with
* java.text.SimpleDateFormat.
*
* @param config the configuration object.
* @throws ValidityException if config doesn't have the required parameter, or if the parameter
* value is not a valid date pattern.
*/
public void setConfiguration(Properties config) throws ValidityException {
String pattern = config.getProperty("date-format");
if (pattern == null) {
throw new ValidityException("Required parameter \"date-format\" is missing for CustomDateFieldHandler.");
}
try {
formatter = new SimpleDateFormat(pattern);
} catch (IllegalArgumentException e) {
throw new ValidityException("Pattern \""+pattern+"\" is not a valid date format.");
}
}
/**
* Returns the value of the field from the object.
*
* @param object The object
* @return The value of the field
* @throws IllegalStateException The Java object has changed and
* is no longer supported by this handler, or the handler is not
* compatible with the Java object
*/
public Object getValue( Object object )
throws IllegalStateException
{
Root root = (Root)object;
Date value = root.getDate();
if (value == null) return null;
return formatter.format(value);
}
/**
* Sets the value of the field on the object.
*
* @param object The object
* @param value The new value
* @throws IllegalStateException The Java object has changed and
* is no longer supported by this handler, or the handler is not
* compatible with the Java object
* @throws IllegalArgumentException The value passed is not of
* a supported type
*/
public void setValue( Object object, Object value )
throws IllegalStateException, IllegalArgumentException
{
Root root = (Root)object;
Date date = null;
try {
date = formatter.parse((String)value);
}
catch(ParseException px) {
throw new IllegalArgumentException(px.getMessage());
}
root.setDate(date);
}
/**
* Creates a new instance of the object described by this field.
*
* @param parent The object for which the field is created
* @return A new instance of the field's value
* @throws IllegalStateException This field is a simple type and
* cannot be instantiated
*/
public Object newInstance( Object parent )
throws IllegalStateException
{
//-- Since it's marked as a string...just return null,
//-- it's not needed.
return null;
}
/**
* Sets the value of the field to a default value.
*
* Reference fields are set to null, primitive fields are set to
* their default value, collection fields are emptied of all
* elements.
*
* @param object The object
* @throws IllegalStateException The Java object has changed and
* is no longer supported by this handler, or the handler is not
* compatible with the Java object
*/
public void resetValue( Object object )
throws IllegalStateException, IllegalArgumentException
{
((Root)object).setDate(null);
}
/**
* @deprecated No longer supported
*/
public void checkValidity( Object object )
throws ValidityException, IllegalStateException
{
// do nothing
}
} |
|
For a ConfigurableFieldHandler, we have to override the method setConfiguration of
the superclass AbstractFieldHandler. AbstractFieldHandler implements this
method on behalf of the ConfigurableFieldHandler interface, but does not do anything.
Define and use the handler
All we have to do now is define the ConfigurableFieldHandler in the mapping file (using a
<field-handler> element), configure it, and use it in some xml field.
mapping.xml |
<?xml version="1.0"?>
<mapping>
<field-handler name="myHandler" class="org.some.package.CustomFieldHandlerImpl">
<param name="date-format" value="yyyyMMddHHmmss"/>
</field-handler>
<class name="Root">
<field name="date" type="string" handler="myHandler"/>
</class>
</mapping> |
|
This mapping file defines a custom configurable field handler instance named "myHandler", of
the type we've just implemented, and configures it with some fancy date pattern. A regular
class mapping defines a field called "date", and specifies that conversion to and from this
field will be performed by the "myHandler" instance.
It would be perfectly legal to use "myHandler" for other fields as well, and also
to define other field-handler instances of the class FieldHandlerImpl (or
any other class).
To complete the example, here's the "Root" class that is used in the example
mapping file:
Root.java |
import java.util.Date;
public class Root {
private Date _date = null;
public Root() {
}
public Date getDate() {
return _date;
}
public void setDate(Date date) {
this._date = date;
}
} |
|
And here a sample XML document:
Sample XML document |
<?xml version="1.0" encoding="UTF-8"?>
<root>
<date>20070711234859</date>
</root> |
|
With these classes, xml mapping file and xml sample file, you should be able to marshall
and unmarshall the date field.
|