Resolver APIs

Volume 10, Issue 10; 09 Feb 2007; last modified 08 Oct 2010

How can I resolve thee? Let me count the ways.

Working on my XMLResolver project has made me look again at the range of entity and URI resolution APIs in the Java platform. How many are there? I got to six without breaking a sweat.

You can resolve with SAX, org.xml.sax.EntityResolver:

InputSource resolveEntity(String publicId, String systemId);

Here we can't look at the entity name nor can we look at the system identifier before it's been made absolute. There's also no way for this interface to indicate failure to resolve, so we can't effectively chain resolvers.

Also with SAX, org.xml.sax.ext.EntityResolver2:

InputSource getExternalSubset(String name, String baseURI);

InputSource resolveEntity(String name,
                          String publicId,
                          String baseURI,
                          String systemId);

Getting an external subset for a document that doesn't have one is kind of cool, though I've never actually tried it.

This version of resolveEntity fixes the bugs in the earlier API: we can examine the system identifier before and after making it absolute and we can return null to indicate that we weren't able to resolve it.

With StAX, javax.xml.stream.XMLResolver:

Object resolveEntity(String publicID,
                     String systemID,
                     String baseURI,
                     String namespace)

Oh, joy! It returns Object! And notice how its signature conflicts with the EntityResolver2 interface so you can't even implement both APIs in a single class.

I can't imagine how a namespace URI comes into play for resolving an entity that includes either a system or public identifier. And vice versa.

And where's the name of the entity? That I could actually use!

We're just warming up, DOM gets into the act too, with org.w3c.dom.ls.LSResourceResolver:

LSInput resolveResource(String type,
                        String namespaceURI,
                        String publicId,
                        String systemId,
                        String baseURI);

Another bizarre conflation of external identifiers and namespace names.

The string valued “type” makes this a sort of chameleon method. If you specify a type of “http://www.w3.org/TR/REC-xml” then it's supposed to resolve “XML resources (i.e. entities)” (sic). But without the entity name, apparently.

If you specify a type of “http://www.w3.org/2001/XMLSchema” then it's supposed to resolve “XML Schema [resources]”. But when does anything have both a namespace and an external identifier?

(If you specify any other type, you're on your own. Fair enough, I guess.)

The Transformer API has one too, javax.xml.transform.URIResolver:

Source resolve(String href, String base);

This is just for URIs, so it's at least a little less complicated.

Finally, contributing to the problem myself, I've proposed one more in org.xmlresolver.NamespaceResolver:

Source resolveNamespace(String uri, String nature, String purpose);

This one resolves namespaces using RDDL natures and purposes. (You aren't supposed to use relative URIs as namespace names, so my decision not to support them in this API was intentional.)

On the whole: Bleh!

I wonder how many of these are actually used with any regularity? I wonder if there's any opportunity for unification and simplification? (And I wonder how many more there are out there.)

Comments

Manual trackback: Embedding XSLT transformations in JARs, or how I learned to (not) love the resolver.

The whole thing is a complete mess, especially as users have to implement and set each resolver on it's own, where most users simply want to manage all resolution attempts.

—Posted by Martin Probst on 11 Feb 2007 @ 11:46 UTC #