/*
XMLTools.java / Frost
Copyright (C) 2003 Frost Project <jtcfrost.sourceforge.net>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package frost.util;
import java.io.*;
import java.util.*;
import thaw.core.Logger;
import javax.xml.parsers.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.OutputKeys;
import org.w3c.dom.*;
import org.xml.sax.*;
/**
* A place to hold utility methods for XML processing.
*/
public class XMLTools {
private static DocumentBuilderFactory validatingFactory = DocumentBuilderFactory.newInstance();
private static DocumentBuilderFactory nonValidatingFactory = DocumentBuilderFactory.newInstance();
{
validatingFactory.setAttribute("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
validatingFactory.setAttribute("http://xml.org/sax/features/external-general-entities",Boolean.FALSE);
validatingFactory.setAttribute("http://xml.org/sax/features/external-parameter-entities",Boolean.FALSE);
validatingFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",Boolean.FALSE);
validatingFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd",Boolean.FALSE);
validatingFactory.setValidating(true);
nonValidatingFactory.setAttribute("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
nonValidatingFactory.setAttribute("http://xml.org/sax/features/external-general-entities",Boolean.FALSE);
nonValidatingFactory.setAttribute("http://xml.org/sax/features/external-parameter-entities",Boolean.FALSE);
nonValidatingFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",Boolean.FALSE);
nonValidatingFactory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd",Boolean.FALSE);
nonValidatingFactory.setValidating(false);
}
/**
* creates a document containing a single element - the one
* returned by getXMLElement of the argument
* @param element the object that will be contained by the document
* @return the document
*/
public static Document getXMLDocument(XMLizable element) {
Document doc = createDomDocument();
doc.appendChild(element.getXMLElement(doc));
return doc;
}
/**
* Serializes the XML into a byte array.
*/
public static byte [] getRawXMLDocument (XMLizable element) {
Document doc = getXMLDocument(element);
File tmp = getXmlTempFile();
byte [] result=null;
try {
writeXmlFile(doc, tmp.getPath());
result = FileAccess.readByteArray(tmp);
} catch (Throwable t) {
Logger.notice(t, "Exception thrown in getRawXMLDocument(XMLizable element): "+ t.toString());
}
tmp.delete();
return result;
}
/**
* Returns the parsed Document for the given xml content.
* @param content xml data
* @return xml document
*/
public static Document parseXmlContent(byte[] content, boolean validating) {
Document result = null;
File tmp = getXmlTempFile();
try {
FileAccess.writeFile(content, tmp);
result = XMLTools.parseXmlFile(tmp, validating);
} catch(Throwable t) {
Logger.notice(t, "Exception thrown in parseXmlContent: "+ t.toString());
}
tmp.delete();
return result;
}
/**
* Parses an XML file and returns a DOM document.
* If validating is true, the contents is validated against the DTD
* specified in the file.
*/
public static Document parseXmlFile(String filename, boolean validating)
throws IllegalArgumentException
{
return parseXmlFile(new File(filename), validating);
}
/**
* Parses an XML file and returns a DOM document.
* If validating is true, the contents is validated against the DTD
* specified in the file.
*/
public static Document parseXmlFile(File file, boolean validating)
throws IllegalArgumentException {
try {
DocumentBuilder builder;
if (validating) {
synchronized (validatingFactory) {
builder = validatingFactory.newDocumentBuilder();
}
} else {
synchronized (nonValidatingFactory) {
builder = nonValidatingFactory.newDocumentBuilder();
}
}
return builder.parse(file);
} catch (SAXException e) {
// A parsing error occurred; the xml input is not valid
Logger.notice(e,
"Parsing of xml file failed (send badfile.xml to a dev for analysis) - " +
"File name: '" + file.getName() + "': "+e.toString());
file.renameTo(new File("badfile.xml"));
throw new IllegalArgumentException();
} catch (ParserConfigurationException e) {
Logger.notice(e, "Exception thrown in parseXmlFile(File file, boolean validating) - " +
"File name: '" + file.getName() + "': "+
e.toString());
} catch (IOException e) {
Logger.notice(e,
"Exception thrown in parseXmlFile(File file, boolean validating) - " +
"File name: '" + file.getName() + "': "+e);
}
return null;
}
/**
* This method writes a DOM document to a file.
*/
public static boolean writeXmlFile(Document doc, String filename) {
return writeXmlFile(doc, new File(filename));
}
/**
* This method writes a DOM document to a file.
*/
public static boolean writeXmlFile(Document doc, File file) {
/* This method didn't work for me, so I replaced it by mine */
try {
FileOutputStream out = new FileOutputStream(file);
StreamResult streamResult;
streamResult = new StreamResult(out);
/* Serialization */
final DOMSource domSource = new DOMSource(doc);
final TransformerFactory transformFactory = TransformerFactory.newInstance();
Transformer serializer;
serializer = transformFactory.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.transform(domSource, streamResult);
return true;
} catch(final javax.xml.transform.TransformerException e) {
Logger.notice(e, "Unable to generate XML because: "+e.toString());
e.printStackTrace();
} catch(java.io.FileNotFoundException e) {
Logger.notice(e, "File not found exception ?!");
}
return false;
}
/**
* This method creates a new DOM document.
*/
public static Document createDomDocument() {
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.newDocument();
return doc;
} catch (ParserConfigurationException e) {
Logger.notice(e, "Exception thrown in createDomDocument(): "+e.toString());
}
return null;
}
/**
* gets a true or false attribute from an element
*/
public static boolean getBoolValueFromAttribute(Element el, String attr, boolean defaultVal) {
String res = el.getAttribute(attr);
if( res == null ) {
return defaultVal;
}
if( res.toLowerCase().equals("true") == true ) {
return true;
} else {
return false;
}
}
/**
* Returns a list containing all Elements of this parent with given tag name.
*/
public static List getChildElementsByTagName(Element parent, String name) {
LinkedList newList = new LinkedList();
NodeList childs = parent.getChildNodes();
for( int x=0; x<childs.getLength(); x++ ) {
Node child = childs.item(x);
if( child.getNodeType() == Node.ELEMENT_NODE ) {
Element ele = (Element)child;
if( ele.getTagName().equals( name ) == true ) {
newList.add( ele );
}
}
}
return newList;
}
/**
* Gets the Element by name from parent and extracts the Text child node.
* E.g.:
* <parent>
* <child>
* text
*/
public static String getChildElementsTextValue( Element parent, String childname ) {
List nodes = getChildElementsByTagName( parent, childname );
if( nodes.size() == 0 ) {
return null;
}
Text txtname = (Text) (((Node)nodes.get(0)).getFirstChild());
if( txtname == null ) {
return null;
}
return txtname.getData();
}
/**
* Gets the Element by name from parent and extracts the CDATASection child node.
*/
public static String getChildElementsCDATAValue( Element parent, String childname ) {
List nodes = getChildElementsByTagName( parent, childname );
if( nodes.size() == 0 ) {
return null;
}
CDATASection txtname = (CDATASection) ((Node)nodes.get(0)).getFirstChild();
if( txtname == null ) {
return null;
}
// if the text contained control characters then it was maybe splitted into multiple CDATA sections.
if( txtname.getNextSibling() == null ) {
return txtname.getData();
}
StringBuffer sb = new StringBuffer(txtname.getData());
while( txtname.getNextSibling() != null ) {
txtname = (CDATASection)txtname.getNextSibling();
sb.append(txtname.getData());
}
return sb.toString();
}
/**
* create a proper temp file (deleted on VM emergency exit).
*/
private static File getXmlTempFile() {
File tmp = FileAccess.createTempFile("xmltools_", ".tmp");
tmp.deleteOnExit();
return tmp;
}
// public static void main(String[] args) {
//
// Document d = createDomDocument();
// Element el = d.createElement("FrostMessage");
//
// CDATASection cdata;
// Element current;
//
// current = d.createElement("MessageId");
// cdata = d.createCDATASection("<![CDATA[\\</MessageId>]]> <helpme />");
// current.appendChild(cdata);
//
// el.appendChild(current);
//
// d.appendChild(el);
//
// boolean ok = writeXmlFile(d, "d:\\AAAAA.xml");
// System.out.println("ok="+ok);
//
// Document dd = parseXmlFile("d:\\AAAAA.xml", false);
// Element root = dd.getDocumentElement();
// String s = XMLTools.getChildElementsCDATAValue(root, "MessageId");
// System.out.println("s="+s);
// }
}