/*
* XMLDemoScreen.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.xmldemo;
import java.io.InputStream;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.xml.parsers.DocumentBuilder;
import net.rim.device.api.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* The main screen for the application. Displays the results of parsing the XML
* file.
*/
public final class XMLDemoScreen extends MainScreen {
// Statics
// -------------------------------------------------------------------------------------
private static final String _xmlFileName = "/xml/bookstore.xml";
// Constants
// -----------------------------------------------------------------------------------
private static final int _tab = 4;
/**
* This constructor parses the XML file into a W3C DOM document, and
* displays it on the screen.
*
* @see Document
* @see DocumentBuilder
* @see DocumentBuilderFactory
*/
public XMLDemoScreen() {
setTitle("XML Demo");
try {
// Build a document based on the XML file.
final DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
final DocumentBuilder builder = factory.newDocumentBuilder();
final InputStream inputStream =
getClass().getResourceAsStream(_xmlFileName);
final Document document = builder.parse(inputStream);
// Normalize the root element of the XML document. This ensures that
// all Text
// nodes under the root node are put into a "normal" form, which
// means that
// there are neither adjacent Text nodes nor empty Text nodes in the
// document.
// See Node.normalize().
final Element rootElement = document.getDocumentElement();
rootElement.normalize();
// Display the root node and all its descendant nodes, which covers
// the entire
// document.
displayNode(rootElement, 0);
} catch (final Exception e) {
System.out.println(e.toString());
}
}
/**
* Displays a node at a specified depth, as well as all its descendants.
*
* @param node
* The node to display.
* @param depth
* The depth of this node in the document tree.
*/
private void displayNode(final Node node, final int depth) {
// Because we can inspect the XML file, we know that it contains only
// XML elements
// and text, so this algorithm is written specifically to handle these
// cases.
// A real-world application will be more robust, and will handle all
// node types.
// See the entire list in org.w3c.dom.Node.
// The XML file is laid out such that each Element node will either have
// one Text
// node child (e.g. <Element>Text</Element>), or >= 1 children
// consisting of at
// least one Element node, and possibly some Text nodes. Start by
// figuring out
// what kind of node we're dealing with.
if (node.getNodeType() == Node.ELEMENT_NODE) {
final StringBuffer buffer = new StringBuffer();
indentStringBuffer(buffer, depth);
final NodeList childNodes = node.getChildNodes();
final int numChildren = childNodes.getLength();
final Node firstChild = childNodes.item(0);
// If the node has only one child and that child is a Text node,
// then it's of
// the form <Element>Text</Element>, so print 'Element = "Text"'.
if (numChildren == 1 && firstChild.getNodeType() == Node.TEXT_NODE) {
buffer.append(node.getNodeName()).append(" = \"").append(
firstChild.getNodeValue()).append('"');
add(new RichTextField(buffer.toString()));
} else {
// The node either has > 1 children, or it has at least one
// Element node child.
// Either way, its children have to be visited. Print the name
// of the element
// and recurse.
buffer.append(node.getNodeName());
add(new RichTextField(buffer.toString()));
// Recursively visit all this node's children.
for (int i = 0; i < numChildren; ++i) {
displayNode(childNodes.item(i), depth + 1);
}
}
} else {
// Node is not an Element node, so we know it is a Text node. Make
// sure it is
// not an "empty" Text node (normalize() doesn't consider a Text
// node consisting
// of only newlines and spaces to be "empty"). If it is not empty,
// print it.
final String nodeValue = node.getNodeValue();
if (nodeValue.trim().length() != 0) {
final StringBuffer buffer = new StringBuffer();
indentStringBuffer(buffer, depth);
buffer.append('"').append(nodeValue).append('"');
add(new RichTextField(buffer.toString()));
}
}
}
/**
* Adds leading spaces to the provided string buffer according to the depth
* of the node it represents.
*
* @param buffer
* The string buffer to add leading spaces to.
* @param depth
* The depth of the node the string buffer represents.
*/
private static void indentStringBuffer(final StringBuffer buffer,
final int depth) {
final int indent = depth * _tab;
for (int i = 0; i < indent; ++i) {
buffer.append(' ');
}
}
}