In the SAX model of processing, the system calls event-handler methods as it parses the file. As the whole document does not have to be kept in main memory, this is quite memory-efficient but then global variables have to be used for communicating between handler methods. In our case, the only global information needed are the current indentation and the fact that the current line be ended.
The current line should be ended at the start of a new element only when the last thing printed was a closing bracket. Many closing brackets can be put on the same line though. So we keep a shared boolean variable for this state and a shared integer storing the current number of blank spaces used for indentation.
Creating a SAX parser is done via a factory in much the
same way as we have shown in the previous section for a DOM parser. Example 8.3 describes the main procedure which creates a factory
(line 29‑) and sets flags for validation. The
parser is obtained on line 32‑
and a property is
set to indicate that we want validation to be done with an XML Schema. Parsing is then
started on line 36‑
by passing a reference to an
Handler
object which receives call-backs during the parsing process.
Example 8.3. [SAXCompact.java
] Text compaction of the cellar book (Example 2.2) with Java using the SAX model
1 import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.XMLReaderFactory; 5 import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; 10 import java.io.IOException; import javax.swing.JTree; import javax.swing.JFrame; import javax.swing.JScrollPane; 15 public class SAXCompact { private static JTree jtree = new JTree();20 public static void main(String argv[]) {
if (argv.length != 1) { System.out.println("Usage: java SAXCompact file"); return; } 25 //XMLParser creation SAXParserFactory factory; SAXParser saxParser; try { factory = SAXParserFactory.newInstance();
30 factory.setNamespaceAware(true); factory.setValidating(true); saxParser = factory.newSAXParser();
saxParser.setProperty( "http://java.sun.com/xml/jaxp/properties/schemaLanguage", 35 "http://www.w3.org/2001/XMLSchema"); // parse file and print compact form
saxParser.parse(argv[0], new CompactHandler()); System.out.println(); // parse file and build a tree form 40 saxParser.parse(argv[0],new JTreeHandler(jtree));
// display the built tree new TreeViewer(jtree).show();
} catch (ParserConfigurationException e) {
System.out.println("Bad parser configuration"); 45 } catch (SAXParseException e) { System.out.println(argv[0]+" is not well-formed"); System.out.println(e.getMessage()+ " at line "+e.getLineNumber()+ ", column "+e.getColumnNumber()); 50 } catch (SAXException e){ System.out.println(e.getMessage()); } catch (IOException e) { System.out.println("IO Error on "+argv[0]); } 55 } // main(String[]) } // class SAXCompact
|
Initialises an empty |
|
Main method that first checks if a file name has been given as an argument to the program. |
|
Creates a document factory. |
|
Creates a new SAX parser. |
|
Parses the file given as argument while sending the parse events to a handler that will output the compact form. |
|
Parses the file given as argument while sending the parse events to a
handler that will add nodes to a |
|
Displays the interactive tree viewer. |
|
Handles exceptions that can occur in both parsing and input file processing. |
Example 8.4 shows the structure of a SAX event handler
(a subclass of the DefaultHandler
class), which defines empty handlers for
all type of events including errors. In our case, only startElement
(line 20‑),
endElement
(line 38‑) and
characters
(line 45‑) are called when encountering text nodes.
When an error is encountered during the parsing process, one of the methods defined
on lines starting on line 66‑ is called. When this happens, we
call the
message
method (line 58‑) which
prints some useful information about the error.
Example 8.4. [CompactHandler.java
] SAX handler for text
compacting an XML file such as Example 2.2
1 import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; 5 public class CompactHandler extends DefaultHandler{ // production of string of spaces with a lazy StringBuffer private static StringBuffer blanks = new StringBuffer(); 10 private String blanks(int n){ for(int i=blanks.length();i<n;i++) blanks.append(' '); return blanks.substring(0,n); } 15 private boolean closed = false; // closed mode? protected int indent; // current indentation value 20 public void startElement(String uri, String localName,String raw, Attributes attrs) throws SAXException { if(closed){ System.out.print('\n'+blanks(indent)); 25 closed = false; } indent=indent+1+localName.length(); System.out.print(localName+'['); // deal with attributes 30 for (int i = 0; i < attrs.getLength(); i++) { if(i>0)System.out.print('\n'+blanks(indent)); System.out.print('@'+attrs.getLocalName(i)+'[' +attrs.getValue(i)+']'); closed=true; 35 } } public void endElement(String uri, String localName, String raw)
throws SAXException { 40 System.out.print(']'); closed = true; indent=indent-1-localName.length(); } 45 public void characters(char[] ch, int start, int length)
throws SAXException { if(closed){ System.out.print('\n'+blanks(indent)); closed = false; 50 } String s = new String(ch,start,length).trim(); System.out.print(s); if(s.length()>0) closed=true; 55 } // error handling... private void message(String mess,SAXParseException e)
throws SAXException{ 60 System.out.println("\n"+mess+ "\n Line:"+e.getLineNumber()+ "\n URI:" +e.getSystemId()+ "\n Message:"+e.getMessage()); } 65 public void fatalError(SAXParseException e) throws SAXException{
message("Fatal error",e); } 70 public void error(SAXParseException e) throws SAXException{ message("Error",e); } public void warning(SAXParseException e) throws SAXException{ 75 message("Warning",e); } }
|
Checks whether the current line should be terminated. Then the name of the current element and an opening bracket are printed. The shared indentation value is updated and the attributes are output if there are any. |
|
A closing bracket is printed and the indentation value is decreased by
the length of the name. Because the file has been validated during the
parsing process, it is sure that the |
|
The characters are printed after having removed leading and trailing
whitespace. Note that the |
|
Method used to output error messages. |
|
Error handling for fatal, validation and recoverable errors. |