Properties and Features
I glossed over validation in the last chapter, and probably left you with a fair amount of questions. When I cover JAXP in , you'll see that you can use either a method (setValidating( )) or a set of classes (javax.xml.validation) to handle validation; you might expect to call a similar methodsetValidation( ) or something similarto initiate validation in SAX. But then, there's also namespace awareness, dealt with quite a bit in (and , with respect to Q names and local namesmaybe setNamespaceAwareness( )? But what about schema validation? And setting the location of a schema to validate on, if the document doesn't specify one? There's also low-level behavior, like telling the parser what to do with entities (parse them? don't parse them?), how to handle strings, and a lot more. As you can imagine, dealing with each of these could cause real API bloat, adding 20 or 30 methods to SAX's XMLReader class. And, even worse, each time a new setting was needed (perhaps for the next type of constraint model supported? How about setRelaxNGSchema( )?), the SAX API would have to add a method or two, and re-release a new version. Clearly, this isn't a very effective approach to API design.
|
To address the ever-changing need to affect parser behavior, without causing constant API change, SAX 2 defines a standard mechanism for setting parser behavior: through the use of properties and features.
Setting Properties and Features
In SAX, a property is a setting that requires passing in some Object argument for the parser to use; for instance, certain types of handlers are set by specifying a URI and supplying the Object that implements that handler's interface. A feature is a setting that is either on (true) or off (false). Several obvious examples come to mind: namespace awareness and validation, for example. SAX includes the methods needed for setting properties and features in the XMLReader interface. This means you have to change very little of your existing code to request validation, set the namespace separator, and handle other feature and property requests. The methods used for setting these properties and features are outlined in Table 4-1.
Table 4-1. You'll use features almost every time you write a program to parse XML; properties are less commonly used, but still important in your XML programming
Method | Returns | Parameters | Syntax |
---|---|---|---|
setProperty( ) |
void |
String propertyID, Object value |
parser.setProperty("[Property URI]", propertyValue); |
setFeature( ) |
void |
String featureID, boolean state |
parser.setFeature("[Feature URI]", featureState); |
getProperty( ) |
Object |
String propertyID |
Object propertyValue = parser.getProperty("[Property URI]"); |
getFeature( ) |
boolean |
String featureID |
boolean featureState = parser.getFeature("[Feature URI]"); |
For all of these, the ID of a specific property or feature is a URI. The standard set of features and properties is listed in the Appendix 0. Additionally, most parsers define additional, vendor-specific properties that their parser supports. For example, Apache Xerces defines quite a few additional features and properties, listed online at http://xml.apache.org/xerces2-j/features.html and http://xml.apache.org/xerces2-j/properties.html.
My Feature URI Is a 404!Feature and property URIs are similar to namespace URIs; they are only used as identifiers for particular options. If you typed one of these URIs into a browser, you'd probably get a nice 404 Not Found error. Good parsers ensure that you do not need network access to resolve these features and properties; think of them as simple constants that happen to be in URI form. These methods are simply invoked and the URI is dereferenced locally, often to constantly represent what action in the parser needs to be taken. |
The most convenient aspect of these methods is that they allow simple addition and modification of properties and features. Although new or updated features will require a parser implementation to add supporting code, the method by which features and properties are accessed remains standard and simple; only a new URI need be defined. Regardless of the complexity (or obscurity) of new XML-related ideas, this robust set of four methods should be sufficient to allow parsers to implement the new ideas.
Error Handling
Invoking the setFeature( ) and setProperty( ) methods can result in SAXNotSupportedExceptions and SAXNotRecognizedExceptions.
|
The first, SAXNotSupportedException, indicates that the parser "knows" about the feature or property but doesn't support it. This is commonly used when a standard property or feature is not yet coded in (such as in alpha or beta versions of parsers). So invoking setFeature("http://xml.org/sax/features/namespaces") on a parser in development might result in a SAXNotSupportedException. The parser recognizes the feature (and probably plans to support it at some point), but doesn't have the ability to perform the requested processing. The second exception, SAXNotRecognizedException, commonly occurs when your code uses vendor-specific features and properties, and then you switch out your parser implementations. The new implementation won't know anything about the other vendor's features or properties, and will throw a SAXNotRecognizedException. You should always explicitly catch these exceptions so you can report them, rather than treating them as just another generic SAXException. Otherwise, you end up losing valuable information about what happened in your code. This means you may have to write a bit of extra code, but thus is the price for good exception handling; here's a slightly updated version of the buildTree( ) method (detailed originally in ) that handles these problems gracefully:
public void buildTree(DefaultTreeModel treeModel, DefaultMutableTreeNode base, String xmlURI) throws IOException, SAXException { String featureURI = ""; XMLReader reader = null; try { // Create instances needed for parsing reader = XMLReaderFactory.createXMLReader( ); JTreeHandler jTreeHandler = new JTreeHandler(treeModel, base); // Register content handler reader.setContentHandler(jTreeHandler); // Register error handler reader.setErrorHandler(jTreeHandler); // Turn on validation featureURI = "http://xml.org/sax/features/validation"; reader.setFeature(featureURI, true); // Turn on schema validation, as well featureURI = "http://apache.org/xml/features/validation/schema"; reader.setFeature(featureURI, true); // Parse InputSource inputSource = new InputSource(xmlURI); reader.parse(inputSource); } catch (SAXNotRecognizedException e) { System.err.println("The parser class " + reader.getClass( ).getName( ) + " does not recognize the feature URI '" + featureURI + "'"); System.exit(-1); } catch (SAXNotSupportedException e) { System.err.println("The parser class " + reader.getClass( ).getName( ) + " does not support the feature URI '" + featureURI + "'"); System.exit(-1); } }