CAPÍTULO 3. MÉTODO
3.7 Análisis del discurso
3.7.1 Marcadores del discurso
From the discussion of namespaces in Chapter 2, you should be starting to realize their importance and impact on parsing and handling XML. Alongside XML Schema, XML Namespaces is easily the most significant concept added to XML since the original XML 1.0 Recommendation. With SAX 2.0, support for namespaces was introduced at the element level. This allows a distinction to be made between the namespace of an element, signified by an element prefix and an associated namespace URI, and the local name of an element. In this case, the term local name refers to the unprefixed name of an element. For example, the local name of the ora:copyright element is simply copyright. The namespace prefix is ora, and the namespace URI is declared as http://www.oreilly.com.
There are two SAX callbacks specifically dealing with namespaces. These callbacks are invoked when the parser reaches the beginning and end of a prefix mapping. Although this
is a new term, it is not a new concept; a prefix mapping is simply an element that uses the
xmlns attribute to declare a namespace. This is often the root element (which may have multiple mappings), but can be any element within an XML document that declares an explicit namespace. For example:
<catalog> <books>
<book title="XML in a Nutshell"
xmlns:xlink="http://www.w3.org/1999/xlink"> <cover xlink:type="simple" xlink:show="onLoad"
xlink:href="xmlnutCover.jpg" ALT="XML in a Nutshell" width="125" height="350" />
</book> </books> </catalog>
In this case, an explicit namespace is declared several element nestings deep within the document. That prefix and URI mapping (in this case, xlink and
http://www.w3.org/1999/xlink, respectively) are then available to elements and attributes within the declaring element.
The startPrefixMapping( ) callback is given the namespace prefix as well as the URI associated with that prefix. The mapping is considered "closed" or "ended" when the element that declared the mapping is closed, which triggers the endPrefixMapping( )
callback. The only twist to these callbacks is that they don't quite behave in the sequential manner in which SAX usually is structured; the prefix mapping callback occurs directly
before the callback for the element that declares the namespace, and the ending of the mapping results in an event just after the close of the declaring element. However, it actually makes a lot of sense: for the declaring element to be able to use the declared namespace mapping, the mapping must be available before the element's callback. It works in just the opposite way for ending a mapping: the element must close (as it may use the namespace), and then the namespace mapping can be removed from the list of available mappings.
In the JTreeContentHandler, there aren't any visual events that should occur within these two callbacks. However, a common practice is to store the prefix and URI mappings in a data structure. You will see in a moment that the element callbacks report the
namespace URI, but not the namespace prefix. If you don't store these prefixes (reported through startPrefixMapping( )), they won't be available in your element callback code. The easiest way to do this is to use a Map, add the reported prefix and URI to this
Map in startPrefixMapping( ), and then remove them in endPrefixMapping( ). This can be accomplished with the following code additions:
class JTreeContentHandler implements ContentHandler { /** Hold onto the locator for location information */ private Locator locator;
/** Store URI to prefix mappings */ private Map namespaceMappings; /** Tree Model to add nodes to */ private DefaultTreeModel treeModel; /** Current node to add sub-nodes to */ private DefaultMutableTreeNode current;
public JTreeContentHandler(DefaultTreeModel treeModel, DefaultMutableTreeNode base) { this.treeModel = treeModel;
this.current = base;
this.namespaceMappings = new HashMap( ); }
// Existing methods
public void startPrefixMapping(String prefix, String uri) { // No visual events occur here.
namespaceMappings.put(uri, prefix); }
public void endPrefixMapping(String prefix) { // No visual events occur here.
for (Iterator i = namespaceMappings.keySet().iterator( ); i.hasNext( ); ) {
String uri = (String)i.next( );
String thisPrefix = (String)namespaceMappings.get(uri); if (prefix.equals(thisPrefix)) { namespaceMappings.remove(uri); break; } } } }
One thing of note: I used the URI as a key to the mappings, rather than the prefix. As I mentioned a moment ago, the startElement( ) callback reports the namespace URI for the element, not the prefix. So keying on URIs makes those lookups faster. However, as you see in endPrefixMapping( ), it does add a little bit of work to removing the mapping when it is no longer available. In any case, storing namespace mappings in this fashion is a fairly typical SAX trick, so store it away in your toolkit for XML programming.
The solution shown here is far from a complete one in terms of dealing with more complex namespace issues. It's perfectly legal to reassign prefixes to new URIs for an element's scope, or to assign multiple prefixes to the same URI. In the example, this would result in widely scoped namespace mappings being overwritten by narrowly scoped ones in the case where identical URIs were
mapped to different prefixes. In a more robust application, you would want to store prefixes and URIs separately, and have a method of relating the two without causing overwriting. However, you get the idea in the example of how to handle namespaces in the general sense.