/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de 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; either version 2.1 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53115 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.framework.xml; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.deegree.datatypes.QualifiedName; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.StringTools; import org.deegree.ogcbase.CommonNamespaces; import org.jaxen.JaxenException; import org.jaxen.XPath; import org.jaxen.dom.DOMXPath; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; 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.w3c.dom.Text; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * XML Tools based on JAXP 1.1 for parsing documents and retrieving node values/node attributes. * Furthermore this utility class provides node retrieval based on XPath expressions. * * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a> * @version $Revision: 1.46 $ */ public final class XMLTools { private static final ILogger LOG = LoggerFactory.getLogger( XMLTools.class ); // hidden constructor to prevent instantiation private XMLTools() { } // ------------------------------------------------------------------------ // XPath based parsing methods // ------------------------------------------------------------------------ public static Node getNode( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { Node node = null; try { XPath xpath = new DOMXPath( xPathQuery ); xpath.setNamespaceContext( nsContext ); node = (Node) xpath.selectSingleNode( contextNode ); if ( xPathQuery.endsWith( "text()" ) ) { List nl = xpath.selectNodes( contextNode ); int pos = xPathQuery.lastIndexOf( "/" ); if ( pos > 0 ) { xPathQuery = xPathQuery.substring( 0, pos ); } else { xPathQuery = "."; } xpath = new DOMXPath( xPathQuery ); xpath.setNamespaceContext( nsContext ); List nl_ = xpath.selectNodes( contextNode ); List tmp = new ArrayList( nl_.size() ); for (int i = 0; i < nl_.size(); i++) { tmp.add( getStringValue( (Node)nl_.get( i ) ) ); } for (int i = 0; i < nl.size(); i++) { try { ((Node)nl.get(i)).getParentNode().removeChild( (Node)nl.get(i) ); } catch (Exception e) {} } Document doc = contextNode.getOwnerDocument(); for (int i = 0; i < tmp.size(); i++) { Text text = doc.createTextNode( (String)tmp.get( i ) ); ((Node)nl_.get( i )).appendChild( text ); node = text; } } } catch (JaxenException e) { throw new XMLParsingException( "Error evaluating XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "': " + e.getMessage() ); } return node; } public static String getNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext, String defaultValue ) throws XMLParsingException { String value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { value = getStringValue( node ); } return value; } public static boolean getNodeAsBoolean( Node contextNode, String xPathQuery, NamespaceContext nsContext, boolean defaultValue ) throws XMLParsingException { boolean value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { String stringValue = getStringValue( node ); if ( "true".equals( stringValue ) || "yes".equals( stringValue ) || "1".equals( stringValue ) ) { value = true; } else if ( "false".equals( stringValue ) || "no".equals( stringValue ) || "0".equals( stringValue ) ) { value = false; } else { throw new XMLParsingException( "XPath-expression '" + xPathQuery + " ' from context node '" + contextNode.getNodeName() + "' has an invalid value ('" + stringValue + "'). Valid values are: 'true', 'yes', '1' " + "'false', 'no' and '0'." ); } } return value; } public static int getNodeAsInt( Node contextNode, String xPathQuery, NamespaceContext nsContext, int defaultValue ) throws XMLParsingException { int value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { String stringValue = getStringValue( node ); try { value = Integer.parseInt( stringValue ); } catch (NumberFormatException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid integer value." ); } } return value; } public static double getNodeAsDouble( Node contextNode, String xPathQuery, NamespaceContext nsContext, double defaultValue ) throws XMLParsingException { double value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { String stringValue = getStringValue( node ); try { value = Double.parseDouble( stringValue ); } catch (NumberFormatException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid double value." ); } } return value; } public static URI getNodeAsURI( Node contextNode, String xPathQuery, NamespaceContext nsContext, URI defaultValue ) throws XMLParsingException { URI value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { String stringValue = getStringValue( node ); try { value = new URI( stringValue ); } catch (URISyntaxException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid URI." ); } } return value; } public static QualifiedName getNodeAsQualifiedName( Node contextNode, String xPathQuery, NamespaceContext nsContext, QualifiedName defaultValue ) throws XMLParsingException { QualifiedName value = defaultValue; Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node != null ) { value = getQualifiedNameValue( node ); } return value; } /** * returns a list of nodes mathing the passed XPath * @param contextNode * @param xPathQuery * @param nsContext * @return * @throws XMLParsingException */ public static List getNodes( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { List nl = null; try { XPath xpath = new DOMXPath( xPathQuery ); xpath.setNamespaceContext( nsContext ); nl = xpath.selectNodes( contextNode ); if ( xPathQuery.endsWith( "text()" ) ) { int pos = xPathQuery.lastIndexOf( "/" ); if ( pos > 0 ) { xPathQuery = xPathQuery.substring( 0, pos ); } else { xPathQuery = "."; } xpath = new DOMXPath( xPathQuery ); xpath.setNamespaceContext( nsContext ); List nl_ = xpath.selectNodes( contextNode ); List tmp = new ArrayList( nl_.size() ); for (int i = 0; i < nl_.size(); i++) { tmp.add( getStringValue( (Node)nl_.get( i ) ) ); } for (int i = 0; i < nl.size(); i++) { try { ((Node)nl.get(i)).getParentNode().removeChild( (Node)nl.get(i) ); } catch (Exception e) {} } nl.clear(); Document doc = contextNode.getOwnerDocument(); for (int i = 0; i < tmp.size(); i++) { Text text = doc.createTextNode( (String)tmp.get( i ) ); ((Node)nl_.get( i )).appendChild( text ); nl.add( text ); } } } catch (JaxenException e) { throw new XMLParsingException( "Error evaluating XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "': " + e.getMessage(), e ); } return nl; } public static String[] getNodesAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { String[] values = null; List nl = getNodes( contextNode, xPathQuery, nsContext ); if ( nl != null ) { values = new String[nl.size()]; for (int i = 0; i < nl.size(); i++) { values[i] = getStringValue( (Node) nl.get( i ) ); } } else { values = new String[0]; } return values; } public static URI[] getNodesAsURIs( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { String[] values = getNodesAsStrings( contextNode, xPathQuery, nsContext ); URI[] uris = new URI[values.length]; for (int i = 0; i < uris.length; i++) { try { uris[i] = new URI( values[i] ); } catch (URISyntaxException e) { throw new XMLParsingException( "Result '" + values[i] + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid URI." ); } } return uris; } public static QualifiedName[] getNodesAsQualifiedNames( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { QualifiedName[] values = null; List nl = getNodes( contextNode, xPathQuery, nsContext ); if ( nl != null ) { values = new QualifiedName[nl.size()]; for (int i = 0; i < nl.size(); i++) { values[i] = getQualifiedNameValue( (Node) nl.get( i ) ); } } else { values = new QualifiedName[0]; } return values; } public static Node getRequiredNode( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { Node node = getNode( contextNode, xPathQuery, nsContext ); if ( node == null ) { throw new XMLParsingException( "XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' yields no result!" ); } return node; } public static String getRequiredNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { Node node = getRequiredNode( contextNode, xPathQuery, nsContext ); return getStringValue( node ); } public static String getRequiredNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext, String[] validValues ) throws XMLParsingException { String value = getRequiredNodeAsString( contextNode, xPathQuery, nsContext ); boolean found = false; for (int i = 0; i < validValues.length; i++) { if ( value.equals( validValues[i] ) ) { found = true; break; } } if ( !found ) { StringBuffer sb = new StringBuffer( "XPath-expression '" + xPathQuery + " ' from context node '" + contextNode.getNodeName() + "' has an invalid value. Valid values are: " ); for (int i = 0; i < validValues.length; i++) { sb.append( "'" ).append( validValues[i] ).append( "'" ); if ( i != validValues.length - 1 ) { sb.append( ", " ); } else { sb.append( "." ); } } } return value; } /** * Returns the parts of the targeted node value which are separated by the specified regex. * * @param contextNode * @param xPathQuery * @param nsContext * @param regex * @return * @throws XMLParsingException */ public static String[] getRequiredNodeAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext, String regex ) throws XMLParsingException { Node node = getRequiredNode( contextNode, xPathQuery, nsContext ); return StringTools.toArray( getStringValue( node ), regex, false ); } public static boolean getRequiredNodeAsBoolean( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { boolean value = false; Node node = getRequiredNode( contextNode, xPathQuery, nsContext ); String stringValue = getStringValue( node ); if ( "true".equals( stringValue ) || "yes".equals( stringValue ) ) { value = true; } else if ( "false".equals( stringValue ) || "no".equals( stringValue ) ) { value = false; } else { throw new XMLParsingException( "XPath-expression '" + xPathQuery + " ' from context node '" + contextNode.getNodeName() + "' has an invalid value ('" + stringValue + "'). Valid values are: 'true', 'yes', 'false' and 'no'." ); } return value; } public static int getRequiredNodeAsInt( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { int value = 0; String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext ); try { value = Integer.parseInt( stringValue ); } catch (NumberFormatException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid integer value." ); } return value; } public static double getRequiredNodeAsDouble( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { double value = 0; String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext ); try { value = Double.parseDouble( stringValue ); } catch (NumberFormatException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid double value." ); } return value; } /** * Returns the parts of the targeted node value which are separated by the specified regex. The * string parts are converted to doubles. * * @param contextNode * @param xPathQuery * @param nsContext * @param regex * @return * @throws XMLParsingException */ public static double[] getRequiredNodeAsDoubles( Node contextNode, String xPathQuery, NamespaceContext nsContext, String regex ) throws XMLParsingException { String[] parts = getRequiredNodeAsStrings( contextNode, xPathQuery, nsContext, regex ); double[] doubles = new double[parts.length]; for (int i = 0; i < parts.length; i++) { try { doubles[i] = Double.parseDouble( parts[i] ); } catch (NumberFormatException e) { throw new XMLParsingException( "Value '" + parts[i] + "' does not denote a valid double value." ); } } return doubles; } public static URI getRequiredNodeAsURI( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { URI uri = null; String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext ); try { uri = new URI( stringValue ); } catch (URISyntaxException e) { throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not denote a valid URI." ); } return uri; } public static QualifiedName getRequiredNodeAsQualifiedName( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { Node node = getRequiredNode( contextNode, xPathQuery, nsContext ); return getQualifiedNameValue( node ); } public static List getRequiredNodes( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { List nl = getNodes( contextNode, xPathQuery, nsContext ); if ( nl.size() == 0 ) { throw new XMLParsingException( "XPath-expression: '" + xPathQuery + "' from context node '" + contextNode.getNodeName() + "' does not yield a result." ); } return nl; } /** * Returns the content of the nodes matching the XPathQuery as a String array. At least one node * must match the query otherwise an exception will be thrown. * * @param contextNode * @param xPathQuery * @param nsContext * @return * @throws XMLParsingException */ public static String[] getRequiredNodesAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { List nl = getRequiredNodes( contextNode, xPathQuery, nsContext ); String[] values = new String[nl.size()]; for (int i = 0; i < nl.size(); i++) { values[i] = getStringValue( (Node) nl.get( i ) ); } return values; } public static QualifiedName[] getRequiredNodesAsQualifiedNames( Node contextNode, String xPathQuery, NamespaceContext nsContext ) throws XMLParsingException { List nl = getRequiredNodes( contextNode, xPathQuery, nsContext ); QualifiedName[] values = new QualifiedName[nl.size()]; for (int i = 0; i < nl.size(); i++) { values[i] = getQualifiedNameValue( (Node) nl.get( i ) ); } return values; } public static void checkValue( String value, String[] validValues ) throws XMLParsingException { for (int i = 0; i < validValues.length; i++) { if ( validValues[i].equals( value ) ) { return; } } StringBuffer sb = new StringBuffer( "Value '" ).append( value ).append( "' is invalid. Valid values are: " ); for (int i = 0; i < validValues.length; i++) { sb.append( "'" ).append( validValues[i] ).append( "'" ); if ( i != validValues.length - 1 ) { sb.append( ", " ); } else { sb.append( "." ); } } throw new XMLParsingException( sb.toString() ); } // ------------------------------------------------------------------------ // Node creation methods // ------------------------------------------------------------------------ /** * Creates a new <code>Element</code> node from the given parameters and appends it to the * also specified <code>Element</code>. * * @param element * <code>Element</code> that the new <code>Element</code> is appended to * @param namespaceURI * use null for default namespace * @param name * qualified name * @return the appended <code>Element</code> node */ public static Element appendElement( Element element, URI namespaceURI, String name ) { return appendElement( element, namespaceURI, name, null ); } /** * Appends a namespace binding for the specified element that binds the given prefix to the * given namespace using a special attribute: xmlns:prefix=namespace * * @param element * @param prefix * @param namespace */ public static void appendNSBinding( Element element, String prefix, URI namespace ) { Attr attribute = element.getOwnerDocument().createAttributeNS( CommonNamespaces.XMLNS.toString(), CommonNamespaces.XMLNS_PREFIX + ":" + prefix ); attribute.setNodeValue( namespace.toString() ); element.getAttributes().setNamedItemNS( attribute ); } /** * Appends the given namespace bindings to the specified element. * <p> * NOTE: The prebound prefix "xml" is skipped. * * @param element * @param nsContext */ public static void appendNSBindings( Element element, NamespaceContext nsContext ) { Map namespaceMap = nsContext.getNamespaceMap (); Iterator prefixIter = namespaceMap.keySet().iterator(); while (prefixIter.hasNext ()) { String prefix = (String) prefixIter.next(); if (!CommonNamespaces.XMLNS_PREFIX.equals(prefix)) { URI namespace = (URI) namespaceMap.get (prefix); appendNSBinding(element, prefix, namespace); } } } // ------------------------------------------------------------------------ // String value methods // ------------------------------------------------------------------------ /** * Returns the text contained in the specified element. * * @param node * current element * @return the textual contents of the element */ public static String getStringValue( Node node ) { NodeList children = node.getChildNodes(); StringBuffer sb = new StringBuffer( children.getLength() * 500 ); if ( node.getNodeValue() != null ) { sb.append( node.getNodeValue().trim() ); } if ( node.getNodeType() != Node.ATTRIBUTE_NODE ) { for (int i = 0; i < children.getLength(); i++) { if ( children.item( i ).getNodeType() == Node.TEXT_NODE || children.item( i ).getNodeType() == Node.CDATA_SECTION_NODE ) { sb.append( children.item( i ).getNodeValue() ); } } } return sb.toString(); } /** * Returns the text contained in the specified child element of the given element. * * @param name * name of the child element * @param namespace * namespace of the child element * @param node * current element * @param defaultValue * default value if element is missing * @return the textual contents of the element or the given default value, if missing */ public static String getStringValue( String name, URI namespace, Node node, String defaultValue ) { String value = defaultValue; Element element = getChildElement( name, namespace, node ); if ( element != null ) { value = getStringValue( element ); } if ( value == null || value.equals( "" ) ) { value = defaultValue; } return value; } /** * Returns the text contained in the specified child element of the given element. * * @param name * name of the child element * @param namespace * namespace of the child element * @param node * current element * @return the textual contents of the element or null, if it is missing * @throws XMLParsingException * if the specified child element is missing */ public static String getRequiredStringValue( String name, URI namespace, Node node ) throws XMLParsingException { Element element = getRequiredChildElement( name, namespace, node ); return getStringValue( element ); } /** * Returns the value of the specified node attribute. * * @param name * name of attribute * @param namespace * namespace of attribute * @param node * current element * @return the textual contents of the attribute * @throws XMLParsingException * if specified attribute is missing */ public static String getRequiredAttrValue( String name, URI namespaceURI, Node node ) throws XMLParsingException { String namespace = namespaceURI == null ? null : namespaceURI.toString(); String value = null; NamedNodeMap atts = node.getAttributes(); if ( atts != null ) { Attr attribute = null; if ( namespace == null ) { attribute = (Attr) atts.getNamedItem( name ); } else { attribute = (Attr) atts.getNamedItemNS( namespace, name ); } if ( attribute != null ) { value = attribute.getValue(); } } if ( value == null ) { throw new XMLParsingException( "Required attribute " + name + '(' + namespaceURI + ") of element " + node.getNodeName() + " is missing." ); } return value; } /** * Parses the value of the submitted <code>Node</code> as a <code>QualifiedName</code>. * <p> * To parse the text contents of an <code>Element</code> node, the actual text node must be * given, not the <code>Element</code> node itself. * </p> * * @param node * @return object representation of the element */ public static QualifiedName getQualifiedNameValue( Node node ) throws XMLParsingException { String name = node.getNodeValue().trim(); QualifiedName qName = null; if ( name.indexOf( ':' ) > -1 ) { String[] tmp = StringTools.toArray( name, ":", false ); try { qName = new QualifiedName( tmp[0], tmp[1], XMLTools.getNamespaceForPrefix( tmp[0], node ) ); } catch (URISyntaxException e) { throw new XMLParsingException( e.getMessage(), e ); } } else { qName = new QualifiedName( name ); } return qName; } /** * Returns the namespace URI that is bound to a given prefix at a certain node in the DOM tree. * * @param prefix * @param node * @return namespace URI that is bound to the given prefix, null otherwise * @throws URISyntaxException */ public static URI getNamespaceForPrefix( String prefix, Node node ) throws URISyntaxException { if ( node == null ) { return null; } if ( node.getNodeType() == Node.ELEMENT_NODE ) { NamedNodeMap nnm = node.getAttributes(); if ( nnm != null ) { for (int i = 0; i < nnm.getLength(); i++) { Attr a = (Attr) nnm.item( i ); if ( a.getName().startsWith( "xmlns:" ) && a.getName().endsWith( ':' + prefix ) ) { return new URI( a.getValue() ); } else if (prefix == null && a.getName().equals( "xmlns")) { return new URI( a.getValue() ); } } } } else if ( node.getNodeType() == Node.ATTRIBUTE_NODE ) { return getNamespaceForPrefix( prefix, ( (Attr) node ).getOwnerElement() ); } return getNamespaceForPrefix( prefix, node.getParentNode() ); } // ------------------------------------------------------------------------ // Old code - deprecated // ------------------------------------------------------------------------ /** * Returns the specified child element of the given element. If there is more than one element * with that name, the first one is returned. * * @deprecated * @param name * name of the child element * @param namespace * namespace of the child element * @param node * current element * @return the element or null, if it is missing * @throws XMLParsingException * if the specified child element is missing * @todo refactoring required */ public static Element getRequiredChildElement( String name, URI namespaceURI, Node node ) throws XMLParsingException { String namespace = namespaceURI == null ? null : namespaceURI.toString(); NodeList nl = node.getChildNodes(); Element element = null; Element childElement = null; if ( ( nl != null ) && ( nl.getLength() > 0 ) ) { for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { element = (Element) nl.item( i ); String s = element.getNamespaceURI(); if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) { if ( element.getLocalName().equals( name ) ) { childElement = element; break; } } } } } if ( childElement == null ) { throw new XMLParsingException( "Required child-element " + name + '(' + namespaceURI + ") of element " + node.getNodeName() + " is missing." ); } return childElement; } /** * Returns the specified child element of the given element. If there is more than one with that * name, the first one is returned. * * @deprecated * @param name * name of the child element * @param namespaceURI * namespace of the child element * @param node * current element * @return the element or null, if it is missing * @TODO refactoring required */ public static Element getChildElement( String name, URI namespaceURI, Node node ) { String namespace = namespaceURI == null ? null : namespaceURI.toString(); NodeList nl = node.getChildNodes(); Element element = null; Element childElement = null; if ( ( nl != null ) && ( nl.getLength() > 0 ) ) { for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { element = (Element) nl.item( i ); String s = element.getNamespaceURI(); if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) { if ( element.getLocalName().equals( name ) ) { childElement = element; break; } } } } } return childElement; } /** * Returns the specified child elements of the given element. * * @deprecated * @param name * name of the child elements * @param namespace * namespace of the child elements * @param node * current element * @return list of matching child elements */ public static ElementList getChildElements( String name, URI namespaceURI, Node node ) { String namespace = namespaceURI == null ? null : namespaceURI.toString(); NodeList nl = node.getChildNodes(); Element element = null; ElementList elementList = new ElementList(); if ( ( nl != null ) && ( nl.getLength() > 0 ) ) { for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { element = (Element) nl.item( i ); String s = element.getNamespaceURI(); if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) { if ( element.getLocalName().equals( name ) ) { elementList.addElement( element ); } } } } } return elementList; } /** * * Create a new and empty DOM document. */ public static Document create() { return getDocumentBuilder().newDocument(); } /** * Create a new document builder with: * <UL> * <li>namespace awareness = true * <li>whitespace ignoring = false * <li>validating = false * <li>expandind entity references = false * </UL> * * @return new document builder */ public static synchronized DocumentBuilder getDocumentBuilder() { DocumentBuilder builder = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware( true ); factory.setExpandEntityReferences( false ); factory.setIgnoringElementContentWhitespace( false ); factory.setValidating( false ); builder = factory.newDocumentBuilder(); } catch (Exception ex) { LOG.logError( ex.getMessage(), ex ); } return builder; } /** * Returns the specified attribute value of the given node. * @param node * current element * @param attrName * local name of the attribute * * @return the value of the attribute or null, if it is missing */ public static String getAttrValue( Node node, String attrName ) { NamedNodeMap atts = node.getAttributes(); if ( atts == null ) { return null; } Attr a = (Attr) atts.getNamedItem( attrName ); if ( a != null ) { return a.getValue(); } return null; } /** * Returns the attribute value of the given node. * * @deprecated * @param node * current element * @param namespaceURI * namespace of the attribute * @param attrName * name of the attribute * * @return the value of the attribute or null, if it is missing */ public static String getAttrValue( Node node, URI namespaceURI, String attrName ) { String namespace = namespaceURI == null ? null : namespaceURI.toString(); NamedNodeMap atts = node.getAttributes(); if ( atts == null ) { return null; } Attr a = (Attr) atts.getNamedItemNS( namespace, attrName ); if ( a != null ) { return a.getValue(); } return null; } /** * Parses an XML document and returns a DOM object. The underlying input stream is closed at the * end. * * @param reader * accessing the resource to parse * @return a DOM object, if en error occurs the response is <code>null</code> */ public static Document parse( Reader reader ) throws IOException, SAXException { javax.xml.parsers.DocumentBuilder parser = null; Document doc = null; try { DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); fac.setNamespaceAware( true ); fac.setValidating( false ); fac.setIgnoringElementContentWhitespace( false ); fac.setValidating( false ); parser = fac.newDocumentBuilder(); doc = parser.parse( new InputSource( reader ) ); } catch (ParserConfigurationException ex) { throw new IOException( "Unable to initialize DocumentBuilder: " + ex.getMessage() ); } catch (Exception e) { throw new SAXException( e.getMessage() ); } finally { reader.close(); } return doc; } /** * Parses an XML document and returns a DOM object. * * @deprecated * @param reader * accessing the resource to parse * @return a DOM object */ public static Document parse( InputStream is ) throws IOException, SAXException { return parse( new InputStreamReader( is ) ); } /** * Copies one node to another node. */ public static Node copyNode( Node source, Node dest ) { if ( source.getNodeType() == Node.TEXT_NODE ) { Text tn = dest.getOwnerDocument().createTextNode( getStringValue( source ) ); return tn; } NamedNodeMap attr = source.getAttributes(); if ( attr != null ) { for (int i = 0; i < attr.getLength(); i++) { ( (Element) dest ).setAttribute( attr.item( i ).getNodeName(), attr.item( i ) .getNodeValue() ); } } NodeList list = source.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { if ( !( list.item( i ) instanceof Text ) ) { if ( !( list.item( i ) instanceof Comment ) ) { Element en = dest.getOwnerDocument().createElementNS( list.item( i ).getNamespaceURI(), list.item( i ).getNodeName() ); if ( list.item( i ).getNodeValue() != null ) { en.setNodeValue( list.item( i ).getNodeValue() ); } Node n = copyNode( list.item( i ), en ); dest.appendChild( n ); } } else if ( ( list.item( i ) instanceof CDATASection ) ) { CDATASection cd = dest.getOwnerDocument().createCDATASection( list.item( i ).getNodeValue() ); dest.appendChild( cd ); } else { Text tn = dest.getOwnerDocument().createTextNode( list.item( i ).getNodeValue() ); dest.appendChild( tn ); } } return dest; } /** * Appends a node to an element. * <p> * The node can be from the same document or a different one (it is automatically * imported, if necessary). * * @param source * @param dest * @return the element that is appended to */ public static Node insertNodeInto( Node source, Node dest ) { Document dDoc = null; Document sDoc = source.getOwnerDocument(); if ( dest instanceof Document ) { dDoc = (Document) dest; } else { dDoc = dest.getOwnerDocument(); } if ( dDoc.equals( sDoc ) ) { dest.appendChild( source ); } else { Element element = dDoc.createElementNS( source.getNamespaceURI(), source.getNodeName() ); dest.appendChild( element ); // FIXME why not use Document.import() here? copyNode seems broken... copyNode( source, element ); } return dest; } /** * Returns the first child element of the submitted node. */ public static Element getFirstChildElement( Node node ) { NodeList nl = node.getChildNodes(); Element element = null; if ( ( nl != null ) && ( nl.getLength() > 0 ) ) { for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { element = (Element) nl.item( i ); break; } } } return element; } /** * @deprecated Returns the first child element of the submitted node that matches the given * local name. */ public static Element getChildElement( Node node, String name ) { NodeList nl = node.getChildNodes(); Element element = null; Element childElement = null; if ( ( nl != null ) && ( nl.getLength() > 0 ) ) { for (int i = 0; i < nl.getLength(); i++) { if ( nl.item( i ) instanceof Element ) { element = (Element) nl.item( i ); if ( element.getNodeName().equals( name ) ) { childElement = element; break; } } } } return childElement; } /** * Returns all child elements of the given node. * * @return all child elements of the given node. */ public static ElementList getChildElements( Node node ) { NodeList children = node.getChildNodes(); ElementList list = new ElementList(); for (int i = 0; i < children.getLength(); i++) { if ( children.item( i ).getNodeType() == Node.ELEMENT_NODE ) { list.elements.add( children.item( i ) ); } } return list; } /** * sets the value of an existing node * * @param target * @param nodeValue */ public static void setNodeValue( Element target, String nodeValue ) { NodeList nl = target.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { target.removeChild( nl.item( i ) ); } Text text = target.getOwnerDocument().createTextNode( nodeValue ); target.appendChild( text ); } /** * Creates a new <code>Element</code> node from the given parameters and appends it to the * also specified <code>Element</code>. Adds a text node to the newly generated * <code>Element</code> as well. * * @param element * <code>Element</code> that the new <code>Element</code> is appended to * @param namespaceURI * use null for default namespace * @param name * qualified name * @param nodeValue * value for a text node that is appended to the generated element * @return the appended <code>Element</code> node */ public static Element appendElement( Element element, URI namespaceURI, String name, String nodeValue ) { String namespace = namespaceURI == null ? null : namespaceURI.toString(); Element newElement = element.getOwnerDocument().createElementNS( namespace, name ); if ( nodeValue != null && !nodeValue.equals( "" ) ) newElement.appendChild( element.getOwnerDocument().createTextNode( nodeValue ) ); element.appendChild( newElement ); return newElement; } }/* ******************************************************************** Changes to this class. What the people have been up to: $Log: XMLTools.java,v $ Revision 1.46 2006/11/23 18:42:35 poth setting method setValue to be not deprecated Revision 1.45 2006/09/05 11:51:22 schmitz Added a whitespace trim while parsing qualified names. Revision 1.44 2006/07/12 14:46:16 poth comment footer added ********************************************************************** */