/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.util.xml;
import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import net.java.sip.communicator.util.*;
/**
* Common XML Tasks
*
* @author Emil Ivov
* @author Damian Minkov
*/
public class XMLUtils
{
private static Logger logger = Logger.getLogger(XMLUtils.class);
/**
* Extracts from node the attribute with the specified name.
* @param node the node whose attribute we'd like to extract.
* @param name the name of the attribute to extract.
* @return a String containing the trimmed value of the attribute or null
* if no such attribute exists
*/
public static String getAttribute(Node node, String name)
{
if (node == null)
return null;
Node attribute = node.getAttributes().getNamedItem(name);
return (attribute == null)
? null
: attribute.getNodeValue().trim();
}
/**
* Extracts the String content of a TXT element.
*
* @param parentNode the node containing the data that we'd like to get.
* @return the string contained by the node or null if none existed.
*/
public static String getText(Element parentNode)
{
Text text = getTextNode(parentNode);
if (text == null)
{
return null;
}
else
{
return text.getData();
}
}
/**
* Sets data to be the TEXT content of element
*
* @param parentNode the parent element.
* @param data the data to set.
*/
public static void setText(Element parentNode, String data)
{
if(data == null)
return;
Text txt = getTextNode(parentNode);
if (txt != null)
txt.setData(data);
else
{
txt = parentNode.getOwnerDocument().createTextNode(data);
parentNode.appendChild(txt);
}
}
/**
* Sets data to be the CDATA content of element
*
* @param element the parent element.
* @param data the data to set.
*/
public static void setCData(Element element, String data)
{
if(data == null)
return;
CDATASection txt = getCDataNode(element);
if (txt != null)
txt.setData(data);
else
{
txt = element.getOwnerDocument().createCDATASection(data);
element.appendChild(txt);
}
}
/**
* Extract the CDATA content of the specified element.
* @param element the element whose data we need
* @return a String containing the CDATA value of element.
*/
public static String getCData(Element element)
{
CDATASection text = getCDataNode(element);
if (text != null)
return text.getData().trim();
else
return null;
}
/**
* Returns element's CDATA child node (if it has one).
* @param element the element whose CDATA we need to get.
* @return a CDATASection object containing the specified element's CDATA
* content
*/
public static CDATASection getCDataNode(Element element)
{
return (CDATASection)getChildByType(element,
Node.CDATA_SECTION_NODE);
}
/**
* Returns element's TEXT child node (if it has one).
* @param element the element whose TEXT we need to get.
* @return a <tt>Text</tt> object containing the specified element's
* text content.
*/
public static Text getTextNode(Element element)
{
return (Text)getChildByType(element, Node.TEXT_NODE);
}
/**
* Returns first of the <tt>element</tt>'s child nodes that is of type
* <tt>nodeType</tt>.
* @param element the element whose child we need.
* @param nodeType the type of the child we need.
* @return a child of the specified <tt>nodeType</tt> or null if none
* was found.
*/
public static Node getChildByType(Element element, short nodeType)
{
if (element == null)
return null;
NodeList nodes = element.getChildNodes();
if (nodes == null || nodes.getLength() < 1)
return null;
Node node;
String data;
for (int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
short type = node.getNodeType();
if (type == nodeType)
{
if (type == Node.TEXT_NODE ||
type == Node.CDATA_SECTION_NODE)
{
data = ( (Text) node).getData();
if (data == null || data.trim().length() < 1)
continue;
}
return node;
}
}
return null;
}
/**
* Writes the specified document to the given file adding indentatation.
* The default encoding is UTF-8.
*
* @param out the output File
* @param document the document to write
*
* @throws java.io.IOException in case a TransformerException is thrown by
* the underlying Transformer.
*/
public static void writeXML(Document document, File out)
throws java.io.IOException
{
// indentedWriteXML(document, new FileOutputStream(out));
writeXML(document
, new StreamResult(
new OutputStreamWriter(
new FileOutputStream(out), "UTF-8"))
, null
, null);
}
/**
* Writes the specified document to the given file adding indentatation.
* The default encoding is UTF-8.
*
* @param writer the writer to use when writing the File
* @param document the document to write
*
* @throws java.io.IOException in case a TransformerException is thrown by
* the underlying Transformer.
*/
public static void writeXML(Document document,
Writer writer)
throws java.io.IOException
{
writeXML(document, new StreamResult(writer), null, null);
}
/**
* Writes the specified document to the given file adding indentatation.
* The default encoding is UTF-8.
*
* @param streamResult the streamResult object where the document should be
* written
* @param document the document to write
* @param doctypeSystem the doctype system of the xml document that we should
* record in the file or null if none is specified.
* @param doctypePublic the public identifier to be used in the document
* type declaration.
*
* @throws java.io.IOException in case a TransformerException is thrown by
* the underlying Transformer.
*/
public static void writeXML(Document document,
StreamResult streamResult,
String doctypeSystem,
String doctypePublic)
throws java.io.IOException
{
try
{
DOMSource domSource = new DOMSource(document);
TransformerFactory tf = TransformerFactory.newInstance();
// not working for jdk 1.4
try
{
tf.setAttribute("indent-number", new Integer(4));
}catch(Exception e){}
Transformer serializer = tf.newTransformer();
if(doctypeSystem != null)
serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,
doctypeSystem);
if(doctypePublic != null)
serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
doctypePublic);
// not working for jdk 1.5
serializer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "4");
serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.transform(domSource, streamResult);
}
catch (TransformerException ex) {
logger.error("Error saving configuration file", ex);
throw new java.io.IOException(
"Failed to write the configuration file: "
+ ex.getMessageAndLocation());
}
catch (IllegalArgumentException ex) {
//this one is thrown by the setOutputProperty or in other words -
//shoudln't happen. so let's just log it down in case ...
logger.error("Error saving configuration file", ex);
}
}
/**
* A simple implementation of XML writing that also allows for indentation.
* @param doc the Document that we will be writing.
* @param out an OutputStream to write the document through.
*/
public static void indentedWriteXML(Document doc, OutputStream out)
{
if (out != null)
{
try
{
// Writer wri = new OutputStreamWriter(out, "UTF-8");
// wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"+lSep);
// (new DOMElementWriter()).write(rootElement, wri, 0, " ");
// wri.flush();
// wri.close();
writeXML(doc
, new StreamResult(
out)
, null
, null);
out.close();
}
catch (IOException exc)
{
throw new RuntimeException("Unable to write xml", exc);
}
}
}
/**
* Whenever you'd need to print a configuration node and/or its children.
*
* @param root the root node to print.
* @param out the print stream that should be used to outpu
* @param recurse boolean
* @param prefix String
*/
public static void printChildElements(Element root,
PrintStream out,
boolean recurse,
String prefix)
{
out.print(prefix + "<" + root.getNodeName());
NamedNodeMap attrs = root.getAttributes();
Node node;
for(int i = 0; i < attrs.getLength(); i++)
{
node = attrs.item(i);
out.print(" " + node.getNodeName() + "=\""
+ node.getNodeValue() + "\"");
}
out.println(">");
String data = getText(root);
if(data != null && data.trim().length() > 0)
out.println(prefix + "\t" + data);
data = getCData(root);
if(data != null && data.trim().length() > 0)
out.println(prefix + "\t<![CDATA[" + data + "]]>");
NodeList nodes = root.getChildNodes();
for(int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE)
{
if(recurse)
printChildElements((Element)node, out, recurse, prefix
+ "\t");
else
out.println(prefix + node.getNodeName());
}
}
out.println(prefix + "</" + root.getNodeName() + ">");
}
/**
* Returns the child element with the specified tagName for the specified
* parent element.
* @param parent The parent whose child we're looking for.
* @param tagName the name of the child to find
* @return The child with the specified name or null if no such child was
* found.
* @throws NullPointerException if parent or tagName are null
*/
public static Element findChild(Element parent, String tagName)
{
if(parent == null || tagName == null)
throw new NullPointerException("Parent or tagname were null! "
+ "parent = " + parent + "; tagName = " + tagName);
NodeList nodes = parent.getChildNodes();
Node node;
int len = nodes.getLength();
for(int i = 0; i < len; i++)
{
node = nodes.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE
&& ((Element)node).getNodeName().equals(tagName))
return (Element)node;
}
return null;
}
/**
* Returns the children elements with the specified tagName for the
* specified parent element.
*
* @param parent The parent whose children we're looking for.
* @param tagName the name of the child to find
* @return List of the children with the specified name
* @throws NullPointerException if parent or tagName are null
*/
public static List<Element> findChildren(Element parent, String tagName)
{
if (parent == null || tagName == null)
throw new NullPointerException("Parent or tagname were null! "
+ "parent = " + parent + "; tagName = " + tagName);
List<Element> result = new ArrayList<Element>();
NodeList nodes = parent.getChildNodes();
Node node;
int len = nodes.getLength();
for (int i = 0; i < len; i++)
{
node = nodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE)
{
Element element = (Element) node;
if (element.getNodeName().equals(tagName))
result.add(element);
}
}
return result;
}
/**
* Looks through all child elements of the specified root (recursively)
* and returns the first element that corresponds to all parameters.
*
* @param root the Element where the search should begin
* @param tagName the name of the node we're looking for
* @param keyAttributeName the name of an attribute that the node has to
* have
* @param keyAttributeValue the value that attribute must have
* @return the Element in the tree under root that matches the specified
* paameters.
* @throws NullPointerException if any of the arguments is null.
*/
public static Element locateElement(Element root,
String tagName,
String keyAttributeName,
String keyAttributeValue)
{
NodeList nodes = root.getChildNodes();
Node node;
int len = nodes.getLength();
for(int i = 0; i < len; i++)
{
node = nodes.item(i);
if(node.getNodeType() != Node.ELEMENT_NODE)
continue;
// is this the node we're looking for?
if(node.getNodeName().equals(tagName))
{
String attr = ((Element)node).getAttribute(keyAttributeName);
if( attr!= null
&& attr.equals(keyAttributeValue))
return (Element) node;
}
//look inside.
Element child = locateElement( (Element) node, tagName
, keyAttributeName, keyAttributeValue);
if (child != null)
return child;
}
return null;
}
/**
* Looks through all child elements of the specified root (recursively) and
* returns the elements that corresponds to all parameters.
*
* @param root the Element where the search should begin
* @param tagName the name of the node we're looking for
* @param keyAttributeName the name of an attribute that the node has to
* have
* @param keyAttributeValue the value that attribute must have
* @return list of Elements in the tree under root that match the specified
* parameters.
* @throws NullPointerException if any of the arguments is null.
*/
public static List<Element> locateElements(Element root, String tagName,
String keyAttributeName, String keyAttributeValue)
{
List<Element> result = new ArrayList<Element>();
NodeList nodes = root.getChildNodes();
Node node;
int len = nodes.getLength();
for (int i = 0; i < len; i++)
{
node = nodes.item(i);
if (node.getNodeType() != Node.ELEMENT_NODE)
continue;
// is this the node we're looking for?
if (node.getNodeName().equals(tagName))
{
Element element = (Element) node;
String attr = element.getAttribute(keyAttributeName);
if (attr != null && attr.equals(keyAttributeValue))
result.add(element);
}
// look inside.
List<Element> childs =
locateElements((Element) node, tagName, keyAttributeName,
keyAttributeValue);
if (childs != null)
result.addAll(childs);
}
return result;
}
}