package citation.xml.reader;
import java.io.InputStream;
import java.util.Hashtable;
import net.rim.device.api.collection.util.SparseList;
import net.rim.device.api.util.StringUtilities;
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.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* The main xml parser for the application forms. Displays the results of parsing the XML file.
*
* This class assumes a xml format in the following form
* <CitationUI> -- top level node must match the name
* <Page_X> -- 2nd level indicates the form page number, where X is number
* <Title> -- 3rd level must always start with a form Title
* <ui_element> -- 3rd level indicates a specific form element to render (<Edit>, <Choice>, <Radio>, <Label>)
* see XML2UI.java for list of valid <ui_element> and attributes
* TODO: add version number attribute to <CitationUI>
*/
public class XMLDocParser {
private static String FORM_NAME = "CitationUI";
private static String PAGE_INDICATOR = "Page_";
private static String TITLE_NAME = "Title";
class XMLFormPage {
public Node pageNode;
public Node titleNode;
public SparseList fieldElements;
public XMLFormPage(Node _pageNode)
{
this.pageNode = _pageNode;
this.fieldElements = new SparseList();
}
}
private Document xmlDoc = null;
private Hashtable xmlFormPageTable;
private String currentPageKey = new String();
private String startPageKey = new String();
/**
* This constructor parses the XML file into a W3C DOM document and makes it's contents available
*
* @param _xmlFileName - pathname and file to be parsed
*/
public XMLDocParser(String _xmlFileName) {
try
{
// Build a document based on the XML file.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream inputStream = getClass().getResourceAsStream( _xmlFileName );
xmlDoc = builder.parse( inputStream );
if (xmlDoc != null)
{
if ( validateFormVersion() )
{
// parse the entire tree and create a node list
xmlFormPageTable = new Hashtable();
parseNodes(xmlDoc.getChildNodes().item(0), 0);
// reset to the start page
currentPageKey = startPageKey;
}
else
{
xmlDoc = null;
}
}
}
catch ( Exception e )
{
System.out.println( e.toString() );
}
}
public boolean isValid() {
return (xmlDoc != null);
}
private boolean validateFormVersion()
{
Node node = xmlDoc.getChildNodes().item(0);
return node.getNodeName().equals(XMLDocParser.FORM_NAME);
}
public int getFormPageCount()
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return 0; }
return xmlFormPageTable.size();
}
public void setFormPage(String _page)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return; }
// ensure _page is a valid page tag
if ( this.xmlFormPageTable.containsKey(_page) )
{
this.currentPageKey = _page;
}
}
public String getFormPage()
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
return this.currentPageKey;
}
public String getTitle()
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
// the text value is stored in the child node of the element
Node titleNode = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).titleNode;
NodeList childNodes = titleNode.getChildNodes();
Node firstChild = childNodes.item(0);
return firstChild.getNodeValue();
}
public int getFieldCount()
{
return ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements.size();
}
public String getFieldType(int idx)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
// make sure request is in range
if (idx >= getFieldCount())
{
return null;
}
// the text value is stored in the child node of the element
SparseList fieldList = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements;
Node fieldNode = (Node)(fieldList.get(idx));
return fieldNode.getNodeName();
}
public String getFieldTextValue(int idx)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
// make sure request is in range
if (idx >= getFieldCount())
{
return null;
}
// the text value is stored in the child node of the element
SparseList fieldList = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements;
Node fieldNode = (Node)(fieldList.get(idx));
NodeList childNodes = fieldNode.getChildNodes();
Node firstChild = childNodes.item(0);
return firstChild.getNodeValue();
}
public String[] getFieldAttrAsStringArray(int idx, String attrText)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
// make sure request is in range
if (idx >= getFieldCount())
{
return null;
}
// the text value is stored in the child node of the element
SparseList fieldList = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements;
Node fieldNode = (Node)(fieldList.get(idx));
NamedNodeMap attrNamedNode = fieldNode.getAttributes();
Node listNode = attrNamedNode.getNamedItem(attrText);
if (listNode == null) return null;
String listText = listNode.getNodeValue();
return StringUtilities.stringToKeywords(listText);
}
public String getFieldAttrAsString(int idx, String attrText)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return null; }
// make sure request is in range
if (idx >= getFieldCount())
{
return null;
}
// the text value is stored in the child node of the element
SparseList fieldList = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements;
Node fieldNode = (Node)(fieldList.get(idx));
NamedNodeMap attrNamedNode = fieldNode.getAttributes();
Node attrNode = attrNamedNode.getNamedItem(attrText);
if (attrNode == null) return null;
return attrNode.getNodeValue();
}
public int getFieldAttrAsNumeric(int idx, String attrText)
{
// guard - ensures we've successfully parsed the xml file
if (!isValid()) { return Integer.MIN_VALUE; }
// make sure request is in range
if (idx >= getFieldCount())
{
return Integer.MIN_VALUE;
}
// the text value is stored in the child node of the element
SparseList fieldList = ((XMLFormPage)xmlFormPageTable.get(currentPageKey)).fieldElements;
Node fieldNode = (Node)(fieldList.get(idx));
NamedNodeMap attrNamedNode = fieldNode.getAttributes();
Node attrNode = attrNamedNode.getNamedItem(attrText);
if (attrNode == null) return Integer.MIN_VALUE;
String nodeValue = attrNode.getNodeValue();
return Integer.parseInt(nodeValue);
}
/**
* parseNodes - parsing the complete XML file and it's descendants. adds elements to UI array
*
* @param node The node to display.
* @param depth The depth of this node in the document tree.
*/
private void parseNodes( Node node, 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 )
{
StringBuffer buffer = new StringBuffer();
NodeList childNodes = node.getChildNodes();
int numChildren = childNodes.getLength();
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 )
{
// debug fun
buffer.append( node.getNodeName() ).append( " = \"" ).append( firstChild.getNodeValue() ).append( '"' );
System.out.println(buffer.toString());
// the real work - if it's a title node, add it - otherwise add to the field element list
if (node.getNodeName().equals(TITLE_NAME))
{
((XMLFormPage)(xmlFormPageTable.get(currentPageKey))).titleNode = node;
}
else
{
((XMLFormPage)(xmlFormPageTable.get(currentPageKey))).fieldElements.add(node);
}
}
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.
// debug fun
buffer.append( node.getNodeName() );
System.out.println(buffer.toString());
// the real work
if ( node.getNodeName().indexOf(PAGE_INDICATOR) == 0)
{
// set the start page as the first page definition encountered
if (startPageKey.length() == 0)
{
startPageKey = node.getNodeName();
}
// new page marker - create a new form page
currentPageKey = node.getNodeName();
xmlFormPageTable.put(currentPageKey, new XMLFormPage(node));
}
// Recursively visit all this node's children.
for ( int i = 0; i < numChildren; ++i )
{
parseNodes( childNodes.item( i ), depth + 1 );
}
}
}
}
}