This is the package which defines the structure of the HDX system, registers new types, and is responsible for managing the implementations of the various factories required.
HDX is defined as a DOM. This does not mean that an HDX object necessarily is a DOM -- it could be represented as a Source, as a SAX stream, or ultimately not in an XML form at all -- however, it is defined using DOM terminology, and all operations on it are representable in those terms.
An HDX is a DOM {@link org.w3c.dom.Document} node,
which has a document element of type <hdx>
, which
in turn contains other elements of registered
types. HDX does not define or constrain these other types
(instead providing facilities for defining
them), other than to require that they be defined in DOM terms.
The types are not generally processable using the DOM, however, and so they must be transformed into a Java object before work can be done on them. This transformation may involve significant (one-time) work, if the object has to be assembled from information in the DOM, or it may be trivial, if the DOM in fact mirrors some underlying object. During processing, the object will generally be manipulated using some interface defined as part of the resulting Java object's specification.
All important elements in the HDX DOM correspond to types
registered with the HDX system. Only those elements which correspond
to registered types can be constructed as Java objects. However, not
all elements must be registered -- elements which are embedded within
important types, and which do not have to be children of the
<hdx>
element, do not have to be registered or have
handlers.
Types are registered by creating an instance of class {@link uk.ac.starlink.hdx.HdxResourceType}, using the static method {@link uk.ac.starlink.hdx.HdxResourceType#newHdxResourceType newHdxResourceType}. See below for an example.
The two core classes in the Hdx package are:
The most important interfaces are:
HdxContainer
. HdxFactory
is
what achieves this, possibly with the background help of an
HdxDocumentFactory
.The most important interfaces for implementors are:
HdxDocumentFactory
are what
constructs HdxContainer
s from URIs and URLs. A class
which is able to construct
a DOM from a particular class of URL -- such as a URL pointing to a
FITS file, for example -- registers a HdxDocumentFactory
instance with HdxFactory
's {@link
uk.ac.starlink.hdx.HdxFactory#registerHdxDocumentFactory
registerHdxDocumentFactory}: when HdxFactory
is asked to
construct a DOM by a call to its {@link
uk.ac.starlink.hdx.HdxFactory#newHdxContainer newHdxContainer} methods,
it works through the list of registered factories until one of them
successfully constructs the DOM. The methods of this interface are
not called directly by client code, but are implemented by those
adding new file formats (such as FITS or HDS, say) to the Hdx system.HdxResourceFactory
type are responsible for exposing a DOM
element as a Java object. The methods of this interface are not
called directly by client code, but are implemented by those adding
new types (such as Weather, say) to the Hdx system.getHdxFacade
method. This can be incorporated into a DOM
based on {@link uk.ac.starlink.hdx.HdxDocument} by using that class's extension method
{@link uk.ac.starlink.hdx.HdxDocument#createElement(HdxFacade)}: the
resulting element behaves exactly like a org.w3c.dom
element, except that all its DOM-building (and optionally
DOM-mutating) methods are handled by the HdxFacade
, which
knows exactly the state of the underlying object, and which can return
the underlying object particularly easily.The HdxContainer
interface is used by most code which
wants to use Hdx objects. The two factory interfaces are not
used by client code, but exist in order to be implemented by those
extending the system.
Hdx objects may be synthesized from binary data files such as HDS or FITS files, or else they may be extracted from the DOMs constructed from XML files. If the thing (Element or URI) passed to method {@link uk.ac.starlink.hdx.HdxFactory#newHdxContainer newHdxContainer} corresponds to a registered Hdx type and it is in no namespace, then the input DOM is processed as you might expect.
The factory can also process elements in a more sophisticated way.
If the element passed to newHdxContainer
does not
correspond to a registered type, or it is in the Hdx namespace, then
the method will examine only those elements and attributes in
the Hdx namespace
(see W3C namespaces
spec). This means that the Hdx information is able to be carried
inside another unrelated XML file, invisible to a namespace-aware
processor examinging only the `background' XML.
The Hdx namespace is defined to be
http://www.starlink.ac.uk/HDX
, which string is accessible
as
{@link uk.ac.starlink.hdx.HdxResourceType#HDX_NAMESPACE}. The Hdx DOM
which is constructed from such an input element contains only the
Hdx-registered elements, in no namespace, so that users of the DOM do
not have to worry about the occasionally subtle details of namespace
processing. Thus the XML file
<rubbish xmlns:x="http://www.starlink.ac.uk/HDX"> <x:hdx> <x:ndx> <x:data uri="file:/tmp/mydata.sdf"/> </x:ndx> </x:hdx> </rubbish>
is transformed into the simpler DOM
<hdx> <ndx> <data uri="file:/tmp/mydata.sdf"/> </ndx> </hdx>
Changes made to the attributes in this transformed DOM are reflected in the original DOM.
Further transformations happen when a DOM is imported into
Hdx. Firstly, the attribute `name
' in the Hdx namespace
is transformed into an element with that name. Secondly, if an
Hdx type has a `hoist' attribute defined (see
{@link uk.ac.starlink.hdx.HdxResourceType#setHoistAttribute setHoistAttribute}),
then any text content of the element will be hoisted up to form the
content of that hoist attribute. Finally, if the Hdx DOM which
results from this does not have <hdx>
as its top
element, but instead an element which is a valid child of
<hdx>
, then it is inserted into a
<hdx>
element. Thus
<mystructure> <mypointer x:name="ndx" xmlns:x="http://www.starlink.ac.uk/HDX"> mydata.sdf </mypointer> </mystructure>
is transformed into
<hdx> <ndx uri="mydata.sdf"/> </hdx>
and processed accordingly.
If you are particularly familiar with the W3C Namespaces
spec, then you will have objected that the `uri
'
attribute in the first example was formally in no namespace,
and should not therefore have been processed. This is true, but it is
so unhelpfully counter-intuitive that as a special case, the
normalisation process described here processes such no-namespace
attributes in the Hdx namespace if the element is itself in
the Hdx namespace, as is the case in the example above.
Use
{@link uk.ac.starlink.hdx.HdxFactory}.{@link uk.ac.starlink.hdx.HdxFactory#getInstance getInstance()}.{@link uk.ac.starlink.hdx.HdxFactory#newHdxContainer(java.net.URI)
newHdxContainer(URI)}
to extract HDX objects from URIs, and
method
{@link uk.ac.starlink.hdx.HdxFactory#newHdxContainer(org.w3c.dom.Element) newHdxContainer(Element)}
to extract Hdx objects from DOMs, using the namespace mechanism
described above.
To define a new type, you must create a new {@link uk.ac.starlink.hdx.HdxResourceType}, using method {@link uk.ac.starlink.hdx.HdxResourceType#newHdxResourceType}, and then act on the resulting object to register validators and constructors. For example, the (very simple) definition of the {@link uk.ac.starlink.hdx.HdxResourceType#TITLE} object consists of:
TITLE = HdxResourceType.newHdxResourceType("title"); TITLE.setHoistAttribute("value"); TITLE.setElementValidator(new ElementValidator() { public boolean validateElement(Element el) { // A TITLE element is valid if it has an attribute "value" return HdxResourceType.match(el) == TITLE && el.hasAttribute(TITLE.getHoistAttribute()); } });
The hoist attribute means that, as described in the namespaces section above, the element
<title>My title</title>
is equivalent to the
normalised form <title value="My title"/>
.
The {@link uk.ac.starlink.hdx.HdxResourceType#HDX} type has a somewhat more complicated validator, plus the assertion
HDX.setConstructedClass("uk.ac.starlink.hdx.HdxContainer");
that constructed (Java) objects must be instances of the {@link uk.ac.starlink.hdx.HdxContainer} interface.
This creation and registration code is located in the static initialiser of a suitable class. This registration is therefore performed whenever this class is loaded and initialised. That happens whenever an object of that class is created, or a static method invoked. The class may also be loaded explicitly by declaring some properties, as described in {@link uk.ac.starlink.hdx.HdxResourceType}.
New types may be declared in their own package, or in the package
uk.ac.starlink.hdx.extension
; non-Starlink types should
not be declared in the uk.ac.starlink.hdx
package, nor in any other package with prefix uk.ac.starlink
.
For an example of a simple class, see the sample class
SimpleWeather.java
, included in the distribution.
To add a new file format which can be given a DOM interface, you must create a class which implements {@link uk.ac.starlink.hdx.HdxDocumentFactory}, which turns a URL into a DOM Document.
Such extending classes are registered with the Hdx system using {@link uk.ac.starlink.hdx.HdxFactory#registerHdxDocumentFactory}, by a mechanism which is described in the {@link uk.ac.starlink.hdx.HdxFactory} class documentation.
These factories can be arbitrarily clever. They can either create a simple DOM and wait for the work of reading the file to be done by the constructors registered with the type using {@link uk.ac.starlink.hdx.HdxResourceType#registerHdxResourceFactory}, or else the factory can construct the object, using a class which implements the {@link uk.ac.starlink.hdx.HdxFacade} interface, and then create an Element which uses that facade using {@link uk.ac.starlink.hdx.HdxDocument#createElement(HdxFacade)}. For further details about this route, including an example, see the {@link uk.ac.starlink.hdx.HdxDocumentFactory} documentation.
That covers construction of a complete Hdx DOM from a URI. However, you should also handle, if appropriate, a reference to your new file format from within a DOM. In the case of the Ndx type, that can be done with a construction such as
ndxType.registerHdxResourceFactory(new HdxResourceFactory() { public Object getObject(Element el) throws HdxException { return new BridgeNdx(new DomNdxImpl(el)); } });
where ndxType
is the HdxResourceType
object which this initialiser has obtained either from
HdxResourceType.match("ndx")
or, preferably, from its
initialisation of the Ndx type using newHdxResourceType
.
The class DomNdxImpl
is a class which is private to the
Ndx package.
See the Starlink web pages.