uk.ac.starlink.hdx
Class HdxFactory

java.lang.Object
  extended by uk.ac.starlink.hdx.HdxFactory

public class HdxFactory
extends Object

Marshalls all the object creation factories involved in the Hdx layer.

To create a new HdxContainer from a URI, use the following paradigm:

 URI myData = new URI("...");
 HdxFactory factory = HdxFactory.getInstance();
 HdxContainer myHdx = factory.newHdxContainer(myData);
 // process myHdx...
 
If you already have a DOM obtained from an XML file, then you can extract the HdxContainer from it using the newHdxContainer(Element) method.

This class, or rather the group of newHdxContainer methods, is also the gatekeeper for the Hdx system, in the sense that it is this class which ensures that the HdxContainer which it produces is in a normalised form, and is valid. The Hdx validation is that defined by the static method HdxResourceType.isValidHdx(Document) and the validators for the individual types, HdxResourceType.isValid(Element).

Instances of this class also provide the facilities to resolve URIs into URLs, supplying whatever context is required in the form of base URIs. They do this using the fullyResolveURI(java.net.URI, org.w3c.dom.Node) method. Since the context required will typically depend on the DOM context, you should find an appropriate factory using findFactory(org.w3c.dom.Node).

To add an extension class, which handles a new file type, you must do two things:

  1. Define a class which implements HdxDocumentFactory, and which registers itself as such a factory, through a call to registerHdxDocumentFactory(uk.ac.starlink.hdx.HdxDocumentFactory), in a static initialiser.
  2. If the new class is called my.new.type, and if there is an `Hdx property' HdxDocumentFactory.load.my.new.type with the value true (or anything which the Boolean class evaluates to true), then the specified class is loaded during initialization of this HdxFactory class. This is an `Hdx property', which means that it may be specified as either a System property, or in an Hdx property file as described in HdxProperties.

Version:
$Id$
Author:
Norman Gray

Method Summary
static HdxFactory findFactory(Node el)
          Obtains the factory instance which should handle a particular element.
 URL fullyResolveURI(String uri, Node context)
          Completely resolves a URI, expressed as a string, into a URL.
 URL fullyResolveURI(URI uri, Node context)
          Completely resolves a URI into a URL.
static HdxFactory getInstance()
          Obtains an instance of the HdxFactory.
 Object getObject(Element el)
          Recovers the Java object which corresponds to the given element.
 HdxContainer newHdxContainer(Element el)
          Constructs a new HdxContainer from the supplied DOM.
 HdxContainer newHdxContainer(Element el, URI systemId)
          Constructs a new HdxContainer from the supplied DOM.
 HdxContainer newHdxContainer(HdxFacade facade)
          Constructs a new HdxContainer which wraps an HdxFacade.
 HdxContainer newHdxContainer(URI uri)
          Constructs a new HdxContainer from the supplied URI.
 HdxContainer newHdxContainer(URL url)
          Constructs a new HdxContainer from the supplied URL.
static void registerHdxDocumentFactory(HdxDocumentFactory factory)
          Adds a HdxDocumentFactory to the list of factories tried by newHdxContainer(java.net.URI).
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

getInstance

public static HdxFactory getInstance()
Obtains an instance of the HdxFactory.


findFactory

public static HdxFactory findFactory(Node el)
Obtains the factory instance which should handle a particular element.

Parameters:
el - indicates the DOM context which the returned factory is to service
Returns:
a HdxFactory instance which can resolve URIs appropriately

registerHdxDocumentFactory

public static void registerHdxDocumentFactory(HdxDocumentFactory factory)
Adds a HdxDocumentFactory to the list of factories tried by newHdxContainer(java.net.URI). The factory is added at the beginning of the list of factories tried.

Parameters:
factory - an object implementing the HdxDocumentFactory interface.

newHdxContainer

public HdxContainer newHdxContainer(URI uri)
                             throws HdxException
Constructs a new HdxContainer from the supplied URI. The resulting HdxContainer is normalised as described in newHdxContainer(Element,URI).

Parameters:
uri - a URI pointing to the resource
Returns:
a new HdxContainer, or null if there is no handler available to parse the given URI
Throws:
HdxException - if there is an unexpected problem creating the HdxContainer (that is, if we ought to be able to handle this URI, but fail for some reason)

newHdxContainer

public HdxContainer newHdxContainer(URL url)
                             throws HdxException
Constructs a new HdxContainer from the supplied URL. The resulting HdxContainer is normalised as described in newHdxContainer(Element,URI).

Parameters:
url - a URL pointing to the resource
Returns:
a new HdxContainer, or null if there is no handler available to parse the given URI
Throws:
HdxException - if there is an unexpected problem creating the HdxContainer (that is, if we ought to be able to handle this URL, but fail for some reason)

newHdxContainer

public HdxContainer newHdxContainer(Element el)
                             throws HdxException
Constructs a new HdxContainer from the supplied DOM.

Equivalent to newHdxContainer(el, null)

Parameters:
el - an element in or beneath which there is the XML which represents a single HDX object
Returns:
an HdxContainer, or null if there is not exactly one HDX element in the DOM
Throws:
HdxException
See Also:
newHdxContainer(Element,URI)

newHdxContainer

public HdxContainer newHdxContainer(Element el,
                                    URI systemId)
                             throws HdxException
Constructs a new HdxContainer from the supplied DOM. Searches through the tree beneath the given element to find an element which is in the HDX namespace (for permissible syntax, see HdxResourceType). This method expects to find only a single HDX element in the DOM tree it receives: if there is more than one, it returns null.

The DOM which this builds is normalised, in the sense that:

The new DOM is backed by the old one, so that changes in the new one also appear in the old one.

The Element argument should contain one or more HDX objects (that is, elements representing an <hdx> element in the HDX namespace). However, as a special case, if it contains only elements which are the content of HDX objects, then they are put inside a new HDX object if that can be done unambiguously. This is a heuristic fix, and its behaviour may change in future.

If the element argument is both an HDX-type element (that is, <hdx> or <ndx> or the like) and is in no namespace, then this method will not look for the HDX namespace in the elements below, and indeed will ignore elements in the HDX namespace. Otherwise, the method will examine only elements and attributes in the HDX namespace. As a special case, if the element is in the HDX namespace, then any unprefixed attributes on the element are taken to be in that namespace also. This is a contradiction to the XML standard, which states that unprefixed attributes are in no namespace, not even the default one. However, there are no reasonable cases where this behaviour is useful, and it is massively confusing, so this constitutes best practice.

Parameters:
el - an element in or beneath which there is the XML which represents a single HDX object
systemId - the system identifier for this element, or null if none is defined
Returns:
an HdxContainer, or null if there is not exactly one HDX element in the DOM
Throws:
HdxException - if there is an unexpected problem normalizing the DOM

newHdxContainer

public HdxContainer newHdxContainer(HdxFacade facade)
                             throws HdxException
Constructs a new HdxContainer which wraps an HdxFacade.

Unlike the other newHdxContainer methods, this does not guarantee to normalise the (implied) input DOM immediately, and this may be postponed until the (implied) resulting Hdx DOM is produced at a later stage. It is only whenever this normalisation is finally done that any errors in the input DOM will materialise. However, since the HdxFacade is a synthetic view of an underlying structure, under control of the same code which is ultimately responsible for validating the input DOM, one can presume that any normalisation errors are bugs in that code. Thus this method either succeeds or thows an exception, and does not return null on any error.

This method is part of the preferred route for generating XML from a Java object which is part of the HDX world. Given that the object, obj has a getHdxFacade method, or equivalent, the route is:

 Element el = HdxFactory
     .getInstance()  // or some suitable .findFactory(...)
     .newHdxContainer(obj.getHdxFacade())
     .getDOM(null) ; // or a suitable base URI
 
Although this seems roundabout, (a) this method is written to be efficient with this route in mind, and (b) this ensures that the DOM or Source produced by the HdxFacade is processed for output in a way consistent with other XML generated by this package.

Parameters:
facade - an HdxFacade which represents an underlying object
Returns:
an HdxContainer
Throws:
HdxException - if there is some problem constructing the DOM

getObject

public Object getObject(Element el)
                 throws HdxException
Recovers the Java object which corresponds to the given element. This finds the correct underlying method, resolves the necessary URIs and URLs, supplying any necessary context when doing so, then invokes the real accessor.

The DOM element should be regarded as the master representation of the data object, thus it is the Element which client code should generally hold on to, rather than the Java object, which can be extracted from the element using this method at any time. That is why this is a get... method rather than being referred to as a constructor.

The returned object is checked to correspond to the type registered using HdxResourceType.setConstructedClass(java.lang.Class).

Parameters:
el - an element in the Hdx DOM
Returns:
an object of the appropriate type, or null on any error
Throws:
HdxException - if the given element is somehow inconsistent
PluginException - (unchecked exception) if the underlying code returned an object which did not match the registered target class obtained from HdxResourceType.getConstructedClass(), if that is non-null

fullyResolveURI

public URL fullyResolveURI(URI uri,
                           Node context)
                    throws HdxException
Completely resolves a URI into a URL. This performs both relative to absolute URI resolution (if required), and URI to URL conversion, in both cases supplying all required context. The result is an absolute URL.

The relative-to-absolute resolution is performed using the base URI corresponding to the given context node, as specified by XML Base Recommendation, section 4.

If no base URI can be determined, then the method uses a last-ditch base URI consisting of the file: URI corresponding to the current directory (as obtained from the System property user.dir).

Note that the natural-looking URI file:filename.xml is an absolute URI, according to RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax (since it has a non-null scheme part). Such URIs should be avoided, but since they are common, and intended to be relative URIs, we special-case this, in a deviation from RFC 2396 by removing the scheme part (this conforms with the remarks on backward compatibility in RFC 2396, section 5.2, step 3, although, as blessed in that section) we do not extend this latitude to other schemes). We allow the file scheme-specifier to be mixed-case.

Parameters:
uri - the URI which is to be resolved. If this is absolute already, it is merely converted to a URL.
context - a DOM Node providing the context for any resolution required; this may be null
Returns:
a URL corresponding to an absolute URI, or null if the URI cannot be resolved for some reason.
Throws:
HdxException - if any of the URI or URL exceptions are thrown, which should happen only if there is some syntactic problem with the input URI. Also thrown if the input URI was relative, and it is impossible to determine a base URI to resolve it against.

fullyResolveURI

public URL fullyResolveURI(String uri,
                           Node context)
                    throws HdxException
Completely resolves a URI, expressed as a string, into a URL. This performs both relative to absolute URI resolution, and URI to URL resolution, in both cases supplying all required context. The result is an absolute URL.

Parameters:
uri - a String holding the URI
context - a DOM Node providing the context for any resolution required; this may be null
Returns:
a URL corresponding to an absolute URI, or null if the URI cannot be resolved for some reason.
Throws:
HdxException - if any of the URI or URL exceptions are thrown, which should happen only if there is some syntactic problem with the input URI.
See Also:
fullyResolveURI(URI,Node)


Copyright © 2015 Central Laboratory of the Research Councils. All Rights Reserved.