package org.alleypress.util.xml;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
import java.io.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
/**
*
* XMLUtil was born from the need to print org.w3c.dom.Document objects and has
* expanded to include automatic conversions from a java.sql.ResultSet to a
* Document object. It will also parse a String and return a Document object.
* For bugs or additions, send me an email.at croftontom@yahoo.com
*
*
* Typical use can be as follows:
*
* ResultSet rs=con.createStatement.executeQuery("select name,address,phone from users");
* Document d=XMLUtil.resultSetToDoc(rs);
* //show what we got
* System.out.println(XMLUtil.docToString(d));
* //do some XSL processing
*
* @author Tom Crofton
* @version 1.9
* @email croftontom@yahoo.com
* @created Nov 15, 2002
* @last modified March 1,2006
*/
public class XMLUtil {
/*
* defaulting to namespace aware doc builder
*/
protected static DocumentBuilder docBuilder=null;
static {
try {
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
docBuilder = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
/**
* Mostly for internal use, it returns a string of tabs used for indenting
* tag text.
*
* @param howMany The number of tabs you want in your string
* @return A string of the number of tab characters you requested
*/
public static String getTabString(int howMany) {
StringBuffer sb = new StringBuffer();
for (int i=0;i0);
if (textNode || cData) {
if (textValued) {
s.append(tabs);
if (cData) {
s.append("");
} else {
s.append(tagValue.trim());
}
s.append("\n");
}
} else {
s.append(tabs+"<"+tagName);
NamedNodeMap atts = n.getAttributes();
if (atts!=null && atts.getLength()>0) {
for (int k=0;k\n");
return s.toString();
} else if (n.getChildNodes().getLength()==1 &&
n.getChildNodes().item(0).getNodeName().equals("#text") &&
n.getChildNodes().item(0).getNodeValue().trim().length()<35) {
//has only a short text node
s.append(">");
s.append(n.getChildNodes().item(0).getNodeValue().trim());
s.append(""+tagName+">\n");
return s.toString();
} else {
s.append(">\n");
}
}
//recursion
NodeList nl=n.getChildNodes();
int len=nl.getLength();
for (int i=0;i\n");
}
return s.toString();
}
/**
* This formats a Node with the first nodes being flush left
*
* @param n the node to be formatted to a string
* @return Correctly formatted node as a String
*/
public static String nodeToString(Node n) {
return nodeToString(n,0);
}
/**
* Returns a full format of the Document object as a string including
* the xml header and any DOCTYPE info that may exist
*
* @param doc Document object
* @return Correctly formatted Document as a String
*/
public static String docToString(Document doc) {
//Header
StringBuffer result = new StringBuffer("\n");
//DocType
DocumentType dt = doc.getDoctype();
if (dt!=null) {
result.append("\n");
}
//Main xml
result.append(nodeToString(doc.getLastChild()));
return result.toString();
}
/**
* Takes a String that should be a corrrectly formed XML document and
* parses it into a Document object. I'm a little worried about thread
* safety here, it needs some investigation.
*
* @param xmlString A properly formed xml document
* @return org.w3c.dom.Document object
* @throws IOException
* @throws SAXException
*/
public static Document stringToDoc(String xmlString) throws IOException,SAXException {
return docBuilder.parse(new ByteArrayInputStream(xmlString.getBytes()));
}
/**
* Creates a new Document to use as a default paramerer when calling
* addResultSetToDoc(Document doc, ResultSet rs, String nodeName) with the given root node name and ResultSet
*
* @param rs The unread ResultSet to be parsed into a Document
* @param rootNodeName The name you want the root node of the document to have
* @return a Document object containing the entire contents returned by the ResultSet
* @throws SQLException
*
*/
public static Document resultSetToDoc(ResultSet rs, String rootNodeName) throws SQLException {
Document doc = docBuilder.newDocument();
addResultSetToDoc(doc,rs,rootNodeName);
return doc;
}
/**
* This calls resultSetToDoc(ResultSet rs, String rootNodeName) with a default
* root node name of ResultSet. see resultSetToDoc(ResultSet rs, String rootNodeName)
*
* @param rs The unread ResultSet to be parsed into a Document
* @return a Document object containing the entire contents returned by the ResultSet
* @throws SQLException
*/
public static Document resultSetToDoc(ResultSet rs) throws SQLException {
return resultSetToDoc(rs,"ResultSet");
}
/**
* Takes an unread ResultSet and appends to a Document object. The document will
* have a root node named from the parameter rootNodeName and child nodes
* each named row. Each row will have one tag for each column
* of the ResultSet named the same as the column name and containing a text
* representation of the column value. If the root node does not exist, it will
* be created. If the root node does exist each row will be appended after
* the last existing row child.
*
* At a later date I may actually add an attribute called type that would be set
* to the actual column datatype, but there hasn't been any advantage to doing
* so yet.
*
* @param doc The XML Document object that the to which the ResultSet will be appended
* @param rs The unread ResultSet to be parsed into a Document
* @param nodeName The name you want the root node of the document to have
* @return a Document object containing the entire contents returned by the ResultSet
* @throws SQLException
*/
public static void addResultSetToDoc(Document doc, ResultSet rs, String nodeName) throws SQLException {
Element node=doc.createElement(nodeName);
if (doc.getFirstChild()==null) {
doc.appendChild(node);
} else {
doc.getFirstChild().appendChild(node);
}
if (rs!=null) {
int colCount = rs.getMetaData().getColumnCount();
String[] colNames=new String[colCount];
for (int i=0;inodeName
*
* @param doc The Document for which the node will be created, for some reason all nodes are relative to a document
* @param map The Map that will be converted to the node
* @param nodeName the name you want for the main tag that will enclose all the tags generated from the Map object
* @return a Node for the given document named from nodeName and containing the name/value pairs of the map as tag/values
*/
public static Node mapToNode(Document doc,Map map,String nodeName) {
Element resultNode=doc.createElement(nodeName);
Iterator keyIterator = map.keySet().iterator();
while (keyIterator.hasNext()) {
String tagName=keyIterator.next().toString();
Element tempNode = doc.createElement(tagName);
if (map.get(tagName)!=null){
Text tx = doc.createTextNode(map.get(tagName).toString());
tempNode.appendChild(tx);
}
resultNode.appendChild(tempNode);
}
return resultNode;
}
/**
* Creates a Properties object from a node containing name and value pairs. Tries to auto-detect
* the tag names based on the structure. The given node is expected to contain one or more nodes
* with name and value tags. The first tag will be the name and the second will be the value.
* Use the following code as an example:
*
* Document d=XMLUtil.stringToDoc("<props><prp><name>name1</name><value>value1</value></prp><prp><name>second</name><value>value2</value></prp></props>");
* Properties p= XMLUtil.nodeToProperties( XPathAPI.eval(d,"/props").nodelist().item(0));
* System.out.println(p);
*
*
*/
public static Properties nodeToProperties(Node node) throws TransformerException {
if (node==null) return null;
Properties props=new Properties();
//how many props are there?
int pCount=(int)XPathAPI.eval(node,"count(./*)").num();
for (int i=0;i