<?xml version="1.0" encoding="ISO-8859-1"?>

<!-- $Id: xml-fieldhandlers.xml,v 1.5 2005/09/20 20:05:16 wguttmn Exp $ -->

<document url="http://castor.org/xml-fieldhandlers.html">

  <properties>
     <author email="keith AT kvisco DOT com">Keith Visco</author>
  </properties>

  <body>
    <title>Castor XML: Writing Custom FieldHandlers</title>


    <section title="Introduction">

       <p>
          Sometimes we need to deal with a data format that Castor doesn't
          support out-of-the-box, such as an unsupported Date/Time
          representation, or we want to wrap and unwrap fields in Wrapper
          objects to get the desired XML output without changing our object
          model. To handle these cases Castor allows specifying a custom
          <api class="org.exolab.castor.mapping.FieldHandler">FieldHandler</api>
          which can do these varying conversions during calls to the fields
          setter and getter methods.
       </p>

       <p>
          <body-note>
          The <i>FieldHandler</i> is the basic interface used by the Castor
          Framework when accessing field values or setting them. By specifying
          a custom <i>FieldHandler</i> in the mapping file we can basically
          intercept the calls to retreive or set a field's value and do
          whatever conversions are necessary.
          </body-note>
        </p>


    </section>

    <section title="Writing a simple FieldHandler">

       <p>
         When a writing a FieldHandler handler we need to provide implementations
         of the various methods specified in the FieldHandler interface. The main
         two methods are the <i>getValue</i> and <i>setValue</i> methods which
         will basically handle all our conversion code. The other methods provide
         ways to create a new instance of the field's value or reset the field
         value.
       </p>
       <p>
         <tip>It's actually even easier to write custom field handlers if we use
              a GeneralizedFieldHandler. See more details in the next section.
         </tip>
       </p>
       <p>
         Let's take a look at how to convert a date in the format YYYY-MM-DD using
         a custom FieldHandler. We want to marshal the following XML input file:
         <code-panel top-caption="test.xml">&lt;?xml version="1.0"?&gt;
&lt;root&gt;2004-05-10&lt;/root&gt;</code-panel>

         The class we'll be marshalling from and unmarshalling to looks as follows:

         <code-panel top-caption="Root.java">
import java.util.Date;

public class Root {

    private Date _date = null;

    public Root() {
        super();
    }

    public Date getDate() {
        return _date;
    }


    public void setDate(Date date) {
        _date = date;
    }
         </code-panel>

        So we need to write a custom FieldHandler that takes the input String
        and converts it into the proper java.util.Date instance:

       <code-panel top-caption="MyDateHandler.java">
import org.exolab.castor.mapping.FieldHandler;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.ValidityException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;



/**
 * The FieldHandler for the Date class
 *
 */
public class MyDateHandler implements FieldHandler
{

    private static final String FORMAT = "yyyy-MM-dd";

    /**
     * Creates a new MyDateHandler instance
     */
    public MyDateHandler() {
        super();
    }

    /**
     * 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
     *  compatiable with the Java object
     */
    public Object getValue( Object object )
        throws IllegalStateException
    {
        Root root = (Root)object;
        Date value = root.getDate();
        if (value == null) return null;
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT);
        Date date = (Date)value;
        return formatter.format(date);
    }


    /**
     * 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
     *  compatiable with the Java object
     * @thorws IllegalArgumentException The value passed is not of
     *  a supported type
     */
    public void setValue( Object object, Object value )
        throws IllegalStateException, IllegalArgumentException
    {
        Root root = (Root)object;
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT);
        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
     *  compatiable 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
    }


}
       </code-panel>

       <p>
         <tip>The <i>newInstance</i> method should return null for immutable types.</tip>
       </p>

       <body-note>
          There is also an
          <api class="org.exolab.castor.mapping.AbstractFieldHandler">AbstractFieldHandler</api>
          that we can extend instead of implementing FieldHandler directly.
          Not only do we not have to implement deprecated methods, but we can also
          gain access to the <i>FieldDescriptor</i> used by Castor.
       </body-note>

       In order to tell Castor that we want to use our Custom FieldHandler
       we must specify it in the mapping file:

        <code-panel top-caption="mapping.xml">

&lt;?xml version="1.0"?&gt;
&lt;mapping&gt;
  &lt;class name="Root"&gt;
     &lt;field name="date" type="string" handler="MyDateHandler"&gt;
        &lt;bind-xml node="text"/&gt;
     &lt;/field&gt;
  &lt;/class&gt;
&lt;/mapping&gt;

        </code-panel>

         We can now use a simple Test class to unmarshal our XML document:

         <code-panel top-caption="Test.java">
import java.io.*;
import org.exolab.castor.xml.*;
import org.exolab.castor.mapping.*;

public class Test {

    public static void main(String[] args) {
	    try {

	        //--load mapping
	        Mapping mapping = new Mapping();
	        mapping.loadMapping("mapping.xml");

           System.out.println("unmarshalling root instance:");
           System.out.println();

           Reader reader = new FileReader("test.xml");
           Unmarshaller unmarshaller = new Unmarshaller(Root.class);
           unmarshaller.setMapping(mapping);
           Root root = (Root) unmarshaller.unmarshal(reader);
           reader.close();

           System.out.println("Root#getDate : " + root.getDate());
	    }
	    catch (Exception e) {
	        e.printStackTrace();
	    }
    }
}

         </code-panel>

         Now simply compile the code and run!

         <code>
% java Test
unmarshalling root instance:

Root#getDate : Mon May 10 00:00:00 CDT 2004
         </code>

         After running our test program we can see that Castor invoked our
         custom FieldHandler and we got our properly formatted date in our
         Root.class.

       </p>

    </section>

    <section title="Writing a GeneralizedFieldHandler">
       <p>
         A <api class="org.exolab.castor.mapping.GeneralizedFieldHandler">
         GeneraliedFieldHandler</api> is an extension of FieldHandler interface
         where we simply write the conversion methods and Castor will automatically
         handle the underlying get/set operations. This allows us to re-use the
         same FieldHandler for fields from different classes that require the
         same conversion.
       </p>
       <body-note>
         <b>Note:</b>
         Currently the GeneralizedFieldHandler cannot be used from a
         <i>binding-file</i> for use with the SourceGenerator, an
         enhancement patch will be checked into CVS for this feature,
         shortly after 0.9.6 final is released.
       </body-note>
      <p>
        The same FieldHandler we used above can be written as a GeneralizedFieldHandler
        as such:
        <code-panel>

import org.exolab.castor.mapping.GeneralizedFieldHandler;
import org.exolab.castor.mapping.FieldDescriptor;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;



/**
 * The FieldHandler for the Date class
 *
 */
public class MyDateHandler
    extends GeneralizedFieldHandler
{

    private static final String FORMAT = "yyyy-MM-dd";

    /**
     * Creates a new MyDateHandler instance
     */
    public MyDateHandler() {
        super();
    }

    /**
     * This method is used to convert the value when the
     * getValue method is called. The getValue method will
     * obtain the actual field value from given 'parent' object.
     * This convert method is then invoked with the field's
     * value. The value returned from this method will be
     * the actual value returned by getValue method.
     *
     * @param value the object value to convert after
     *  performing a get operation
     * @return the converted value.
     */
    public Object convertUponGet(Object value) {
        if (value == null) return null;
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT);
        Date date = (Date)value;
        return formatter.format(date);
    }


    /**
     * This method is used to convert the value when the
     * setValue method is called. The setValue method will
     * call this method to obtain the converted value.
     * The converted value will then be used as the value to
     * set for the field.
     *
     * @param value the object value to convert before
     *  performing a set operation
     * @return the converted value.
     */
    public Object convertUponSet(Object value) {
        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT);
        Date date = null;
        try {
            date = formatter.parse((String)value);
        }
        catch(ParseException px) {
            throw new IllegalArgumentException(px.getMessage());
        }
        return date;
    }

    /**
     * Returns the class type for the field that this
     * GeneralizedFieldHandler converts to and from. This
     * should be the type that is used in the
     * object model.
     *
     * @return the class type of of the field
     */
    public Class getFieldType() {
        return Date.class;
    }

    /**
     * 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;
    }

}

        </code-panel>

        Everything else is the same. So we can re-run our test case using this
        GeneralizedFieldHandler and we'll get the same result. The main difference
        is that we implement the <i>convertUponGet</i> and <i>convertUponSet</i>
        methods.

        <p>
           Notice that we never reference the <i>Root</i> class in our
           GeneralizedFieldHandler. This allows us to use the same exact
           FieldHandler for any field that requires this type of conversion.
        </p>

      </p>
    </section>
    <section title="No Constructor, No Problem!">
      <p>
         A number of classes such as type-safe enum style classes have no constructor, but
         instead have some sort of static factory method used for converting a string value
         into an instance of the class. With a custom FieldHandler we can allow Castor to
         work nicely with these types of classes.
      </p>
     <p>
       <tip>
          Castor XML automatically supports these types of classes if they have a specific
          method:
          <code>public static <i>{Type}</i> valueOf(String)</code>
       </tip>
       <body-note>Note: We're working on the same support for Castor JDO</body-note>
     </p>

     <p>
     Even though Castor XML supports the "valueOf" method type-safe enum style classes, we'll
     show you how to write a custom handler for these classes anyway since it's useful for
     any type of class regardless of the name of the factory method.
     </p>
     <p>
       Let's look at how to write a handler for the following type-safe enum style class,
       which was actually generated by Castor XML (javadoc removed for brevity):
       <code-panel>

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;

public class Color implements java.io.Serializable {


    public static final int RED_TYPE = 0;

    public static final Color RED = new Color(RED_TYPE, "red");

    public static final int GREEN_TYPE = 1;

    public static final Color GREEN = new Color(GREEN_TYPE, "green");

    public static final int BLUE_TYPE = 2;

    public static final Color BLUE = new Color(BLUE_TYPE, "blue");

    private static java.util.Hashtable _memberTable = init();

    private int type = -1;

    private java.lang.String stringValue = null;


    private Color(int type, java.lang.String value) {
        super();
        this.type = type;
        this.stringValue = value;
    } //-- test.types.Color(int, java.lang.String)


    public static java.util.Enumeration enumerate()
    {
        return _memberTable.elements();
    } //-- java.util.Enumeration enumerate()

    public int getType()
    {
        return this.type;
    } //-- int getType()

    private static java.util.Hashtable init()
    {
        Hashtable members = new Hashtable();
        members.put("red", RED);
        members.put("green", GREEN);
        members.put("blue", BLUE);
        return members;
    } //-- java.util.Hashtable init()

    public java.lang.String toString()
    {
        return this.stringValue;
    } //-- java.lang.String toString()

    public static Color valueOf(java.lang.String string)
    {
        Object obj = null;
        if (string != null) obj = _memberTable.get(string);
        if (obj == null) {
            String err = "'" + string + "' is not a valid Color";
            throw new IllegalArgumentException(err);
        }
        return (Color) obj;
    } //-- test.types.Color valueOf(java.lang.String)

}
       </code-panel>

      The <i>GeneralizedFieldHandler</i> for the above <i>Color</i> class is as follows
      (javadoc removed for brevity):

      <code-panel top-caption="ColorHandler.java">
import org.exolab.castor.mapping.GeneralizedFieldHandler;
import org.exolab.castor.mapping.FieldDescriptor;

/**
 * The FieldHandler for the Color class
**/
public class ColorHandler
    extends GeneralizedFieldHandler
{

    public ColorHandler() {
        super();
    }

    public Object convertUponGet(Object value) {
        if (value == null) return null;
        Color color = (Color)value;
        return color.toString();
    }


    public Object convertUponSet(Object value) {
        return Color.valueOf((String)value);
    }

    public Class getFieldType() {
        return Color.class;
    }

    public Object newInstance( Object parent )
        throws IllegalStateException
    {
        //-- Since it's marked as a string...just return null,
        //-- it's not needed.
        return null;
    }

}
      </code-panel>
     </p>
     <p>
      That's all there really is to it. Now we just need to hook this up to our mapping file
      and run a sample test.
     </p>
     <p>
       If we have a root class <i>Foo</i> as such:
       <code-panel top-caption="Foo.java">
public class Foo {

    private Color _color = null;
    private int _size = 0;
    private String _name = null;

    public Foo() {
        super();
    }

    public Color getColor() {
        return _color;
    }

    public String getName() {
        return _name;
    }

    public int getSize() {
        return _size;
    }

    public void setColor(Color color) {
        _color = color;
    }

    public void setName(String name) {
        _name = name;
    }

    public void setSize(int size) {
        _size = size;
    }

}
       </code-panel>
       <br/>
       Our mapping file would be the following:
       <code-panel top-caption="mapping.xml">
&lt;?xml version="1.0"?&gt;
&lt;mapping&gt;
  &lt;class name="Foo"&gt;
     &lt;field name="size" type="integer"&gt;
        &lt;bind-xml node="element"/&gt;
     &lt;/field&gt;
     &lt;field name="name" type="string"/&gt;
     &lt;field name="color" type="string" handler="ColorHandler"/&gt;
  &lt;/class&gt;
&lt;/mapping&gt;
       </code-panel>

       We can now use our custom FieldHandler to unmarshal the following xml input:
       <code-panel top-caption="test.xml">
&lt;?xml version="1.0"?&gt;
&lt;foo&gt;
   &lt;name&gt;MyFoo&lt;/name&gt;
   &lt;size&gt;345&lt;/size&gt;
   &lt;color&gt;blue&lt;/color&gt;
&lt;/foo&gt;
       </code-panel>

       A sample test class is as follows:
       <code-panel top-caption="Test.java">

import java.io.*;
import org.exolab.castor.xml.*;
import org.exolab.castor.mapping.*;

public class Test {

    public static void main(String[] args) {
	    try {

	        //--load mapping
	        Mapping mapping = new Mapping();
	        mapping.loadMapping("mapping.xml");

            System.out.println("unmarshalling Foo:");
            System.out.println();

            Reader reader = new FileReader("test.xml");
            Unmarshaller unmarshaller = new Unmarshaller(Foo.class);
            unmarshaller.setMapping(mapping);
            Foo foo = (Foo) unmarshaller.unmarshal(reader);
            reader.close();

            System.out.println("Foo#size : " + foo.getSize());
            System.out.print("Foo#color: ");
            if (foo.getColor() == null) {
                System.out.println("null");
            }
            else {
                System.out.println(foo.getColor().toString());
            }

            PrintWriter pw = new PrintWriter(System.out);
            Marshaller marshaller = new Marshaller(pw);
            marshaller.setMapping(mapping);
            marshaller.marshal(foo);
            pw.flush();
	    }
	    catch (Exception e) {
	        e.printStackTrace();
	    }
    }
}
        </code-panel>
     </p>
    </section>

    <section title="Collections and FieldHandlers">
       <p>
         <body-note>The <i>GeneralizedFieldHandler</i> checked into the CVS
         version (main branch / 0.9.6 Final) of Castor automatically supports iterating over
         the items of a collection and passing them one-by-one to the
         <i>convertUponGet</i>. For backward compatibility or to handle
         the collection iteration yourself, simply add the following to
         the constructor of your GeneralizedFieldHandler implementation:
         <code>
            setCollectionIteration(false);
         </code>
         </body-note>
      </p>
      <p>
         If you're going to be using custom field handlers for collection fields with
         a GeneralizedFieldHandler using versions of Castor prior to 0.9.6, then
         you'll need to handle the collection iteration yourself in the
         <i>convertUponGet</i>  method.
       </p>
      <p>
         If you're not using a GeneralizedFieldHandler then you'll need to handle the
         collection iteration yourself in the <i>FieldHandler#getValue()</i> method.
       </p>
       <p><tip>Since Castor incrementally adds items to collection fields, there
          usually is no need to handle collections directly in the
          <i>convertUponSet</i> method (or the <i>setValue()</i> for those
          not using GeneralizedFieldHandler).
          </tip>
       </p>
    </section>

  </body>
</document>
