Mega Code Archive
Handles DOM processing allowing the reading and writing of hierarchical structures as XML files
//package org.gicentre.utils.io;
import javax.xml.parsers.*; // For Document builder factory.
import org.xml.sax.*; // For SAX Exception handling.
import org.w3c.dom.*; // For document object model (DOM).
import java.io.*; // For file handling.
import java.util.*; // For vector structure.
// ****************************************************************************************
/** Handles DOM processing allowing the reading and writing of hierarchical structures as
* XML files. Uses the Document Object Model (DOM) to store the tree of nodes, therefore
* not suitable for very large structures. For reading very large structures represented as
* XML, use SAX processing instead.
* @author Jo Wood, giCentre, City University London.
* @version 3.1, 18th February, 2011.
*/
// *****************************************************************************************
/* This file is part of giCentre utilities library. gicentre.utils is free software: you can
* redistribute it and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* gicentre.utils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* source code (see COPYING.LESSER included with this source code). If not, see
* http://www.gnu.org/licenses/.
*/
public class DOMProcessor
{
// ----------------------- Object Variables -----------------------
private int indent; // Indent level.
private Document dom; // Document object model.
private PrintWriter out; // Output stream.
private Vector matches; // List of matching elements.
// ------------------------- Constructors -------------------------
/** Creates a new empty DOM ready for processing.
*/
public DOMProcessor()
{
// Do nothing.
}
/** Wraps the given DOM in this processor allowing it to be written
* as an XML file, or appended with new nodes.
* @param dom Document Object Model to use in processor.
*/
public DOMProcessor(Document dom)
{
this.dom = dom;
}
/** Reads and the given XML file and constructs a DOM from it.
* @param fileName Name of XML file to read.
*/
public DOMProcessor(String fileName)
{
readXML(fileName);
}
/** Reads XML from the given input stream and constructs a DOM from it.
* @param inStream Stream from which to read XML.
*/
public DOMProcessor(InputStream inStream)
{
readXML(inStream);
}
// ------------------------- Methods ---------------------------
/** Reports whether we have an empty DOM.
* @return True if DOM is empty.
*/
public boolean isEmpty()
{
if (dom == null)
{
return true;
}
return false;
}
/** Adds a new element to the root of the DOM.
* @param name Name of the new element
* @return New element in the DOM.
*/
public Node addElement(String name)
{
if (dom == null)
{
// Create a DocumentBuilder using the DocumentBuilderFactory.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
indent = 0;
try
{
db = dbf.newDocumentBuilder();
}
catch (ParserConfigurationException e)
{
System.err.println("Problem finding an XML parser:\n"+e);
return null;
}
dom = db.getDOMImplementation().createDocument(null,name,null);
return dom.getDocumentElement();
}
return addElement(name,null,dom.getDocumentElement());
}
/** Adds a new element to the given one within the DOM.
* @param name Name of the new element
* @param existingElement Element onto which the new element should be attached.
* @return New element in the DOM.
*/
public Node addElement(String name, Node existingElement)
{
return addElement(name,null,existingElement);
}
/** Adds a new element to the given one within the DOM.
* @param name Name of the new element
* @param text Text to attach to element or null if none required.
* @param existingElement Element onto which the new element should be attached.
* @return New element in the DOM.
*/
public Node addElement(String name, String text, Node existingElement)
{
// Create the new element node and attach it to existing node.
Node newNode = dom.createElement(name);
existingElement.appendChild(newNode);
// Add text if given.
if (text != null)
{
Node textNode = dom.createTextNode(text);
newNode.appendChild(textNode);
}
return newNode;
}
/** Renames the given element with the given new name.
* @param existingElement Element to rename.
* @param newName New name to give element.
*/
public void renameElement(Node existingElement, String newName)
{
// Create an element with the new name
Node newElement = dom.createElement(newName);
// Copy the attributes to the new element
NamedNodeMap attrs = existingElement.getAttributes();
for (int i=0; igetText(null,node).
* If more than one element containing text exists, multiple text values
* are returned.
* @param node Node from which to start search.
* @return Array of strings associated with all occurrences of
* text in the node or its children. Array will be 0 length if none found.
*/
public String[] getText(Node node)
{
return getText(null,node);
}
/** Searches for a given element and returns text associated with it. If more than one
* element with the given name exists, multiple text values are returned.
* @param elementName Element to search for. If elementName is null, search will be
* for all text contained within the given node.
* @param node Node from which to start search.
* @return Array of strings associated with all occurrences of the given element.
* Array will be 0 length if none found.
*/
public String[] getText(String elementName, Node node)
{
matches = new Vector();
searchText(elementName,node);
// Convert match vector into an array;
String[] matchArray = new String[matches.size()];
int i=0;
for (Node matchedNode : matches)
{
matchArray[i++] = matchedNode.getNodeValue();
}
matches = null;
return matchArray;
}
/** Searches for a given node and returns text associated with
* it. This version does not recurse to the node's children.
* @param node Node to search.
* @return Text associated with the node, or null if none found.
*/
public String getNodeText(Node node)
{
// Look for text in child (text stored in its own node).
NodeList children = node.getChildNodes();
for (int i=0; i();
searchAttributes(attributeName,node);
// Convert match vector into an array;
String[] matchArray = new String[matches.size()];
int i=0;
for (Node matchedNode : matches)
{
matchArray[i++] = matchedNode.getNodeValue();
}
matches = null;
return matchArray;
}
/** Searches the given node for a given attribute and returns the value associated with it.
* This version does not recurse to children of the given node.
* @param attributeName Attribute to search for.
* @param node Node from which to start search.
* @return Value associated with the attribute, or null if not found.
*/
public String getNodeAttribute(String attributeName, Node node)
{
// Only consider document or element nodes.
if ((node.getNodeType() != Node.DOCUMENT_NODE) &&
(node.getNodeType() != Node.ELEMENT_NODE))
{
return null;
}
// Search attributes associated with the node.
NamedNodeMap attributes = node.getAttributes();
for (int i=0; igetNodeElements and
* recursively search for children of returned nodes.
* @param name Element name to search for.
* @param node Node from which to start search.
* @return Array of elements with the given name. Array will be 0 length if none found.
*/
public Node[] getElements(String name, Node node)
{
matches = new Vector();
searchNode(name,node);
// Convert match vector into an array;
Node[] matchArray = new Node[matches.size()];
matches.toArray(matchArray);
matches = null;
return matchArray;
}
/** Returns a DOM element with the given name that is the child of the
* given node. This is a non-recursive method that only looks for immediate
* children. Note that unlike getNodeElements()
this method only
* returns the first matched child of the given node.
* @param name Element name to search for.
* @param node Node from which to examine children.
* @return Child node or null if none found.
*/
public Node getNodeElement(String name, Node node)
{
// Only consider document or element nodes.
if ((node.getNodeType() != Node.DOCUMENT_NODE) &&
(node.getNodeType() != Node.ELEMENT_NODE))
{
return null;
}
NodeList children = node.getChildNodes();
for (int i=0; i matchedChildren = new Vector();
NodeList children = node.getChildNodes();
for (int i=0; i 0)
{
//matches.add(child.getNodeValue());
matches.add(child);
}
}
}
}
if ((node.getNodeType() == Node.DOCUMENT_NODE) ||
(node.getNodeType() == Node.ELEMENT_NODE))
{
// Search child nodes.
NodeList children = node.getChildNodes();
for (int i=0; i");
// Output the document's child nodes.
NodeList children = node.getChildNodes();
for (int i=0; i");
// Output any child nodes that exist.
NodeList children = node.getChildNodes();
for (int i=0; i");
indent--;
break;
}
case Node.ENTITY_REFERENCE_NODE: // Entity reference nodes.
{
indent++;
indent();
out.print("&"+name+";");
indent--;
break;
}
case Node.PROCESSING_INSTRUCTION_NODE: // Processing instruction.
{
indent++;
indent();
out.print(""+name);
if ((value != null) && (value.length() > 0))
{
out.print(" "+value);
}
out.println("?>");
indent--;
break;
}
}
// Finally output closing tags for each element.
if (type == Node.ELEMENT_NODE)
{
out.print(""+node.getNodeName()+">");
indent--;
if (node.getNextSibling() == null)
{
indent(); // Only throw new line if this is the last sibling.
}
}
}
/** Converts a given string into XML-friendly code by replacing
* quotes, triangular brackets etc. with their symbolic equivalent.
* @param text Text to process.
* @return Processed text with XML friendly symbols.
*/
private static String makeFriendly(String text)
{
StringBuffer newText = new StringBuffer();
if (text == null)
{
return null;
}
int numCharacters = text.length();
for (int i=0; i':
{
newText.append(">");
break;
}
case '&':
{
newText.append("&");
break;
}
case '"':
{
newText.append(""");
break;
}
default:
{
newText.append(ch);
}
}
}
return newText.toString();
}
/** Indents output to current tree depth.
*/
private void indent()
{
out.println("");
for (int i=1; i