Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 4
The SwingUtilities.invokeLater() method only requires a Runnable object and executes that object's run() method on the Swing event-dispatcher thread. In the lostFocus() method, I replaced the call to requestFocus() with an instantiation of a FocusRequester object. That change results in focus returning successfully to the field that failed validation.
While we might like to be able to call any methods at any point in a program, such a simple approach fails with complex implementations
like the Swing event dispatcher. The solution is to use the built-in SwingUtilities class to interact with the event dispatcher.
All well-formed XML files have a tree structure. For example, Listing 8.1, myaddresses.xml, can be represented by a tree with two ADDRESS nodes:
Listing 8.1: myaddresses.xml
<?xml version="1.0"?>
<!DOCTYPE ADDRESS_BOOK SYSTEM "abml.dtd">
<ADDRESS_BOOK>
<ADDRESS>
<NAME>Joe Jones </NAME>
<STREET>4332 Sunny Hill Road
</STREET>
<CITY>Fairfax</CITY>
<STATE>VA</STATE>
<ZIP>21220</ZIP>
</ADDRESS>
<ADDRESS>
<NAME>Sterling Software
</NAME>
<STREET> 7900 Sudley
Road</STREET>
<STREET> Suite 500</STREET>
<CITY>Manassas</CITY>
<STATE>VA </STATE>
<ZIP>20109
</ZIP>
</ADDRESS>
</ADDRESS_BOOK>
Often, XML beginners wrongly assume that a DOM tree will look exactly like their mental image of the corresponding XML document.
Let's say you must find the first NAME element of the first ADDRESS in Listing 8.1. By looking at the XML, you might think that the DOM's third node is the one you want. Listing 8.2 attempts
to find the node in that way:
Listing 8.2: BadDomLookup.java
package com.javaworld.jpitfalls.article4;
import javax.xml.parsers.*;
import java.io.*;
import org.w3c.dom.*;
public class BadDomLookup
{
public static void main(String args[])
{
try
{
if (args.length < 1)
{
System.out.println("USAGE: " +
"com.javaworld.jpitfalls.article4.BadDomLookup xmlfile");
System.exit(1);
}
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(args[0]));
// get first Name of first Address
NodeList nl = doc.getElementsByTagName("ADDRESS");
int count = nl.getLength();
System.out.println("# of \"ADDRESS\" elements: " + count);
if (count > 0)
{
Node n = nl.item(0);
System.out.println("This node name is: " +
n.getNodeName());
// get the NAME node of this ADDRESS node
Node nameNode = n.getFirstChild();
System.out.println("This node name is: " +
nameNode.getNodeName());
}
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
The simple program BadDomLookup uses the Java API for XML Processing (JAXP) to parse the DOM (I tested this example with both Xerces 1.3 and Sun's default
JAXP parser). After you obtain the W3C Document object, you retrieve a NodeList of ADDRESS elements and then look to obtain the first NAME element by accessing the first child under ADDRESS.
When you execute Listing 8.2, this is what you get:
C:\classes\com\javaworld\jpitfalls\article4>java com.javaworld.jpitfalls.article4.BadDomLookup myaddresses.xml # of "ADDRESS" elements: 2 This node name is: ADDRESS This node name is: #text
The result clearly shows that the program has failed to accomplish its task. Instead of an ADDRESS node, you've received a text node. What happened?
Unfortunately, the complexity of the DOM implementation differs from the simple conceptual model. The primary disparity is
that the DOM tree includes text nodes for ignorable white space. Ignorable white space is white space that falls between tags -- a carriage return, for example. In Listing 8.2, there is
a text node between the ADDRESS and the first NAME element.
There are two solutions to this problem, and Listing 8.3, a rewrite of our program, demonstrates both of them:
Listing: 8.3 GoodDomLookup.java
package com.javaworld.jpitfalls.article4;
import javax.xml.parsers.*;
import java.io.*;
import org.w3c.dom.*;
class DomUtil
{
public static boolean isBlank(String buf)
{
if (buf == null)
return false;
int len = buf.length();
for (int i=0; i < len; i++)
{
char c = buf.charAt(i);
if (!Character.isWhitespace(c))
return false;
}
return true;
}
public static void normalizeDocument(Node n)
{
if (!n.hasChildNodes())
return;
NodeList nl = n.getChildNodes();
for (int i = 0; i < nl.getLength(); i++)
{
Node cn = nl.item(i);
if (cn.getNodeType() == Node.TEXT_NODE &&
isBlank(cn.getNodeValue()))
{
n.removeChild(cn);
i--;
}
else
normalizeDocument(cn);
}
}
public static Element getFirstChildElement(Element elem)
{
if (!elem.hasChildNodes())
return null;
for (Node cn = elem.getFirstChild(); cn != null;
cn = cn.getNextSibling())
{
if (cn.getNodeType() == Node.ELEMENT_NODE)
return (Element) cn;
}
return null;
}
}
public class GoodDomLookup
{
public static void main(String args[])
{
try
{
if (args.length < 1)
{
System.out.println("USAGE: " +
"com.javaworld.jpitfalls.article4.BadDomLookup xmlfile");
System.exit(1);
}
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File(args[0]));
// get first Name of first Address
System.out.println("Method #1: Skip Ignorable White space...");
NodeList nl = doc.getElementsByTagName("ADDRESS");
int count = nl.getLength();
System.out.println("# of \"ADDRESS\" elements: " + count);
if (count > 0)
{
Node n = nl.item(0);
System.out.println("This node name is: " +
n.getNodeName());
// get the NAME node of this ADDRESS node
Node nameNode = DomUtil.getFirstChildElement((Element)n);
System.out.println("This node name is: " +
nameNode.getNodeName());
}
// get first Name of first Address
System.out.println("Method #2: Normalize document...");
DomUtil.normalizeDocument(doc.getDocumentElement());
// Below is exact code in BadDomLookup
nl = doc.getElementsByTagName("ADDRESS");
count = nl.getLength();
System.out.println("# of \"ADDRESS\" elements: " + count);
if (count > 0)
{
Node n = nl.item(0);
System.out.println("This node name is: " +
n.getNodeName());
// get the NAME node of this ADDRESS node
Node nameNode = n.getFirstChild();
System.out.println("This node name is: " +
nameNode.getNodeName());
}
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
The key class in GoodDomLookup is the DomUtil class, which has three methods. Those methods solve the DOM lookup problem in two ways: