Example 18-1 illustrates how you might use the interfaces discussed in this chapter in a typical programming situation. This application takes a document that uses the furniture.dtd sample DTD from Chapter 20 and validates that the parts list included in the document matches the actual parts used within the document.
/**
* PartsCheck.java
*
* DOM Usage example from the O'Reilly _XML in a Nutshell_ book.
*
*/
// we'll use the Apache Software Foundation's Xerces parser.
import org.apache.xerces.parsers.*;
import org.apache.xerces.framework.*;
// import the DOM and SAX interfaces
import org.w3c.dom.*;
import org.xml.sax.*;
// get the necessary java support classes
import java.io.*;
import java.util.*;
/**
* This class is designed to check the parts list of an XML document that
* represents a piece of furniture for validity. It uses the DOM to
* analyze the actual furniture description and then check it against the
* parts list that is embedded in the document.
*/
public class PartsCheck {
// static constants
public static final String FURNITURE_NS =
"http://namespaces.oreilly.com/furniture/";
// contains the true part count, keyed by part number
HashMap m_hmTruePartsList = new HashMap( );
/**
* The main function that allows this class to be invoked from the command
* line. Check each document provided on the command line for validity.
*/
public static void main(String[] args) {
PartsCheck pc = new PartsCheck( );
try {
for (int i = 0; i < args.length; i++) {
pc.validatePartsList(args[i]);
}
} catch (Exception e) {
System.err.println(e);
}
}
/**
* Given a system identifier for an XML document, this function compares
* the actual parts used to the declared parts list within the document. It
* prints warnings to standard error if the lists don't agree.
*/
public void validatePartsList(String strXMLSysID) throws IOException,
SAXException
{
// create a new parser
DOMParser dp = new DOMParser( );
// parse the document and get the DOM Document interface
dp.parse(strXMLSysID);
Document doc = dp.getDocument( );
// get an accurate parts list count
countParts(doc.getDocumentElement( ), 1);
// compare it to the parts list in the document
reconcilePartsList(doc);
}
/**
* Updates the true parts list by adding the count to the current count
* for the part number given.
*/
private void recordPart(String strPartNum, int cCount)
{
if (!m_hmTruePartsList.containsKey(strPartNum)) {
// this part isn't listed yet
m_hmTruePartsList.put(strPartNum, new Integer(cCount));
} else {
// update the count
Integer cUpdate = (Integer)m_hmTruePartsList.get(strPartNum);
m_hmTruePartsList.put(strPartNum, new Integer(cUpdate.intValue( ) + cCount));
}
}
/**
* Counts the parts referenced by and below the given node.
*/
private void countParts(Node nd, int cRepeat)
{
// start the local repeat count at 1
int cLocalRepeat = 1;
// make sure we should process this element
if (FURNITURE_NS.equals(nd.getNamespaceURI( ))) {
Node ndTemp;
if ((ndTemp = nd.getAttributes( ).getNamedItem("repeat")) != null) {
// this node specifies a repeat count for its children
cLocalRepeat = Integer.parseInt(ndTemp.getNodeValue( ));
}
if ((ndTemp = nd.getAttributes( ).getNamedItem("part_num")) != null) {
// start the count at 1
int cCount = 1;
String strPartNum = ndTemp.getNodeValue( );
if ((ndTemp = nd.getAttributes( ).getNamedItem("count")) != null) {
// more than one part needed by this node
cCount = Integer.parseInt(ndTemp.getNodeValue( ));
}
// multiply the local count by the repeat passed in from the parent
cCount *= cRepeat;
// add the new parts count to the total
recordPart(strPartNum, cCount);
}
}
// now process the children
NodeList nl = nd.getChildNodes( );
Node ndCur;
for (int i = 0; i < nl.getLength( ); i++) {
ndCur = nl.item(i);
if (ndCur.getNodeType( ) == Node.ELEMENT_NODE) {
// recursively count the parts for the child, using the local repeat
countParts(ndCur, cLocalRepeat);
}
}
}
/**
* This method reconciles the true parts list against the list in the document.
*/
private void reconcilePartsList(Document doc)
{
Iterator iReal = m_hmTruePartsList.keySet().iterator( );
String strPartNum;
int cReal;
Node ndCheck;
// loop through all of the parts in the true parts list
while (iReal.hasNext( )) {
strPartNum = (String)iReal.next( );
cReal = ((Integer)m_hmTruePartsList.get(strPartNum)).intValue( );
// find the part list element in the document
ndCheck = doc.getElementById(strPartNum);
if (ndCheck == null) {
// this part isn't even listed!
System.err.println("missing <part_name> element for part #" +
strPartNum + " (count " + cReal + ")");
} else {
Node ndTemp;
if ((ndTemp = ndCheck.getAttributes( ).getNamedItem("count")) != null) {
int cCheck = Integer.parseInt(ndTemp.getNodeValue( ));
if (cCheck != cReal) {
// counts don't agree
System.err.println("<part_name> element for part #" +
strPartNum + " is incorrect: true part count = " + cReal +
" (count in document is " + cCheck + ")");
}
} else {
// they didn't provide a count for this part!
System.err.println("missing count attribute for part #" +
strPartNum + " (count " + cReal + ")");
}
}
}
}
}
When this application is run over the bookcase.xml sample document from Chapter 20, it generates the following output:
missing count attribute for part #HC (count 8) <part_name> element for part #A is incorrect: true part count = 2 (count in document is 1)
To compile and use this sample application, download and install the Xerces Java Parser from the Apache-XML project (http://xml.apache.org/xerces-j). The code was compiled and tested with Sun's JDK Version 1.3.1.
Copyright © 2002 O'Reilly & Associates. All rights reserved.