Version 1.0 sources and examples in CVS. Javadoc here
YAXOM is an Java-oriented XML-object engine which allows a set of Java new operations (that is, creating objects) and method calls can be scripted away in XML and executed - and the resulting objects made available, by name, to external Java code.
Its main rationale is to decouple configuration (in XML) from instantiation as much as possbile; however, anywhere Java objects need to be instantiated in a scriptable way, but the result used by Java code, Yaxom can come handy.
Usage scenario
Typically, an application needs some configuration file. Property files are ok for simple configuration, but more complex setups may be difficult to render effectively via properties. The typical choice nowadays is to use an XML file, since while it's simple, well known and can be edited by the user, it allows to express relations and dependencies quite effectively by its markup structure.
The typical design goes like this:
As a simple example, say a <directory> element is used to denote a directory;
Let's say a DOM tree is built using a JAXP-compliant parser creating a org.w3c.dom.Document;
After successful parsing, some code will navigate the org.w3c.dom.Document; searching for <directory> Element objects and creating/using corresponding Java.io.File objects, maybe invoking some setDirectory() method on an application object...
..which will for example look for other files in that directory.
A different approach
Nothing wrong with that - if only the problem that, every time the configuration layout is changed, the bit of application that interprets the (runtime representation of the) XML configuration needs to be maintained and updated - and this, besides the other bit which actually makes use of the resulting objects.
Since the final goal is almost always to produce some Java objects, Yaxom cuts the middle man (pardon, code) by allowing (package-public) Java objects to be declared directly in XML, providing an engine which does the repetitive task of parsing the XML and producing such objects.
Also slightly more complex operations, like referencing created objects, setting attributes after construction via public setters, or invoking methods on them are made available.
Most of what you can do in Java, you can do in Yaxom. Note that, however, it is not in the scope of Yaxom to be an optimized Java-in-XML interpreter, so such features (expecially the method calling) should be used with care..
Flow of control
The Yaxom engine (an instance of the class org.sadun.yaxom.Engine) interprets an XML file scanning sequentially each node and interpreting it according to well-defined rules. The entry point are the various createObjects overloads. For example (assuming a JAXP parser is installed in the system),
org.sadun.yaxom.Engine engine = new org.sadun.yaxom.Engine(); Map objects = engine.createObjects(new File("config.xml")); File directory=(File)objects.get("dir1"); if (directory==null) throw new RuntimeException("Missing dir1 in configuration!");will execute config.xml and create the objects defined therein. The example above assumes that one such object is a java.io.File with name (id) dir1
If not in the yaxom: namespace (see below), any encountered XML element is usually interpreted as an instruction for instantiating a Java object.
Each object is given a name ("id", either explicitly by using an yaxom:id attribute or automatically by the system) which can be referenced in future instantiations (in the current version, auto-generated names are hidden), and is put both on a stack and in a Map (using the id, a String object, as key). Objects created subsequently may refer to such objects by id.
If subelements appear in an element, the relative objects are created first and treated as a parameter list for a constructor with a type-compatible parameter list.
When the engine is finished parsing the XML and creating objects, the Map can be retrieved and used to access the programmatically the created Java objects.
An example of Yaxom XML is
<?xml version="1.0"?> <yaxom:objects xmlns:yaxom="http://www.sadun.org/org.sadun.yaxom" xmlns:java.lang="http://java.sun.org/java.lang" <util:IndentedPrintWriter xmlns:util="http://www.sadun.org/org.sadun.util" yaxom:id="pw"> <java.lang:System yaxom:member="out"/> <java.lang:int yaxom:id="tab">5</java.lang:int> </util:IndentedPrintWriter> </yaxom:objects>which creates an org.sadun.util.IndentedPrintWriter wrapping over System.out and an indentation value of 5 (alright, there's no particular reason for which an IndentedPrintWriter should be created with Yaxom, but it's a typical Java class example chosen at random).
Yaxom naming conventions
Yaxom employs conventions in then XML files to minimize the effort for expressing Java object in XML. In particular:
For example <String> may indicate a Java.lang.String object.
For example, xmlns:Java="http://yaxom.sadun.org/Java.lang" can be used to associate the Java XML namespace to the Java.lang Java package. A Java.lang.String object in this case will be matched by the tag <Java:String>.
Note: as a consequence, there aren't import clauses in Yaxom. Each tag which is not in the default XML namespace will indicate its package via its prefix - which can be defined as shorts as needed. Which prefixes to declare depends on the objects which need to be created - for example, it can be lexically convenient to declare j as prefix for Java.lang and jio for Java.io, etc.
For example, <j:String>Hello World</j:String> will instantiate a Java.lang.String object whose value is "Hello World".
For example, <j:String yaxom:id="s">Hello World</j:String> will instantiate a Java.lang.String object whose value is "Hello World" and has an id s (as in writing String s="Hello World").
For example, let's have a class TestClass which has a constructor public TestClass(String s). A Yaxom XML to instantiate it is:
<?xml version="1.0"?>
+--- <ytest:TestClass
|
instantiate a TestClass | xmlns:yaxom="http://www.sadun.org/org.sadun.yaxom" <== Yaxom reserved namespace
using the String created by | xmlns:ytest="http://www.sadun.org/org.sadun.yaxom.test" <== namespace for the test class' package
the <Java.lang:String> -----+ xmlns:Java.lang="http://Java.sun.org/java.lang"> <== namespace for Java.lang package
subelement as construction |
parameter | <Java.lang:String>Hello World</Java.lang:String> <== instantiate a String
|
+--- </ytest:TestClass>
As usual, the yaxom:id attribute may be used to assign a name to the primitive type value. This is equivalent to declaring a named variable in java.
For example, <java.lang:int yaxom:id="integerValue">10</java.lang:int> is equivalent to int integerValue=10; in Java (assuming that an XML namespace declaration xmlns:Java.lang="http://Java.sun.org/java.lang" is present in the XML.
For example, <j:System yaxom:member="out"/> references System.out.