Provides support for the encoding of objects, and the objects reachable from them, into XML; and the complementary reconstruction of the object graph from XML.

This facility is similar to existing products such as JSX or XStream, with the following advantages:

The default XML mapping for a class and its sub-classes is defined using a static final {@link javolution.xml.XmlFormat XmlFormat} instance. For example:

      public abstract class Graphic {
          private boolean _isVisible;
          private Paint _paint; // null if none.
          private Stroke _stroke; // null if none.
          private Transform _transform; // null if none.
           
          // XML format with positional associations for child elements 
          // (see {@link javolution.xml.XmlFormat XmlFormat} for examples of type-based associations).
          protected static final XmlFormat GRAPHIC_XML = new XmlFormat(Graphic.class) {
               public void format(Object obj, XmlElement xml) {
                   Graphic g = (Graphic) obj;
                   xml.setAttribute("isVisible", g._isVisible); 
                   xml.getContent().addLast(g._paint);
                   xml.getContent().addLast(g._stroke);
                   xml.getContent().addLast(g._transform);
               }
               public Object parse(XmlElement xml) {
                   Graphic g = (Graphic) xml.object();
                   g._isVisible = xml.getAttribute("isVisible", true);
                   g._paint = (Paint) xml.getContent().removeFirst();
                   g._stroke = (Stroke) xml.getContent().removeFirst();
                   g._transform = (Transform) xml.getContent().removeFirst();
                   return g;
              }
          };
      }
Sub-classes may override the inherited XML format:
      public class Area extends Graphic {
          private Surface _geometry;  
        
          // Adds geometry (surface) to format.
          protected static final XmlFormat AREA_XML = new XmlFormat(Area.class) {
              public void format(Object obj, XmlElement xml) {
                  Area area = (Area) obj;
                  GRAPHIC_XML.format(area, xml); // Calls parent format.
                  xml.getContent().addLast(area._geometry);
              }
              public Object parse(XmlElement xml) {
                  Area area = (Area) GRAPHIC_XML.parse(xml); // Calls parent parse.
                  area._geometry = (Surface) xml.getContent().removeFirst();
                  return area;
              }
          };
      }
The following writes a graphic area to a file, then reads it:
      new ObjectWriter().write(area, new FileOutputStream("C:/area.xml"));
      Area a = (Area) new ObjectReader().read(new FileInputStream("C:/area.xml"));

For multiple objects transmissions over open I/O streams, {@link javolution.xml.XmlInputStream} and {@link javolution.xml.XmlOutputStream} are recommended.

Here is an example of valid XML representation for an area:
      <graphics:Area xmlns:graphics="java:org.jscience.graphics" isVisible="true">
          <graphics:Color rgb="#F3EBC6"/>
          <Null/>
          <Null/>
          <graphics:geom2d.Polygon id="1">
              <graphics:geom2d.Point x="123" y="-34"/>
              <graphics:geom2d.Point x="-43" y="-34"/>
              <graphics:geom2d.Point x="-12" y="123"/>
          </graphics:geom2d.Polygon>
      </graphics:Area>

XML formats can be dynamically created or modified. The following illustrates the creation of a xml format for double[] instances using java.lang.Double objects.

       // XML format for java.lang.Double 
       XmlFormat doubleXml = new XmlFormat() { 
           public void format(Object obj, XmlElement xml) {
               xml.setAttribute("value", ((Double)obj).doubleValue());
           }
           public Object parse(XmlElement xml) {
               return new Double(xml.getAttribute("value", 0.0));
           }
       };
       XmlFormat.setInstance(doubleXml, java.lang.Double.class); // {@link javolution.realtime.LocalContext Local} setting.
       
       // XML format for double[] 
       XmlFormat doubleArrayXml = new XmlFormat() {
           public void format(Object obj, XmlElement xml) {
               double[] values = (double[])obj;
               xml.setAttribute("length", values.length);
               for (int i=0; i < values.length;) 
                   xml.getContent().add(new Double(values[i++]));
           }
           public Object parse(XmlElement xml) {
               int length = xml.getAttribute("length", 0);
               double[] values = new double[length];
               Iterator i=xml.getContent().fastIterator();
               for (int j=0; j < length;) 
                   values[j++] = ((Double)i.next()).doubleValue();
               return values;
            }
       };
       XmlFormat.setInstance(doubleArrayXml, new double[0].getClass()); // {@link javolution.realtime.LocalContext Local} setting.
       
       // Replaces default "[D" tag with "double[]" (easier to read) 
       XmlFormat.setAlias(doubleArrayXml.getMappedClass(), "double[]");

An alternative implementation could utilize {@link javolution.realtime.Realtime real-time} numbers instead of java.lang.Double (faster and no garbage generated when marshalling/unmarshalling is performed in a {@link javolution.realtime.PoolContext PoolContext}).

The {@link javolution.xml.XmlFormat XmlFormat} does not have to use the class public default constructor ({@link javolution.xml.XmlElement#object xml.object()}), instances can be created using factory methods, private constructors (with constructor parameters set from the XML element) or even retrieved from a collection (if the object is shared or unique).

Cross-references are supported for formats having an {@link javolution.xml.XmlFormat#identifier identifier} attribute. For example:

    public class Polygon extends Surface { 
        FastList _vertices = new FastList(); 
        
        protected static final XmlFormat POLYGON_XML = new XmlFormat(Polygon.class) {
            public String identifier(boolean isReference) { 
                return isReference ? "ref" : "id";          
            }                              
            public void format(Object obj, XmlElement xml) {
                Polygon polygon = (Polygon) obj;
                xml.getContent().addAll(polygon._vertices); 
            }
            public Object parse(XmlElement xml) {
                Polygon polygon = (Polygon) xml.object();
                polygon._vertices.addAll(xml.getContent());
                return polygon;
            }
        };
    }
Here the XML representation of a list of three polygons, the first and the last one being shared:
      <root:java.util.ArrayList xmlns:root="java:" xmlns="java:org.jscience.graphics.geom2d>
          <Polygon id="1">
              <Point x="123" y="-34"/>
              <Point x="-43" y="-34"/>
              <Point x="-12" y="123"/>
          </Polygon>
          <Polygon id="2">
              <Point x="-43" y="-34"/>
              <Point x="123" y="-34"/>
              <Point x="-12" y="123"/>
          </Polygon>
          <Polygon ref="1"/>
      </root:java.util.ArrayList>
The value of the identifier attribute can be set explicitly. For example:
      public abstract class Person {
          private String _name;
          protected static final XmlFormat PERSON_XML = new XmlFormat(Person.class) {
              public String identifier(boolean isReference) {
                  return isReference ? "ref" : "name";
              }
              public void format(Object obj, XmlElement xml) {
                  Person person = (Person) obj;
                  xml.setAttribute("name", person._name); // Sets identifier value explicitly.
              }
              public Object parse(XmlElement xml) {
                  Persone person = (Person) xml.object();
                  person._name = xml.getAttribute("name", "");
                  return person;
              }
          };
      }

Circular references are supported for formats having the {@link javolution.xml.XmlFormat#preallocate XmlFormat.preallocate(xml)} method implemented.

Finally, here is a code excerpt illustrating how objects can be efficiently transmitted over the network using the java.nio facility instead of classic I/O (slower):

      // Client thread.
      ObjectReader or = new ObjectReader();
      ByteBuffer bb = ByteBuffer.allocateDirect(XML_SIZE);
      SocketChannel sc = SocketChannel.open(new InetSocketAddress(LOCAL_HOST, PORT));
      sc.read(bb); // Reads socket into byte buffer.
      bb.flip();
      Object obj = or.read(bb); // Parses byte buffer.
      bb.clear();
          ...
      // Server thread.
      ObjectWriter ow = new ObjectWriter();
      ByteBuffer bb = ByteBuffer.allocateDirect(XML_SIZE);
      ServerSocketChannel ssc = ServerSocketChannel.open();
      ssc.socket().bind(new InetSocketAddress(PORT));
      SocketChannel sc = ssc.accept(); // Waits for connections.
      ow.write(obj, bb); // Formats object into byte buffer.
      bb.flip();
      sc.write(bb); // Sends byte buffer.
      bb.clear();

When using NIO, the ByteBuffer capacity has to be large enough to hold the largest XML representation of the objects being transmitted.