Design of XSOM

By Kohsuke Kawaguchi

This document describes the details you need to know to extend/maintain XSOM.

Design Goals

The primary design goals of XSOM are:

  1. Expose all the information defined in the schema spec
  2. Provide additional methods that helps simplifying client applications.

Providing mutation methods was a non-goal for this project, primarily because of the added complexity.

Building workspace

The workspace uses Ant as the build tool. The followings are the major targets:

clean
remove the intermediate and output files.
compile
generate a parser by RelaxNGCC and compile all the source files into the bin directory.
jar
make a jar file
release
build a distribution zip file that contains everything from the source code to a binary file
src-zip
Build a zip file that contains the source code.

Architecture

XSOM consists of roughly three parts. The first part is the public interface, which is defined in the com.sun.xml.xsom package. The entire functionality of XSOM is exposed via this interface. This interface is derived from a draft document submitted to W3C by some WG members.

The second part is the implementation of these interfaces, the com.sun.xml.xsom.impl package. These code are all hand-written.

The third part is a parser that reads XML representation of XML Schema and builds XSOM nodes accordingly. The package is com.sun.xml.xsom.parser. This part of the code is mostly generated by RelaxNGCC.

Implementation Details

Most of the implementation classes are fairly simple. Probably the only one interesting piece of code is the Ref class, which is a reference to other schema components.

The Ref class itself is just a place hodler and this class defined a series of inner interfaces that are specialized to hold a reference to different kinds of schema components. The sole purpose of this indirection layer is to support forward references during a parsing of the XML representation.

A typical reference interface would look like this:

public static interface Term {
    /** Obtains a reference as a term. */
    XSTerm getTerm();
}

In case this indirection is unnecessary, all implementation classes of XSTerm implements this Ref.Term interface. This applies to all the other types of the Ref interface. Therefore, whereever a reference is necessary, you can stimply pass a real object. In other words, a direct reference (XS***Impl) can be always treated as an indirect reference (Ref.***).

Implementations for forward references are placed in the com.sun.xml.xsom.impl.parser.DelayedRef class. The detail will be discussed later.

Parser

The following collaboration diagram shows various objects that participate in a parsing process.

XSOMParser is the only publicly visible component in this picture. This class also keeps references to vairous other objects that are necessary to parse schemas. This includes an error handler, the root SchemaSet object, an entity resolver, etc.

Whenever the parse method is called, it will create a new NGCCRuntimeEx and configure XMLReader so that a schema file is parsed into this NGCCRuntimeEx instance. NGCCRuntimeEx derives from NGCCRuntime, which is a class generated by RelaxNGCC. This object will use other RelaxNGCC-generated classes and parse a document and constructs a XSOM object graph appropriately.

When a new XML document is referenced by an import or include statement, a new set of NGCCRuntimeEx is set up to parse that document. One NGCCRuntimeEx can only parse one XML document.

Forward references and back-patching

Since we use SAX to parse schemas, the referenced schema component is often unavailable when we hit a reference. Because of this, when we see a reference, we create a "delayed" reference that keeps the name of the referenced component.

Note that because of the way XML Schema <redefine> works, all the references by name must be lazily bound even if the component is already defined.

All these "delayed" references are remembered and tracked by XSOMParser. When the client calls the XSOMParser.getResult method, XSOMParser will make sure that they resolve to a schema component correctly. "Delayed" references are available in the DelayedRef class.

RelaxNGCC

The actual parser is generated by RelaxNGCC from xsom/src/*.rng files. xmlschema.rng is the entry point and all the other files are referenced from this file. For more information about RelaxNGCC, goto here. Or just contact me (as I'm one of the developers of RelaxNGCC.)