package org.benow.java.rest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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;
import org.xml.sax.SAXException;
/**
* Select utilities from the benow-xml api
* {link http://benow.ca/projects/XML/}
* @author andy
*
*/
public class XML {
private static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
public static Document loadDocument(
InputStream in) throws IOException {
StringWriter writer = new StringWriter();
InputStreamReader reader = new InputStreamReader(in);
copy(reader, writer);
// http://www.velocityreviews.com/forums/t143346-xml-and-invalid-byte-utf-8-a.html
in = new ByteArrayInputStream(writer.toString().getBytes("UTF-8"));
String xml = writer.toString();
return loadDocument(xml);
}
public static Document loadDocument(
String xml) throws IOException {
try {
factory.setNamespaceAware(true);
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
throw new IOException("Unexpected error: " + e.getMessage());
}
return builder.parse(new ByteArrayInputStream(xml.getBytes("utf8")));
} catch (SAXException sxe) {
System.err.println("Error parsing:\n"+xml);
// Error generated during parsing
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();
throw new IOException("Error parsing: " + x.getMessage());
}
}
private static void copy(
InputStreamReader reader,
StringWriter writer) throws IOException {
char[] buff = new char[512];
int read = reader.read(buff);
while (read > 0) {
writer.write(buff, 0, read);
read = reader.read(buff);
}
}
/**
* Get node value by path name, in limited xpath format. For example
* <ul>
* <li>getByPath(curr,"@attr"); - gets attribute attr value from given node</li>
* <li>getByPath(curr,"/some/elem"); - gets element value for elem element within some element off root</li>
* < li>getByPath(curr,"/some/@attr"); - gets attribute value for attr attribute of some element</li>
* <li>getByPath(curr,"some"); - gets attribute value for some element</li>
* </ul>
* // and other xpath operators are not supported.
*
* @param releaseElem
* @param path
* @return object at path, a string, or node of some sort
*/
public static Object getByPath(
Element releaseElem,
String path) {
while (path.indexOf('/') == 0)
path = path.substring(1, path.length());
if (path.substring(0, 1).equals("@")) {
String attrName = path.substring(1, path.length());
Node attr = _getAttributeNode(releaseElem, attrName);
if (attr != null)
return attr.getNodeValue();
else
return null;
}
Node node = getNodeByPath(releaseElem, path);
if (node == null)
return null;
if (node.getNodeType() == Node.ELEMENT_NODE) { // is element, get text node
if (node.getChildNodes().getLength() == 0)
return null;
if (node.getChildNodes().getLength() == 1)
return node.getFirstChild().getNodeValue();
return node;
} else
// is attribute, get value
return node.getNodeValue();
}
public static Node getNodeByPath(
Node curr,
String path) {
if (path.substring(0, 2).equals("@/")) {
String attrName = path.substring(2, path.length());
return _getAttributeNode(curr, attrName);
}
if (path.substring(0, 1).equals("@")) {
String attrName = path.substring(1, path.length());
return _getAttributeNode(curr, attrName);
}
int pos = path.indexOf("/");
int pos2 = path.indexOf("@");
if (pos == -1 && pos2 == -1)
return getChildElement(curr, path);
String nodeName;
String nextPath;
if (pos2 != -1 && (pos > pos2 || pos == -1)) {
nodeName = path.substring(0, pos2);
nextPath = path.substring(pos2);
} else {
nodeName = path.substring(0, pos);
nextPath = path.substring(pos + 1);
}
Node node = getChildElement(curr, nodeName);
if (node == null) // node not found
return null;
return getNodeByPath(node, nextPath);
}
private static Node getChildElement(
Node node,
String name) {
if (node == null)
return null;
NodeList nodes = node.getChildNodes();
if (nodes != null) {
for (int i = 0; i < nodes.getLength(); i++) {
Node curr = nodes.item(i);
if (curr.getNodeType() == Node.ELEMENT_NODE && curr.getNodeName().equals(name)) {
return curr;
}
}
}
return null;
}
private static Node _getAttributeNode(
Node node,
String name) {
if (node == null || node.getAttributes().getLength() == 0)
return null;
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
Node curr = attrs.item(i);
// alert("curr: name: "+curr.nodeName+" val:"+curr.nodeValue);
if (curr.getNodeName().equals(name)) {
// alert("found: "+name);
return curr;
}
}
return null;
}
public static String elementToString(
Element toShow) {
return elementToString(toShow, "");
}
private static String elementToString(
Element curr,
String indent) {
String result=indent+"<"+curr.getNodeName();
if (curr.hasAttributes()) {
NamedNodeMap nm = curr.getAttributes();
for (int i=0;i<nm.getLength();i++) {
result+=" "+nm.item(i).getNodeName()+"=\""+nm.item(i).getNodeValue()+"\"";
}
}
if (curr.hasChildNodes()) {
NodeList cn = curr.getChildNodes();
if (cn.getLength()==1) {
Node currN=cn.item(0);
if (currN.getNodeType()==Node.TEXT_NODE) {
result += "><![CDATA[" + currN.getNodeValue() + "]]>";
result += "</" + curr.getNodeName() + ">";
return result;
} else if (currN.getNodeType() == Node.CDATA_SECTION_NODE) {
result += "><![CDATA[" + currN.getNodeValue() + "]]>";
result += "</" + curr.getNodeName() + ">";
return result;
}
}
result+=">";
for (int i=0;i<cn.getLength();i++) {
Node currN=cn.item(i);
if (currN.getNodeType() == Node.TEXT_NODE)
result += "<![CDATA[" + currN.getNodeValue() + "]]>";
if (currN.getNodeType()==Node.ELEMENT_NODE)
result += "\n" + elementToString((Element) currN, indent + " ");
if (currN.getNodeType()==Node.COMMENT_NODE)
result+="\n<!--"+currN.getNodeValue()+"-->";
}
result += "\n" + indent + "</" + curr.getNodeName() + ">";
} else {
result+="/>";
}
return result;
}
/**
* Get immediate child elements with the given name
* (whereas getElementsByTagName will get any named elems,
* not just immediate children)
*
* @param element
* @param childElemName
* @return elements with given name
*/
public static List<Element> getChildElements(
Element element,
String childElemName) {
List<Element> result = new ArrayList<Element>();
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node curr = children.item(i);
if (curr.getNodeType() == Node.ELEMENT_NODE) {
if (curr.getNodeName().equals(childElemName))
result.add((Element) curr);
}
}
return result;
}
public static void saveDocument(
Document doc,
File toSaveTo) throws IOException {
FileOutputStream fos = new FileOutputStream(toSaveTo);
try {
String xmlStr = elementToString(doc.getDocumentElement());
fos.write(xmlStr.getBytes());
} finally {
fos.flush();
fos.close();
}
}
public static String documentToString(
Document ownerDocument) {
String enc = "UTF-8";
if (ownerDocument.getXmlEncoding() == null) {
if (ownerDocument.getInputEncoding() != null)
enc = ownerDocument.getInputEncoding();
} else
enc = ownerDocument.getXmlEncoding();
String xml = "<?xml version=\"1.0\" encoding=\"" + enc + "\"?>";
xml += "\n" + elementToString(ownerDocument.getDocumentElement());
return xml;
}
}