/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010, Geomatys
*
* This library 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;
* version 2.1 of the License.
*
* This library 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.
*/
package org.geotoolkit.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.geotoolkit.lang.Static;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.nio.IOUtilities;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Utils method for dom parsing.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public final class DomUtilities extends Static {
private DomUtilities(){}
/**
* Convinient method to aquiere a DOM document from an input.
* This is provided as a convinient method, use the default JRE classes so it may
* not be the faster parsing method.
*/
public static Document read(final Object input) throws ParserConfigurationException, SAXException, IOException {
final InputStream stream = toInputStream(input);
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final DocumentBuilder constructeur = factory.newDocumentBuilder();
final Document document = constructeur.parse(stream);
stream.close();
return document;
}
/**
* Convinient method to write a dom.
*
* @param doc : DOM document to write
* @param output : output file, url, uri, outputstream
* @throws TransformerException
*/
public static void write(final Document doc, final Object output) throws TransformerException, FileNotFoundException, IOException{
final Source source = new DOMSource(doc);
final Result result;
if(output instanceof File){
result = new StreamResult((File)output);
}else if(output instanceof Writer){
result = new StreamResult((Writer)output);
}else{
final OutputStream stream = toOutputStream(output);
result = new StreamResult(stream);
}
final TransformerFactory factory = TransformerFactory.newInstance();
final Transformer trs = factory.newTransformer();
trs.setOutputProperty(OutputKeys.INDENT, "yes");
trs.setOutputProperty(OutputKeys.ENCODING, "ISO-8859-1");
trs.transform(source, result);
}
/**
* Search and return the first node with a given tag name.
* This will search recursivly in the node
*
* @param parent : node to explore
* @param tagName : child node name
* @return first element with tagName in parent node or null
*/
public static Element firstElement(final Element parent, final String tagName){
return firstElement(parent, tagName, true);
}
/**
* Search and return the first node with a given tag name.
*
* @param parent : node to explore
* @param tagName : child node name
* @param recursive : search in sub nodes or not
* @return first element with tagName in parent node or null
*/
public static Element firstElement(final Element parent, final String tagName, final boolean recursive){
if(recursive){
final NodeList lst = parent.getElementsByTagName(tagName);
if(lst.getLength() > 0){
return (Element) lst.item(0);
}
}else{
//search only in this node
final NodeList lst = parent.getChildNodes();
for(int i=0,n=lst.getLength();i<n;i++){
final Node child = lst.item(i);
if(child instanceof Element && tagName.equalsIgnoreCase(child.getLocalName())){
return (Element) child;
}
}
}
return null;
}
/**
* Search and return the list node with a given tag name.
*
* @param parent : node to explore
* @param tagName : child node name
* @param recursive : search in sub nodes or not
* @return first element with tagName in parent node or null
*/
public static List<Element> getListElements(final Element parent, final String tagName){
final NodeList lst = parent.getElementsByTagName(tagName);
List<Element> result = new ArrayList<>();
for(int i=0,n=lst.getLength();i<n;i++){
final Node child = lst.item(i);
if(child instanceof Element && tagName.equalsIgnoreCase(child.getNodeName())){
result.add((Element) child);
}
}
return result;
}
/**
* Search and return the list node with a given tag name.
*
* @param parent : node to explore
* @param tagName : child node name
* @param recursive : search in sub nodes or not
* @return first element with tagName in parent node or null
*/
public static List<Element> getListElementsNonRecusive(final Element parent, final String tagName){
final NodeList lst = parent.getChildNodes();
List<Element> result = new ArrayList<>();
for(int i=0,n=lst.getLength();i<n;i++){
final Node child = lst.item(i);
if(child instanceof Element && tagName.equalsIgnoreCase(child.getLocalName())){
result.add((Element) child);
}
}
return result;
}
/**
* Return the first node in the given node children which localName
* matchs the given name. Or null if there are no such node.
*/
public static Node getNodeByLocalName(final Node parent, final String name) {
if (name.equalsIgnoreCase(parent.getLocalName())) return parent;
final NodeList lst = parent.getChildNodes();
for(int i=0,n=lst.getLength();i<n;i++){
final Node child = lst.item(i);
if(name.equalsIgnoreCase(child.getLocalName())){
return child;
}
}
return null;
}
/**
* Search a child node with the given tag name and return it's text value
* converted to the given clazz.
* The convertion in made using the geotoolkit Converters.
*
* @param <T> : wished value class
* @param parent : node to explore
* @param tagName : child node name
* @param clazz : wished value class
* @return T or null if no node with tagname was found or convertion to given class failed.
*/
public static <T> T textValue(final Element parent, final String tagName, final Class<T> clazz) throws UnconvertibleObjectException{
final Element ele = firstElement(parent, tagName, true);
if(ele == null) return null;
final String text = ele.getTextContent();
if(text == null) return null;
return ObjectConverters.convert(text, clazz);
}
/**
* Search a child node with the given tag name and return it's text value
* converted to the given clazz.
* The convertion in made using the geotoolkit Converters.
*
* @param <T> : wished value class
* @param parent : node to explore
* @param tagName : child node name
* @param clazz : wished value class
* @return T or null if no node with tagname was found or convertion to given class failed.
*/
public static <T> T textValue(final Element parent, final String tagName, final Class<T> clazz, final boolean recursive) throws UnconvertibleObjectException{
final Element ele = firstElement(parent, tagName, recursive);
if(ele == null) return null;
final String text = ele.getTextContent();
if(text == null) return null;
return ObjectConverters.convert(text, clazz);
}
/**
* Search a child node with the given tag name and return it's text attribute
* converted to the given clazz.
* The convertion in made using the geotoolkit Converters.
*
* @param <T> : wished value class
* @param parent : node to explore
* @param tagName : child node name
* @param attributeName : child node attibute name
* @param clazz : wished value class
* @return T or null if no node with tagname was found or convertion to given class failed.
*/
public static <T> T textAttributeValue(final Element parent, final String tagName,final String attributeName, final Class<T> clazz) throws UnconvertibleObjectException{
final Element ele = firstElement(parent, tagName, true);
if(ele == null) return null;
final String text = ele.getAttribute(attributeName);
if(text == null) return null;
return ObjectConverters.convert(text, clazz);
}
/**
* Search a child node with the given tag name and return it's text attribute
* converted to the given clazz.
* The convertion in made using the geotoolkit Converters.
*
* @param <T> : wished value class
* @param parent : node to explore
* @param tagName : child node name
* @param attributeName : child node attibute name
* @param clazz : wished value class
* @return T or null if no node with tagname was found or convertion to given class failed.
*/
public static <T> T textAttributeValue(final Element parent, final String tagName,final String attributeName, final Class<T> clazz, boolean recursive) throws UnconvertibleObjectException{
final Element ele = firstElement(parent, tagName, recursive);
if(ele == null) return null;
final String text = ele.getAttribute(attributeName);
if(text == null) return null;
return ObjectConverters.convert(text, clazz);
}
/**
* Same as {@link DomUtilities#textValue(org.w3c.dom.Element, java.lang.String, java.lang.Class) }
* but dont throw any exception.
*/
public static <T> T textValueSafe(final Element parent, final String tagName, final Class<T> clazz) {
try {
return textValue(parent, tagName, clazz);
} catch (UnconvertibleObjectException ex) {
Logging.getLogger("org.geotoolkit.util").log(Level.WARNING, null, ex);
return null;
}
}
/**
* Same as {@link DomUtilities#textValue(org.w3c.dom.Element, java.lang.String, java.lang.Class) }
* but dont throw any exception.
*/
public static <T> T textValueSafe(final Element parent, final String tagName, final Class<T> clazz, final boolean recusive) {
try {
return textValue(parent, tagName, clazz, recusive);
} catch (UnconvertibleObjectException ex) {
Logging.getLogger("org.geotoolkit.util").log(Level.WARNING, null, ex);
return null;
}
}
/**
* Same as {@link DomUtilities#textAttributeValue(org.w3c.dom.Element, java.lang.String, java.lang.String, java.lang.Class) }
* but dont throw any exception.
*/
public static <T> T textAttributeValueSafe(final Element parent, final String tagName, final String attributeName, final Class<T> clazz) {
try {
return textAttributeValue(parent, tagName,attributeName, clazz);
} catch (UnconvertibleObjectException ex) {
Logging.getLogger("org.geotoolkit.util").log(Level.WARNING, null, ex);
return null;
}
}
/**
* Same as {@link DomUtilities#textAttributeValue(org.w3c.dom.Element, java.lang.String, java.lang.String, java.lang.Class, java.lang.boolean) }
* but dont throw any exception.
*/
public static <T> T textAttributeValueSafe(final Element parent, final String tagName, final String attributeName, final Class<T> clazz, final boolean recursive) {
try {
return textAttributeValue(parent, tagName,attributeName, clazz, recursive);
} catch (UnconvertibleObjectException ex) {
Logging.getLogger("org.geotoolkit.util").log(Level.WARNING, null, ex);
return null;
}
}
/**
* Convert an object source to a stream.
*/
private static OutputStream toOutputStream(final Object input) throws FileNotFoundException, IOException{
return IOUtilities.openWrite(input);
}
/**
* Convert an object source to a stream.
*/
private static InputStream toInputStream(final Object input) throws FileNotFoundException, IOException{
//special case when input object is document itelf
if (input instanceof String) {
try {
//try to open it as a path
final URL url = new URL((String) input);
} catch (MalformedURLException ex) {
//consider it's the document itself
return new ByteArrayInputStream(input.toString().getBytes());
}
}
return IOUtilities.open(input);
}
}