/**
<p>Support for the encoding of objects, and the objects reachable from them,
into <code>XML</code>; and the complementary reconstruction of the
object graph from <code>XML</code>.
</p>
<p><i> This page has been translated into
<a href="http://www.webhostinghub.com/support/es/misc/paquete-javolutionxml">Spanish</a>
language by Maria Ramos from <a href="http://www.webhostinghub.com/support/edu">
Webhostinghub.com/support/edu</a>.</i></p>
<h2><a name="OVERVIEW">XML marshalling/unmarshalling facility:</a></h2>
<IMG alt="XML Data Binding" src="doc-files/xmlDataBinding.png">
<p> Key Advantages:<ul>
<li> Real-time characteristics with no adverse effect on memory footprint or
garbage collection (e.g. it can be used for time critical communications).
{@link javolution.xml.XMLFormat XMLFormat} is basically a "smart"
wrapper around our real-time StAX-like
{@link javolution.xml.stream.XMLStreamReader XMLStreamReader} and
{@link javolution.xml.stream.XMLStreamWriter XMLStreamWriter}.</li>
<li> Works directly with your existing Java classes, no need to create new classes
or customize your implementation in any way.</li>
<li> The XML representation can be high level and impervious to obfuscation
or changes to your implementation.</li>
<li> Performance on a par or better than default Java<sup>TM</sup> Serialization/Deserialization
(See <a href="https://bindmark.dev.java.net/">bindmark</a> for performance comparison).</li>
<li> Small footprint, runs on any platform including Android.</li>
<li> The XML mapping can be defined for a top class (or interface) and is automatically
inherited by all sub-classes (or all implementing classes).</li>
<li> Supports object references (to avoid expanding objects already serialized).</li>
</ul>
</p>
<p> The default XML format for a class is typically defined using
the {@link javolution.xml.DefaultXMLFormat} annotation tag.
[code]
@DefaultXMLFormat(Graphic.XML.class)
public abstract class Graphic implements XMLSerializable {
private boolean isVisible;
private Paint paint; // null if none.
private Stroke stroke; // null if none.
private Transform transform; // null if none.
// Default XML format with name associations (members identified by an unique name).
public static class XML extends XMLFormat {
public void write(Graphic g, OutputElement xml) throws XMLStreamException {
xml.setAttribute("isVisible", g.isVisible);
xml.add(g.paint, "Paint");
xml.add(g.stroke, "Stroke");
xml.add(g.transform, "Transform");
}
public void read(InputElement xml, Graphic g) throws XMLStreamException {
g.isVisible = xml.getAttribute("isVisible", true);
g.paint = xml.get("Paint");
g.stroke = xml.get("Stroke");
g.transform = xml.get("Transform");
}
};
}[/code]
Sub-classes may override the inherited XML format:
[code]
@DefaultXMLFormat(Area.XML.class)
public class Area extends Graphic {
private Shape geometry;
// Adds geometry to format.
public static class XML extends XMLFormat<Area> {
XMLFormat graphicXML = new Graphic.XML();
public void write(Area area, OutputElement xml) throws XMLStreamException {
graphicXML.write(area, xml); // Calls parent write.
xml.add(area.geometry, "Geometry");
}
public void read(InputElement xml, Area area) throws XMLStreamException {
graphicXML.read(xml, area); // Calls parent read.
area.geometry = xml.get("Geometry");
}
};
}[/code]
The following writes a graphic area to a file, then reads it:
[code]
// Creates some useful aliases for class names.
XMLBinding binding = new XMLBinding();
binding.setAlias(Color.class, "Color");
binding.setAlias(Polygon.class, "Polygon");
binding.setClassAttribute("type"); // Use "type" instead of "class" for class attribute.
// Writes the area to a file.
XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream("C:/area.xml"));
writer.setBinding(binding); // Optional.
writer.setIndentation("\t"); // Optional (use tabulation for indentation).
writer.write(area, "Area", Area.class);
writer.close();
// Reads the area back
XMLObjectReader reader = XMLObjectReader.newInstance(new FileInputStream("C:/area.xml"));
reader.setBinding(binding);
Area a = reader.read("Area", Area.class);
reader.close();
[/code]
Here is an example of valid XML representation for an area:
[code]
<Area isVisible="true">
<Paint type="Color" rgb="#F3EBC6" />
<Geometry type="Polygon">
<Vertex x="123" y="-34" />
<Vertex x="-43" y="-34" />
<Vertex x="-12" y="123" />
</Geometry>
</Area>[/code]
</p>
<p> The following table illustrates the variety of XML representations supported
(Foo class with a single String member named text):
<center>
<table cellpadding="2" cellspacing="2" border="1" style="text-align: left"><tbody>
<tr>
<td style="vertical-align: top; text-align: center;"><span style="font-weight: bold;">XML FORMAT</span></td>
<td style="vertical-align: top; text-align: center;"><span style="font-weight: bold;">XML DATA</span></td>
</tr>
<tr>
<td>[code]
XMLFormat<Foo> XML = new XMLFormat<Foo>() {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.setAttribute("text", foo.text);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getAttribute("text", "");
}
};[/code]</td>
<td><pre>
<b><!-- Member as attribute --></b>
<Foo text="This is a text"/>
</pre></td>
</tr>
<tr>
<td>
[code]
XMLFormat<Foo> XML = new XMLFormat<Foo>() {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getNext();
}
};[/code]</td>
<td><pre>
<b><!-- Member as anonymous nested element --></b>
<Foo>
<java.lang.String value="This is a text"/>
</Foo>
</pre></td>
</tr>
<tr>
<td>
[code]
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.addText(foo.text); // or xml.getStreamWriter().writeCDATA(foo.text) to use CDATA block.
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getText().toString(); // Content of a text-only element.
}
};[/code]</td>
<td><pre>
<b><!-- Member as Character Data --></b>
<Foo>This is a text</Foo>
</pre></td>
</tr>
<tr>
<td>
[code]
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text, "Text");
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.get("Text");
}
};[/code]</td>
<td><pre>
<b><!-- Member as named element of unknown type --></b>
<Foo>
<Text class="java.lang.String" value="This is a text"/>
</Foo>
</pre></td>
</tr>
<tr>
<td><pre>
[code]
XMLFormat<Foo> XML = new XMLFormat<Foo>(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text, "Text", String.class);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.get("Text", String.class);
}
};[/code]</td>
<td><pre>
<b><!-- Member as named element of actual type known --></b>
<Foo>
<Text value="This is a text"/>
</Foo>
</pre></td>
</tr>
</tbody></table>
</center>
</p>
<p> XML format do not have to use the classes
public no-arg constructors, 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). For example:
[code]
@DefaultXMLFormat(Point.XML.class)
public final class Point implements XMLSerializable {
private int x;
private int y;
private Point() {}; // No-arg constructor not visible.
public static Point valueOf(int x, int y) { ... }
public static class XML = new XMLFormat<Point>() {
public boolean isReferencable() {
return false; // Always manipulated by value.
}
public Point newInstance(Class<Point> cls, InputElement xml) throws XMLStreamException {
return Point.valueOf(xml.getAttribute("x", 0), xml.getAttribute("y", 0));
}
public void write(Point point, OutputElement xml) throws XMLStreamException {
xml.setAttribute("x", point.x);
xml.setAttribute("y", point.y);
}
public void read(InputElement xml, Point point) throws XMLStreamException {
// Do nothing immutable.
}
};
}[/code]
</p>
<p> Document cross-references are supported, including circular references.
Let's take for example:
[code]
@DefaultXMLFormat(xml=Polygon.XML.class)
public class Polygon implements Shape, XMLSerializable {
private Point[] vertices;
public static class XML extends XMLFormat<Polygon> {
public void write(Polygon polygon, OutputElement xml) throws XMLStreamException {
xml.setAttibutes("count", vertices.length);
for (Point p : vertices) {
xml.add(p, "Vertex", Point.class);
}
}
public void read(InputElement xml, Polygon polygon) throws XMLStreamException {
int count = xml.getAttributes("count", 0);
polygon.vertices = new Point[count];
for (int i=0; i < count; i++) {
vertices[i] = xml.get("Vertex", Point.class);
}
}
};
}
Polygon[] polygons = new Polygon[] {p1, p2, p1};
...
TextBuilder xml = TextBuilder.newInstance();
AppendableWriter out = new AppendableWriter().setOutput(xml)
XMLObjectWriter writer = XMLObjectWriter.newInstance(out);
writer.setXMLReferenceResolver(new XMLReferenceResolver()); // Enables cross-references.
writer.write(polygons, "Polygons", Polygon[].class);
writer.close();
System.out.println(xml);
[/code]
Prints the following (noticed that the first polygon and last one are being shared).
[code]
<Polygons length="3">
<Polygon id="0" count="3">
<Vertex x="123" y="-34" />
<Vertex x="-43" y="-34" />
<Vertex x="-12" y="123" />
</Polygon>
<Polygon id="1" count="3">
<Vertex x="-43" y="-34" />
<Vertex x="123" y="-34" />
<Vertex x="-12" y="123" />
</Polygon>
<Polygon ref="0"/>
</Polygons>[/code]
</p>
<B>ALGORITHMS:</B>
<p> Our {@link javolution.xml.XMLObjectReader XMLObjectReader}/{@link javolution.xml.XMLObjectWriter XMLObjectWriter}
are in fact simple wrappers around our <b>J</b>avolution high-performance StAX-like
{@link javolution.xml.stream.XMLStreamReader XMLStreamReader} and
{@link javolution.xml.stream.XMLStreamWriter XMLStreamWriter} classes.
The logic of these wrappers is described below:
<pre>
OutputElement.add(object, name, uri, class):
1. if (object == null) return
2. getStreamWriter().writeStartElement(uri, name)
3. isReference = referenceResolver.writeReference(object, this)
4. if (!isReference) binding.getFormat(class).write(object, this)
5. getStreamWriter().writeEndElement()
6. end
InputElement.get(name, uri, class):
1. if (!getStreamReader().getLocalName().equals(name) ||
!getStreamReader().getNamespaceURI().equals(uri)) return null
2. object = referenceResolver.readReference(inputElement)
3. if (object != null) Goto 8 // Found reference
4. format = binding.getFormat(class)
5. object = format.newInstance(class, inputElement)
6. referenceResolver.createReference(object, inputElement) // Done before parsing to support circular references.
7. format.read(inputElement, object)
8. getStreamReader().nextTag()
9. end
</pre></p>
*/
package javolution.xml;