    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/framework/xml/XMLTools.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036
    037    package org.deegree.framework.xml;
    038
    039    import java.io.IOException;
    040    import java.io.InputStream;
    041    import java.io.InputStreamReader;
    042    import java.io.Reader;
    043    import java.io.StringReader;
    044    import java.net.URI;
    045    import java.net.URISyntaxException;
    046    import java.util.ArrayList;
    047    import java.util.Iterator;
    048    import java.util.List;
    049    import java.util.Map;
    050
    051    import javax.xml.parsers.DocumentBuilder;
    052    import javax.xml.parsers.DocumentBuilderFactory;
    053    import javax.xml.parsers.ParserConfigurationException;
    054
    055    import org.deegree.datatypes.QualifiedName;
    056    import org.deegree.framework.log.ILogger;
    057    import org.deegree.framework.log.LoggerFactory;
    058    import org.deegree.framework.util.StringTools;
    059    import org.deegree.ogcbase.CommonNamespaces;
    060    import org.jaxen.JaxenException;
    061    import org.jaxen.XPath;
    062    import org.jaxen.dom.DOMXPath;
    063    import org.w3c.dom.Attr;
    064    import org.w3c.dom.CDATASection;
    065    import org.w3c.dom.Comment;
    066    import org.w3c.dom.Document;
    067    import org.w3c.dom.Element;
    068    import org.w3c.dom.NamedNodeMap;
    069    import org.w3c.dom.Node;
    070    import org.w3c.dom.NodeList;
    071    import org.w3c.dom.Text;
    072    import org.xml.sax.InputSource;
    073    import org.xml.sax.SAXException;
    074
    075    /**
    076     * XML Tools based on JAXP 1.1 for parsing documents and retrieving node values/node attributes. Furthermore this
    077     * utility class provides node retrieval based on XPath expressions.
    078     *
    079     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider </a>
    080     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
    081     * @author last edited by: $Author: aschmitz $
    082     *
    083     * @version $Revision: 20895 $, $Date: 2009-11-19 15:59:59 +0100 (Do, 19. Nov 2009) $
    084     */
    085    public final class XMLTools {
    086
    087        private static final ILogger LOG = LoggerFactory.getLogger( XMLTools.class );
    088
    089        private XMLTools() {
    090            // hidden constructor to prevent instantiation
    091        }
    092
    093        // ------------------------------------------------------------------------
    094        // XPath based parsing methods
    095        // ------------------------------------------------------------------------
    096
    097        /**
    098         * @param contextNode
    099         * @param xPathQuery
    100         * @param nsContext
    101         * @return Node
    102         * @throws XMLParsingException
    103         */
    104        public static Node getNode( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    105                                throws XMLParsingException {
    106            Node node = null;
    107            try {
    108                XPath xpath = new DOMXPath( xPathQuery );
    109                xpath.setNamespaceContext( nsContext );
    110                node = (Node) xpath.selectSingleNode( contextNode );
    111
    112                if ( xPathQuery.endsWith( "text()" ) ) {
    113                    List<Node> nl = xpath.selectNodes( contextNode );
    114                    int pos = xPathQuery.lastIndexOf( "/" );
    115                    if ( pos > 0 ) {
    116                        xPathQuery = xPathQuery.substring( 0, pos );
    117                    } else {
    118                        xPathQuery = ".";
    119                    }
    120                    xpath = new DOMXPath( xPathQuery );
    121                    xpath.setNamespaceContext( nsContext );
    122                    List<Node> nl_ = xpath.selectNodes( contextNode );
    123                    List<String> tmp = new ArrayList<String>( nl_.size() );
    124                    for ( int i = 0; i < nl_.size(); i++ ) {
    125                        tmp.add( getStringValue( nl_.get( i ) ) );
    126                    }
    127
    128                    for ( int i = 0; i < nl.size(); i++ ) {
    129                        try {
    130                            nl.get( i ).getParentNode().removeChild( nl.get( i ) );
    131                        } catch ( Exception e ) {
    132                            // no exception thrown, why catch them?
    133                        }
    134                    }
    135
    136                    Document doc = contextNode.getOwnerDocument();
    137                    for ( int i = 0; i < tmp.size(); i++ ) {
    138                        Text text = doc.createTextNode( tmp.get( i ) );
    139                        nl_.get( i ).appendChild( text );
    140                        node = text;
    141                    }
    142                }
    143
    144            } catch ( JaxenException e ) {
    145                throw new XMLParsingException( "Error evaluating XPath-expression '" + xPathQuery + "' from context node '"
    146                                               + contextNode.getNodeName() + "': " + e.getMessage(), e );
    147            }
    148            return node;
    149        }
    150
    151        /**
    152         * @param contextNode
    153         * @param xpath
    154         * @param nsContext
    155         * @return the element
    156         * @throws XMLParsingException
    157         * @throws ClassCastException
    158         *             if the node was not an element
    159         */
    160        public static Element getElement( Node contextNode, String xpath, NamespaceContext nsContext )
    161                                throws XMLParsingException {
    162            Node node = getNode( contextNode, xpath, nsContext );
    163            return (Element) node;
    164        }
    165
    166        /**
    167         * @param contextNode
    168         * @param xPathQuery
    169         * @param nsContext
    170         * @param defaultValue
    171         * @return the node's String value
    172         * @throws XMLParsingException
    173         */
    174        public static String getNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    175                                              String defaultValue )
    176                                throws XMLParsingException {
    177
    178            String value = defaultValue;
    179            Node node = getNode( contextNode, xPathQuery, nsContext );
    180
    181            if ( node != null ) {
    182                value = getStringValue( node );
    183            }
    184            return value;
    185        }
    186
    187        /**
    188         * @param contextNode
    189         * @param xPathQuery
    190         * @param nsContext
    191         * @param defaultValue
    192         * @return the node's boolean value
    193         * @throws XMLParsingException
    194         */
    195        public static boolean getNodeAsBoolean( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    196                                                boolean defaultValue )
    197                                throws XMLParsingException {
    198            boolean value = defaultValue;
    199            Node node = getNode( contextNode, xPathQuery, nsContext );
    200            if ( node != null ) {
    201                String stringValue = getStringValue( node );
    202
    203                if ( "true".equals( stringValue ) || "yes".equals( stringValue ) || "1".equals( stringValue ) ) {
    204                    value = true;
    205                } else if ( "false".equals( stringValue ) || "no".equals( stringValue ) || "0".equals( stringValue ) ) {
    206                    value = false;
    207                } else {
    208                    throw new XMLParsingException( "XPath-expression '" + xPathQuery + " ' from context node '"
    209                                                   + contextNode.getNodeName() + "' has an invalid value ('" + stringValue
    210                                                   + "'). Valid values are: 'true', 'yes', '1' " + "'false', 'no' and '0'." );
    211                }
    212            }
    213            return value;
    214        }
    215
    216        /**
    217         * @param contextNode
    218         * @param xPathQuery
    219         * @param nsContext
    220         * @param defaultValue
    221         * @return the node's integer value
    222         * @throws XMLParsingException
    223         */
    224        public static int getNodeAsInt( Node contextNode, String xPathQuery, NamespaceContext nsContext, int defaultValue )
    225                                throws XMLParsingException {
    226            int value = defaultValue;
    227            Node node = getNode( contextNode, xPathQuery, nsContext );
    228            if ( node != null ) {
    229                String stringValue = getStringValue( node );
    230                try {
    231                    value = Integer.parseInt( stringValue );
    232                } catch ( NumberFormatException e ) {
    233                    throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    234                                                   + "' from context node '" + contextNode.getNodeName()
    235                                                   + "' does not denote a valid integer value." );
    236                }
    237            }
    238            return value;
    239        }
    240
    241        /**
    242         * @param contextNode
    243         * @param xPathQuery
    244         * @param nsContext
    245         * @param defaultValue
    246         * @return the node's double value
    247         * @throws XMLParsingException
    248         */
    249        public static double getNodeAsDouble( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    250                                              double defaultValue )
    251                                throws XMLParsingException {
    252            double value = defaultValue;
    253            Node node = getNode( contextNode, xPathQuery, nsContext );
    254            if ( node != null ) {
    255                String stringValue = getStringValue( node );
    256                try {
    257                    value = Double.parseDouble( stringValue );
    258                } catch ( NumberFormatException e ) {
    259                    throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    260                                                   + "' from context node '" + contextNode.getNodeName()
    261                                                   + "' does not denote a valid double value." );
    262                }
    263            }
    264            return value;
    265        }
    266
    267        /**
    268         * @param contextNode
    269         * @param xPathQuery
    270         * @param nsContext
    271         * @param defaultValue
    272         * @return the node as URI
    273         * @throws XMLParsingException
    274         */
    275        public static URI getNodeAsURI( Node contextNode, String xPathQuery, NamespaceContext nsContext, URI defaultValue )
    276                                throws XMLParsingException {
    277            URI value = defaultValue;
    278            Node node = getNode( contextNode, xPathQuery, nsContext );
    279            if ( node != null ) {
    280                String stringValue = getStringValue( node );
    281                try {
    282                    value = new URI( stringValue );
    283                } catch ( URISyntaxException e ) {
    284                    throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    285                                                   + "' from context node '" + contextNode.getNodeName()
    286                                                   + "' does not denote a valid URI." );
    287                }
    288            }
    289            return value;
    290        }
    291
    292        /**
    293         * @param contextNode
    294         * @param xPathQuery
    295         * @param nsContext
    296         * @param defaultValue
    297         * @return the node as qualified name
    298         * @throws XMLParsingException
    299         */
    300        public static QualifiedName getNodeAsQualifiedName( Node contextNode, String xPathQuery,
    301                                                            NamespaceContext nsContext, QualifiedName defaultValue )
    302                                throws XMLParsingException {
    303
    304            QualifiedName value = defaultValue;
    305            Node node = getNode( contextNode, xPathQuery, nsContext );
    306
    307            if ( node != null ) {
    308                value = getQualifiedNameValue( node );
    309            }
    310            return value;
    311
    312        }
    313
    314        /**
    315         * returns a list of nodes matching the passed XPath
    316         *
    317         * @param contextNode
    318         * @param xPathQuery
    319         * @param nsContext
    320         * @return a list of nodes matching the passed XPath
    321         * @throws XMLParsingException
    322         */
    323        public static List<Node> getNodes( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    324                                throws XMLParsingException {
    325            List<Node> nl = null;
    326            try {
    327                XPath xpath = new DOMXPath( xPathQuery );
    328                xpath.setNamespaceContext( nsContext );
    329                nl = xpath.selectNodes( contextNode );
    330
    331                if ( xPathQuery.endsWith( "text()" ) ) {
    332
    333                    int pos = xPathQuery.lastIndexOf( "/" );
    334                    if ( pos > 0 ) {
    335                        xPathQuery = xPathQuery.substring( 0, pos );
    336                    } else {
    337                        xPathQuery = ".";
    338                    }
    339                    xpath = new DOMXPath( xPathQuery );
    340                    xpath.setNamespaceContext( nsContext );
    341                    List<?> nl_ = xpath.selectNodes( contextNode );
    342                    List<String> tmp = new ArrayList<String>( nl_.size() );
    343                    for ( int i = 0; i < nl_.size(); i++ ) {
    344                        tmp.add( getStringValue( (Node) nl_.get( i ) ) );
    345                    }
    346
    347                    for ( int i = 0; i < nl.size(); i++ ) {
    348                        try {
    349                            nl.get( i ).getParentNode().removeChild( nl.get( i ) );
    350                        } catch ( Exception e ) {
    351                            // ignored, but why? Nothing is actually thrown here?
    352                        }
    353                    }
    354
    355                    nl.clear();
    356                    Document doc = contextNode.getOwnerDocument();
    357                    for ( int i = 0; i < tmp.size(); i++ ) {
    358                        Text text = doc.createTextNode( tmp.get( i ) );
    359                        ( (Node) nl_.get( i ) ).appendChild( text );
    360                        nl.add( text );
    361                    }
    362                }
    363            } catch ( JaxenException e ) {
    364                throw new XMLParsingException( "Error evaluating XPath-expression '" + xPathQuery + "' from context node '"
    365                                               + contextNode.getNodeName() + "': " + e.getMessage(), e );
    366            }
    367            return nl;
    368        }
    369
    370        /**
    371         * @param contextNode
    372         * @param xPathQuery
    373         * @param nsContext
    374         * @return the list of nodes as strings
    375         * @throws XMLParsingException
    376         */
    377        public static String[] getNodesAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    378                                throws XMLParsingException {
    379            String[] values = null;
    380            List<Node> nl = getNodes( contextNode, xPathQuery, nsContext );
    381            if ( nl != null ) {
    382                values = new String[nl.size()];
    383                for ( int i = 0; i < nl.size(); i++ ) {
    384                    values[i] = getStringValue( nl.get( i ) );
    385                }
    386            } else {
    387                values = new String[0];
    388            }
    389            return values;
    390        }
    391
    392        /**
    393         * @param contextNode
    394         *            to get the strings from
    395         * @param xPathQuery
    396         *            finding the nodes
    397         * @param nsContext
    398         *            to find the namespaces from.
    399         * @return the list of nodes as strings or an empty list, never <code>null</code>
    400         * @throws XMLParsingException
    401         */
    402        public static List<String> getNodesAsStringList( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    403                                throws XMLParsingException {
    404            List<String> result = new ArrayList<String>();
    405            List<Node> nl = getNodes( contextNode, xPathQuery, nsContext );
    406            if ( nl != null ) {
    407                result = new ArrayList<String>( nl.size() );
    408                for ( int i = 0; i < nl.size(); i++ ) {
    409                    result.add( getStringValue( nl.get( i ) ) );
    410                }
    411            }
    412            return result;
    413        }
    414
    415        /**
    416         * @param contextNode
    417         * @param xPathQuery
    418         * @param nsContext
    419         * @return the nodes as URIs
    420         * @throws XMLParsingException
    421         */
    422        public static URI[] getNodesAsURIs( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    423                                throws XMLParsingException {
    424            String[] values = getNodesAsStrings( contextNode, xPathQuery, nsContext );
    425            URI[] uris = new URI[values.length];
    426            for ( int i = 0; i < uris.length; i++ ) {
    427                try {
    428                    uris[i] = new URI( values[i] );
    429                } catch ( URISyntaxException e ) {
    430                    throw new XMLParsingException( "Result '" + values[i] + "' of XPath-expression '" + xPathQuery
    431                                                   + "' from context node '" + contextNode.getNodeName()
    432                                                   + "' does not denote a valid URI." );
    433                }
    434            }
    435            return uris;
    436        }
    437
    438        /**
    439         * @param contextNode
    440         * @param xPathQuery
    441         * @param nsContext
    442         * @return the nodes as qualified names
    443         * @throws XMLParsingException
    444         */
    445        public static QualifiedName[] getNodesAsQualifiedNames( Node contextNode, String xPathQuery,
    446                                                                NamespaceContext nsContext )
    447                                throws XMLParsingException {
    448
    449            QualifiedName[] values = null;
    450            List<Node> nl = getNodes( contextNode, xPathQuery, nsContext );
    451            if ( nl != null ) {
    452                values = new QualifiedName[nl.size()];
    453                for ( int i = 0; i < nl.size(); i++ ) {
    454                    values[i] = getQualifiedNameValue( nl.get( i ) );
    455                }
    456            } else {
    457                values = new QualifiedName[0];
    458            }
    459            return values;
    460
    461        }
    462
    463        /**
    464         * @param contextNode
    465         * @param xPathQuery
    466         * @param nsContext
    467         * @return the node
    468         * @throws XMLParsingException
    469         */
    470        public static Node getRequiredNode( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    471                                throws XMLParsingException {
    472            Node node = getNode( contextNode, xPathQuery, nsContext );
    473            if ( node == null ) {
    474                throw new XMLParsingException( "XPath-expression '" + xPathQuery + "' from context node '"
    475                                               + contextNode.getNodeName() + "' yields no result!" );
    476            }
    477            return node;
    478        }
    479
    480        /**
    481         * @param contextNode
    482         * @param xpath
    483         * @param nsContext
    484         * @return the element
    485         * @throws XMLParsingException
    486         * @throws ClassCastException
    487         *             if the node was not an element
    488         */
    489        public static Element getRequiredElement( Node contextNode, String xpath, NamespaceContext nsContext )
    490                                throws XMLParsingException {
    491            Node node = getRequiredNode( contextNode, xpath, nsContext );
    492            return (Element) node;
    493        }
    494
    495        /**
    496         * @param contextNode
    497         * @param xPathQuery
    498         * @param nsContext
    499         * @return the node as string
    500         * @throws XMLParsingException
    501         */
    502        public static String getRequiredNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    503                                throws XMLParsingException {
    504            Node node = getRequiredNode( contextNode, xPathQuery, nsContext );
    505            return getStringValue( node );
    506        }
    507
    508        /**
    509         * @param contextNode
    510         *            the parent of the requested node
    511         * @param xPathQuery
    512         *            the node to get out of the dom
    513         * @param nsContext
    514         *            context of the node
    515         * @param validValues
    516         *            the values that are valid for the required node
    517         * @return one of the String valid String values
    518         * @throws XMLParsingException
    519         *             if no Node was found or the text of the Node was not present in the given valid strings.
    520         */
    521        public static String getRequiredNodeAsString( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    522                                                      String[] validValues )
    523                                throws XMLParsingException {
    524            String value = getRequiredNodeAsString( contextNode, xPathQuery, nsContext );
    525            boolean found = false;
    526            for ( int i = 0; i < validValues.length; i++ ) {
    527                if ( value.equals( validValues[i] ) ) {
    528                    found = true;
    529                    break;
    530                }
    531            }
    532            if ( !found ) {
    533                StringBuffer sb = new StringBuffer( "XPath-expression '" + xPathQuery + " ' from context node '"
    534                                                    + contextNode.getNodeName()
    535                                                    + "' has an invalid value. Valid values are: " );
    536                for ( int i = 0; i < validValues.length; i++ ) {
    537                    sb.append( "'" ).append( validValues[i] ).append( "'" );
    538                    if ( i != validValues.length - 1 ) {
    539                        sb.append( ", " );
    540                    } else {
    541                        sb.append( "." );
    542                    }
    543                }
    544                throw new XMLParsingException( sb.toString() );
    545            }
    546            return value;
    547        }
    548
    549        /**
    550         * Returns the parts of the targeted node value which are separated by the specified regex.
    551         *
    552         * @param contextNode
    553         * @param xPathQuery
    554         * @param nsContext
    555         * @param regex
    556         * @return the parts of the targeted node value which are separated by the specified regex.
    557         * @throws XMLParsingException
    558         */
    559        public static String[] getRequiredNodeAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    560                                                         String regex )
    561                                throws XMLParsingException {
    562            Node node = getRequiredNode( contextNode, xPathQuery, nsContext );
    563            return StringTools.toArray( getStringValue( node ), regex, false );
    564        }
    565
    566        /**
    567         * @param contextNode
    568         * @param xPathQuery
    569         * @param nsContext
    570         * @return the node as boolean
    571         * @throws XMLParsingException
    572         */
    573        public static boolean getRequiredNodeAsBoolean( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    574                                throws XMLParsingException {
    575            boolean value = false;
    576            Node node = getRequiredNode( contextNode, xPathQuery, nsContext );
    577            String stringValue = getStringValue( node );
    578            if ( "true".equals( stringValue ) || "yes".equals( stringValue ) ) {
    579                value = true;
    580            } else if ( "false".equals( stringValue ) || "no".equals( stringValue ) ) {
    581                value = false;
    582            } else {
    583                throw new XMLParsingException( "XPath-expression '" + xPathQuery + " ' from context node '"
    584                                               + contextNode.getNodeName() + "' has an invalid value ('" + stringValue
    585                                               + "'). Valid values are: 'true', 'yes', 'false' and 'no'." );
    586            }
    587
    588            return value;
    589        }
    590
    591        /**
    592         * @param contextNode
    593         * @param xPathQuery
    594         * @param nsContext
    595         * @return the node as integer
    596         * @throws XMLParsingException
    597         */
    598        public static int getRequiredNodeAsInt( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    599                                throws XMLParsingException {
    600
    601            int value = 0;
    602            String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext );
    603            try {
    604                value = Integer.parseInt( stringValue );
    605            } catch ( NumberFormatException e ) {
    606                throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    607                                               + "' from context node '" + contextNode.getNodeName()
    608                                               + "' does not denote a valid integer value." );
    609            }
    610            return value;
    611        }
    612
    613        /**
    614         * @param contextNode
    615         * @param xPathQuery
    616         * @param nsContext
    617         * @return the node as double
    618         * @throws XMLParsingException
    619         */
    620        public static double getRequiredNodeAsDouble( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    621                                throws XMLParsingException {
    622
    623            double value = 0;
    624            String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext );
    625            try {
    626                value = Double.parseDouble( stringValue );
    627            } catch ( NumberFormatException e ) {
    628                throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    629                                               + "' from context node '" + contextNode.getNodeName()
    630                                               + "' does not denote a valid double value." );
    631            }
    632            return value;
    633        }
    634
    635        /**
    636         * Returns the parts of the targeted node value which are separated by the specified regex. The string parts are
    637         * converted to doubles.
    638         *
    639         * @param contextNode
    640         * @param xPathQuery
    641         * @param nsContext
    642         * @param regex
    643         * @return the parts of the targeted node value which are separated by the specified regex.
    644         * @throws XMLParsingException
    645         */
    646        public static double[] getRequiredNodeAsDoubles( Node contextNode, String xPathQuery, NamespaceContext nsContext,
    647                                                         String regex )
    648                                throws XMLParsingException {
    649            String[] parts = getRequiredNodeAsStrings( contextNode, xPathQuery, nsContext, regex );
    650            double[] doubles = new double[parts.length];
    651            for ( int i = 0; i < parts.length; i++ ) {
    652                try {
    653                    doubles[i] = Double.parseDouble( parts[i] );
    654                } catch ( NumberFormatException e ) {
    655                    throw new XMLParsingException( "Value '" + parts[i] + "' does not denote a valid double value." );
    656                }
    657            }
    658            return doubles;
    659        }
    660
    661        /**
    662         * @param contextNode
    663         * @param xPathQuery
    664         * @param nsContext
    665         * @return the node as URI
    666         * @throws XMLParsingException
    667         */
    668        public static URI getRequiredNodeAsURI( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    669                                throws XMLParsingException {
    670
    671            URI uri = null;
    672            String stringValue = getRequiredNodeAsString( contextNode, xPathQuery, nsContext );
    673
    674            try {
    675                uri = new URI( stringValue );
    676            } catch ( URISyntaxException e ) {
    677                throw new XMLParsingException( "Result '" + stringValue + "' of XPath-expression '" + xPathQuery
    678                                               + "' from context node '" + contextNode.getNodeName()
    679                                               + "' does not denote a valid URI." );
    680            }
    681            return uri;
    682        }
    683
    684        /**
    685         * @param contextNode
    686         * @param xPathQuery
    687         * @param nsContext
    688         * @return the node as qualified name
    689         * @throws XMLParsingException
    690         */
    691        public static QualifiedName getRequiredNodeAsQualifiedName( Node contextNode, String xPathQuery,
    692                                                                    NamespaceContext nsContext )
    693                                throws XMLParsingException {
    694            Node node = getRequiredNode( contextNode, xPathQuery, nsContext );
    695            return getQualifiedNameValue( node );
    696        }
    697
    698        /**
    699         * @param contextNode
    700         * @param xPathQuery
    701         * @param nsContext
    702         * @return the nodes
    703         * @throws XMLParsingException
    704         */
    705        public static List<Node> getRequiredNodes( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    706                                throws XMLParsingException {
    707            List<Node> nl = getNodes( contextNode, xPathQuery, nsContext );
    708            if ( nl.size() == 0 ) {
    709                throw new XMLParsingException( "XPath-expression: '" + xPathQuery + "' from context node '"
    710                                               + contextNode.getNodeName() + "' does not yield a result." );
    711            }
    712
    713            return nl;
    714        }
    715
    716        /**
    717         * @param contextNode
    718         * @param xpath
    719         * @param nsContext
    720         * @return a list of Elements
    721         * @throws XMLParsingException
    722         * @throws ClassCastException
    723         *             if the resulting nodes of the xpath are not elements
    724         */
    725        public static List<Element> getRequiredElements( Node contextNode, String xpath, NamespaceContext nsContext )
    726                                throws XMLParsingException {
    727            List<Node> nodes = getRequiredNodes( contextNode, xpath, nsContext );
    728
    729            ArrayList<Element> list = new ArrayList<Element>( nodes.size() );
    730            for ( Node n : nodes ) {
    731                list.add( (Element) n );
    732            }
    733
    734            return list;
    735        }
    736
    737        /**
    738         * @param contextNode
    739         * @param xpath
    740         * @param nsContext
    741         * @return a list of Elements
    742         * @throws XMLParsingException
    743         * @throws ClassCastException
    744         *             if the resulting nodes of the xpath are not elements
    745         */
    746        public static List<Element> getElements( Node contextNode, String xpath, NamespaceContext nsContext )
    747                                throws XMLParsingException {
    748            List<Node> nodes = getNodes( contextNode, xpath, nsContext );
    749
    750            ArrayList<Element> list = new ArrayList<Element>( nodes.size() );
    751            for ( Node n : nodes ) {
    752                list.add( (Element) n );
    753            }
    754
    755            return list;
    756        }
    757
    758        /**
    759         * Returns the content of the nodes matching the XPathQuery as a String array. At least one node must match the
    760         * query otherwise an exception will be thrown.
    761         *
    762         * @param contextNode
    763         * @param xPathQuery
    764         * @param nsContext
    765         * @return the content of the nodes matching the XPathQuery as a String array.
    766         * @throws XMLParsingException
    767         */
    768        public static String[] getRequiredNodesAsStrings( Node contextNode, String xPathQuery, NamespaceContext nsContext )
    769                                throws XMLParsingException {
    770
    771            List<Node> nl = getRequiredNodes( contextNode, xPathQuery, nsContext );
    772
    773            String[] values = new String[nl.size()];
    774            for ( int i = 0; i < nl.size(); i++ ) {
    775                values[i] = getStringValue( nl.get( i ) );
    776            }
    777
    778            return values;
    779        }
    780
    781        /**
    782         * @param contextNode
    783         * @param xPathQuery
    784         * @param nsContext
    785         * @return the qualified names
    786         * @throws XMLParsingException
    787         */
    788        public static QualifiedName[] getRequiredNodesAsQualifiedNames( Node contextNode, String xPathQuery,
    789                                                                        NamespaceContext nsContext )
    790                                throws XMLParsingException {
    791
    792            List<Node> nl = getRequiredNodes( contextNode, xPathQuery, nsContext );
    793
    794            QualifiedName[] values = new QualifiedName[nl.size()];
    795            for ( int i = 0; i < nl.size(); i++ ) {
    796                values[i] = getQualifiedNameValue( nl.get( i ) );
    797            }
    798
    799            return values;
    800        }
    801
    802        /**
    803         * @param value
    804         * @param validValues
    805         * @throws XMLParsingException
    806         */
    807        public static void checkValue( String value, String[] validValues )
    808                                throws XMLParsingException {
    809            for ( int i = 0; i < validValues.length; i++ ) {
    810                if ( validValues[i].equals( value ) ) {
    811                    return;
    812                }
    813            }
    814            StringBuffer sb = new StringBuffer( "Value '" ).append( value ).append( "' is invalid. Valid values are: " );
    815            for ( int i = 0; i < validValues.length; i++ ) {
    816                sb.append( "'" ).append( validValues[i] ).append( "'" );
    817                if ( i != validValues.length - 1 ) {
    818                    sb.append( ", " );
    819                } else {
    820                    sb.append( "." );
    821                }
    822            }
    823            throw new XMLParsingException( sb.toString() );
    824        }
    825
    826        // ------------------------------------------------------------------------
    827        // Node creation methods
    828        // ------------------------------------------------------------------------
    829
    830        /**
    831         * Creates a new <code>Element</code> node from the given parameters and appends it to the also specified
    832         * <code>Element</code>.
    833         *
    834         * @param element
    835         *            <code>Element</code> that the new <code>Element</code> is appended to
    836         * @param namespaceURI
    837         *            use null for default namespace
    838         * @param name
    839         *            qualified name
    840         * @return the appended <code>Element</code> node
    841         */
    842        public static Element appendElement( Element element, URI namespaceURI, String name ) {
    843            return appendElement( element, namespaceURI, name, null );
    844        }
    845
    846        /**
    847         * Appends a namespace binding for the specified element that binds the given prefix to the given namespace using a
    848         * special attribute: xmlns:prefix=namespace
    849         *
    850         * @param element
    851         * @param prefix
    852         * @param namespace
    853         */
    854        public static void appendNSBinding( Element element, String prefix, URI namespace ) {
    855            Attr attribute = element.getOwnerDocument().createAttributeNS( CommonNamespaces.XMLNS.toASCIIString(),
    856                                                                           CommonNamespaces.XMLNS_PREFIX + ":" + prefix );
    857            attribute.setNodeValue( namespace.toASCIIString() );
    858            element.getAttributes().setNamedItemNS( attribute );
    859        }
    860
    861        /**
    862         * Appends the default namespace binding for the specified element.
    863         *
    864         * @param element
    865         * @param namespace
    866         */
    867        public static void appendNSDefaultBinding( Element element, URI namespace ) {
    868            Attr attribute = element.getOwnerDocument().createAttributeNS( CommonNamespaces.XMLNS.toASCIIString(),
    869                                                                           CommonNamespaces.XMLNS_PREFIX );
    870            attribute.setNodeValue( namespace.toASCIIString() );
    871            element.getAttributes().setNamedItemNS( attribute );
    872        }
    873
    874        /**
    875         * Appends the given namespace bindings to the specified element.
    876         * <p>
    877         * NOTE: The prebound prefix "xml" is skipped.
    878         *
    879         * @param element
    880         * @param nsContext
    881         */
    882        public static void appendNSBindings( Element element, NamespaceContext nsContext ) {
    883            Map<String, URI> namespaceMap = nsContext.getNamespaceMap();
    884            Iterator<String> prefixIter = namespaceMap.keySet().iterator();
    885            while ( prefixIter.hasNext() ) {
    886                String prefix = prefixIter.next();
    887                if ( !CommonNamespaces.XMLNS_PREFIX.equals( prefix ) ) {
    888                    URI namespace = namespaceMap.get( prefix );
    889                    appendNSBinding( element, prefix, namespace );
    890                }
    891            }
    892        }
    893
    894        // ------------------------------------------------------------------------
    895        // String value methods
    896        // ------------------------------------------------------------------------
    897
    898        /**
    899         * Returns the text contained in the specified element.
    900         *
    901         * @param node
    902         *            current element
    903         * @return the textual contents of the element
    904         */
    905        public static String getStringValue( Node node ) {
    906            NodeList children = node.getChildNodes();
    907            StringBuffer sb = new StringBuffer( children.getLength() * 500 );
    908            if ( node.getNodeValue() != null ) {
    909                sb.append( node.getNodeValue().trim() );
    910            }
    911            if ( node.getNodeType() != Node.ATTRIBUTE_NODE ) {
    912                for ( int i = 0; i < children.getLength(); i++ ) {
    913                    if ( children.item( i ).getNodeType() == Node.TEXT_NODE
    914                         || children.item( i ).getNodeType() == Node.CDATA_SECTION_NODE ) {
    915                        sb.append( children.item( i ).getNodeValue() );
    916                    }
    917                }
    918            }
    919            return sb.toString();
    920        }
    921
    922        /**
    923         * Returns the text contained in the specified child element of the given element.
    924         *
    925         * @param name
    926         *            name of the child element
    927         * @param namespace
    928         *            namespace of the child element
    929         * @param node
    930         *            current element
    931         * @param defaultValue
    932         *            default value if element is missing
    933         * @return the textual contents of the element or the given default value, if missing
    934         */
    935        public static String getStringValue( String name, URI namespace, Node node, String defaultValue ) {
    936
    937            String value = defaultValue;
    938            Element element = getChildElement( name, namespace, node );
    939
    940            if ( element != null ) {
    941                value = getStringValue( element );
    942            }
    943            if ( value == null || value.equals( "" ) ) {
    944                value = defaultValue;
    945            }
    946
    947            return value;
    948        }
    949
    950        /**
    951         * Returns the text contained in the specified child element of the given element.
    952         *
    953         * @param name
    954         *            name of the child element
    955         * @param namespace
    956         *            namespace of the child element
    957         * @param node
    958         *            current element
    959         * @return the textual contents of the element or null, if it is missing
    960         * @throws XMLParsingException
    961         *             if the specified child element is missing
    962         */
    963        public static String getRequiredStringValue( String name, URI namespace, Node node )
    964                                throws XMLParsingException {
    965            Element element = getRequiredChildElement( name, namespace, node );
    966            return getStringValue( element );
    967        }
    968
    969        /**
    970         * Returns the value of the specified node attribute.
    971         *
    972         * @param name
    973         *            name of attribute
    974         * @param namespaceURI
    975         *            namespace of attribute
    976         * @param node
    977         *            current element
    978         * @return the textual contents of the attribute
    979         * @throws XMLParsingException
    980         *             if specified attribute is missing
    981         */
    982        public static String getRequiredAttrValue( String name, URI namespaceURI, Node node )
    983                                throws XMLParsingException {
    984
    985            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    986
    987            String value = null;
    988            NamedNodeMap atts = node.getAttributes();
    989            if ( atts != null ) {
    990                Attr attribute = null;
    991                if ( namespace == null ) {
    992                    attribute = (Attr) atts.getNamedItem( name );
    993                } else {
    994                    attribute = (Attr) atts.getNamedItemNS( namespace, name );
    995                }
    996
    997                if ( attribute != null ) {
    998                    value = attribute.getValue();
    999                }
    1000            }
    1001            if ( value == null ) {
    1002                throw new XMLParsingException( "Required attribute " + name + '(' + namespaceURI + ") of element "
    1003                                               + node.getNodeName() + " is missing." );
    1004            }
    1005            return value;
    1006        }
    1007
    1008        /**
    1009         * Parses the value of the submitted <code>Node</code> as a <code>QualifiedName</code>.
    1010         * <p>
    1011         * To parse the text contents of an <code>Element</code> node, the actual text node must be given, not the
    1012         * <code>Element</code> node itself.
    1013         * </p>
    1014         *
    1015         * @param node
    1016         * @return object representation of the element
    1017         * @throws XMLParsingException
    1018         */
    1019        public static QualifiedName getQualifiedNameValue( Node node )
    1020                                throws XMLParsingException {
    1021
    1022            String name = node.getTextContent().trim();
    1023            QualifiedName qName = null;
    1024            if ( name.indexOf( ':' ) > -1 ) {
    1025                String[] tmp = StringTools.toArray( name, ":", false );
    1026                try {
    1027                    qName = new QualifiedName( tmp[0], tmp[1], XMLTools.getNamespaceForPrefix( tmp[0], node ) );
    1028                } catch ( URISyntaxException e ) {
    1029                    throw new XMLParsingException( e.getMessage(), e );
    1030                }
    1031            } else {
    1032                qName = new QualifiedName( name );
    1033            }
    1034            return qName;
    1035        }
    1036
    1037        /**
    1038         * Returns the namespace URI that is bound to a given prefix at a certain node in the DOM tree.
    1039         *
    1040         * @param prefix
    1041         * @param node
    1042         * @return namespace URI that is bound to the given prefix, null otherwise
    1043         * @throws URISyntaxException
    1044         */
    1045        public static URI getNamespaceForPrefix( String prefix, Node node )
    1046                                throws URISyntaxException {
    1047            if ( node == null ) {
    1048                return null;
    1049            }
    1050            if ( node.getNodeType() == Node.ELEMENT_NODE ) {
    1051                NamedNodeMap nnm = node.getAttributes();
    1052                if ( nnm != null ) {
    1053                    // LOG.logDebug( "(searching namespace for prefix (" + prefix
    1054                    // + "), resulted in a namedNodeMap for the currentNode: " + node.getNodeName() );
    1055                    for ( int i = 0; i < nnm.getLength(); i++ ) {
    1056                        Attr a = (Attr) nnm.item( i );
    1057                        // LOG.logDebug( "\t(searching namespace for prefix (" + prefix + "), resulted
    1058                        // in an attribute: "
    1059                        // + a.getName() );
    1060
    1061                        if ( a.getName().startsWith( "xmlns:" ) && a.getName().endsWith( ':' + prefix ) ) {
    1062                            return new URI( a.getValue() );
    1063                        } else if ( prefix == null && a.getName().equals( "xmlns" ) ) {
    1064                            return new URI( a.getValue() );
    1065                        }
    1066                    }
    1067                }
    1068            } else if ( node.getNodeType() == Node.ATTRIBUTE_NODE ) {
    1069                return getNamespaceForPrefix( prefix, ( (Attr) node ).getOwnerElement() );
    1070            }
    1071            return getNamespaceForPrefix( prefix, node.getParentNode() );
    1072        }
    1073
    1074        // ------------------------------------------------------------------------
    1075        // Old code - deprecated
    1076        // ------------------------------------------------------------------------
    1077
    1078        /**
    1079         * Returns the specified child element of the given element. If there is more than one element with that name, the
    1080         * first one is returned.
    1081         *
    1082         * @deprecated
    1083         * @param name
    1084         *            name of the child element
    1085         * @param namespaceURI
    1086         *            namespaceURI of the child element
    1087         * @param node
    1088         *            current element
    1089         * @return the element or null, if it is missing
    1090         * @throws XMLParsingException
    1091         *             if the specified child element is missing
    1092         * @throws XMLParsingException
    1093         * @todo refactoring required
    1094         */
    1095        @Deprecated
    1096        public static Element getRequiredChildElement( String name, URI namespaceURI, Node node )
    1097                                throws XMLParsingException {
    1098
    1099            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    1100
    1101            NodeList nl = node.getChildNodes();
    1102            Element element = null;
    1103            Element childElement = null;
    1104
    1105            if ( ( nl != null ) && ( nl.getLength() > 0 ) ) {
    1106                for ( int i = 0; i < nl.getLength(); i++ ) {
    1107                    if ( nl.item( i ) instanceof Element ) {
    1108                        element = (Element) nl.item( i );
    1109                        String s = element.getNamespaceURI();
    1110                        if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) {
    1111                            if ( element.getLocalName().equals( name ) ) {
    1112                                childElement = element;
    1113                                break;
    1114                            }
    1115                        }
    1116                    }
    1117                }
    1118            }
    1119
    1120            if ( childElement == null ) {
    1121                throw new XMLParsingException( "Required child-element " + name + '(' + namespaceURI + ") of element "
    1122                                               + node.getNodeName() + " is missing." );
    1123            }
    1124
    1125            return childElement;
    1126        }
    1127
    1128        /**
    1129         * Returns the specified child element of the given element. If there is more than one with that name, the first one
    1130         * is returned.
    1131         *
    1132         * @deprecated
    1133         * @param name
    1134         *            name of the child element
    1135         * @param namespaceURI
    1136         *            namespace of the child element
    1137         * @param node
    1138         *            current element
    1139         * @return the element or null, if it is missing
    1140         * @TODO refactoring required
    1141         */
    1142        @Deprecated
    1143        public static Element getChildElement( String name, URI namespaceURI, Node node ) {
    1144
    1145            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    1146
    1147            NodeList nl = node.getChildNodes();
    1148            Element element = null;
    1149            Element childElement = null;
    1150
    1151            if ( ( nl != null ) && ( nl.getLength() > 0 ) ) {
    1152                for ( int i = 0; i < nl.getLength(); i++ ) {
    1153                    if ( nl.item( i ) instanceof Element ) {
    1154                        element = (Element) nl.item( i );
    1155                        String s = element.getNamespaceURI();
    1156                        if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) {
    1157                            if ( element.getLocalName().equals( name ) ) {
    1158                                childElement = element;
    1159                                break;
    1160                            }
    1161                        }
    1162                    }
    1163                }
    1164            }
    1165            return childElement;
    1166        }
    1167
    1168        /**
    1169         * Returns the specified child elements of the given element.
    1170         *
    1171         * @deprecated
    1172         * @param name
    1173         *            name of the child elements
    1174         * @param namespaceURI
    1175         *            namespaceURI of the child elements
    1176         * @param node
    1177         *            current element
    1178         * @return list of matching child elements
    1179         */
    1180        @Deprecated
    1181        public static ElementList getChildElements( String name, URI namespaceURI, Node node ) {
    1182
    1183            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    1184
    1185            NodeList nl = node.getChildNodes();
    1186            Element element = null;
    1187            ElementList elementList = new ElementList();
    1188
    1189            if ( ( nl != null ) && ( nl.getLength() > 0 ) ) {
    1190                for ( int i = 0; i < nl.getLength(); i++ ) {
    1191                    if ( nl.item( i ) instanceof Element ) {
    1192                        element = (Element) nl.item( i );
    1193
    1194                        String s = element.getNamespaceURI();
    1195
    1196                        if ( ( s == null && namespace == null ) || ( namespace != null && namespace.equals( s ) ) ) {
    1197                            if ( element.getLocalName().equals( name ) ) {
    1198                                elementList.addElement( element );
    1199                            }
    1200                        }
    1201                    }
    1202                }
    1203            }
    1204            return elementList;
    1205        }
    1206
    1207        /**
    1208         *
    1209         * Create a new and empty DOM document.
    1210         *
    1211         * @return a new and empty DOM document.
    1212         */
    1213        public static Document create() {
    1214            return getDocumentBuilder().newDocument();
    1215        }
    1216
    1217        /**
    1218         * Create a new document builder with:
    1219         * <UL>
    1220         * <li>namespace awareness = true
    1221         * <li>whitespace ignoring = false
    1222         * <li>validating = false
    1223         * <li>expandind entity references = false
    1224         * </UL>
    1225         *
    1226         * @return new document builder
    1227         */
    1228        public static synchronized DocumentBuilder getDocumentBuilder() {
    1229            DocumentBuilder builder = null;
    1230            try {
    1231                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    1232                factory.setNamespaceAware( true );
    1233                factory.setExpandEntityReferences( false );
    1234                factory.setIgnoringElementContentWhitespace( false );
    1235                factory.setValidating( false );
    1236                try {
    1237                    factory.setAttribute( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
    1238                } catch ( IllegalArgumentException _ ) {
    1239                    // ignore it, we just cannot set the feature
    1240                }
    1241                builder = factory.newDocumentBuilder();
    1242            } catch ( Exception ex ) {
    1243                LOG.logError( ex.getMessage(), ex );
    1244            }
    1245            return builder;
    1246        }
    1247
    1248        /**
    1249         * Returns the specified attribute value of the given node.
    1250         *
    1251         * @param node
    1252         *            current element
    1253         * @param attrName
    1254         *            local name of the attribute
    1255         *
    1256         * @return the value of the attribute or null, if it is missing
    1257         * @deprecated use
    1258         * @see #getAttrValue(Node, URI, String, String) instead
    1259         */
    1260        @Deprecated
    1261        public static String getAttrValue( Node node, String attrName ) {
    1262            NamedNodeMap atts = node.getAttributes();
    1263            if ( atts == null ) {
    1264                return null;
    1265            }
    1266            Attr a = (Attr) atts.getNamedItem( attrName );
    1267            if ( a != null ) {
    1268                return a.getValue();
    1269            }
    1270            return null;
    1271        }
    1272
    1273        /**
    1274         * Returns the specified attribute value of the given node.
    1275         *
    1276         * @param node
    1277         *            current element
    1278         * @param namespaceURI
    1279         *            namespace of the attribute
    1280         * @param attrName
    1281         *            local name of the attribute
    1282         * @param defaultVal
    1283         *            default value to be returned if attribute is nat available
    1284         *
    1285         * @return the value of the attribute or null, if it is missing
    1286         */
    1287        public static String getAttrValue( Node node, URI namespaceURI, String attrName, String defaultVal ) {
    1288            if ( node == null ) {
    1289                return null;
    1290            }
    1291            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    1292            NamedNodeMap atts = node.getAttributes();
    1293            if ( atts == null ) {
    1294                return defaultVal;
    1295            }
    1296            Attr a = null;
    1297            if ( namespace == null ) {
    1298                a = (Attr) atts.getNamedItem( attrName );
    1299            } else {
    1300                a = (Attr) atts.getNamedItemNS( namespace, attrName );
    1301            }
    1302            if ( a != null ) {
    1303                return a.getValue();
    1304            }
    1305            return defaultVal;
    1306        }
    1307
    1308        /**
    1309         * Parses an XML document and returns a DOM object. The underlying input stream is closed at the end.
    1310         *
    1311         * @param reader
    1312         *            accessing the resource to parse
    1313         * @return a DOM object, if en error occurs the response is <code>null</code>
    1314         *
    1315         * @throws IOException
    1316         * @throws SAXException
    1317         */
    1318        public static Document parse( Reader reader )
    1319                                throws IOException, SAXException {
    1320            javax.xml.parsers.DocumentBuilder parser = null;
    1321            Document doc = null;
    1322            try {
    1323                DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
    1324                fac.setNamespaceAware( true );
    1325                fac.setValidating( false );
    1326                fac.setIgnoringElementContentWhitespace( false );
    1327                fac.setValidating( false );
    1328                try {
    1329                    fac.setAttribute( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
    1330                } catch ( IllegalArgumentException _ ) {
    1331                    // ignore, we just can't set the feature
    1332                }
    1333                parser = fac.newDocumentBuilder();
    1334                doc = parser.parse( new InputSource( reader ) );
    1335            } catch ( ParserConfigurationException ex ) {
    1336                throw new IOException( "Unable to initialize DocumentBuilder: " + ex.getMessage() );
    1337            } catch ( Exception e ) {
    1338                throw new SAXException( e.getMessage() );
    1339            } finally {
    1340                reader.close();
    1341            }
    1342            return doc;
    1343        }
    1344
    1345        /**
    1346         * Parses an XML document and returns a DOM object.
    1347         *
    1348         * @deprecated
    1349         * @param is
    1350         *            accessing the resource to parse
    1351         * @return a DOM object
    1352         * @throws IOException
    1353         * @throws SAXException
    1354         */
    1355        @Deprecated
    1356        public static Document parse( InputStream is )
    1357                                throws IOException, SAXException {
    1358            return parse( new InputStreamReader( is ) );
    1359
    1360        }
    1361
    1362        /**
    1363         * Copies one node to another node.
    1364         *
    1365         * @param source
    1366         * @param dest
    1367         * @return the copied node
    1368         */
    1369        public static Node copyNode( Node source, Node dest ) {
    1370            if ( source.getNodeType() == Node.TEXT_NODE ) {
    1371                Text tn = dest.getOwnerDocument().createTextNode( getStringValue( source ) );
    1372                return tn;
    1373            }
    1374            NamedNodeMap attr = source.getAttributes();
    1375            if ( attr != null ) {
    1376                for ( int i = 0; i < attr.getLength(); i++ ) {
    1377                    ( (Element) dest ).setAttribute( attr.item( i ).getNodeName(), attr.item( i ).getNodeValue() );
    1378                }
    1379            }
    1380            NodeList list = source.getChildNodes();
    1381            for ( int i = 0; i < list.getLength(); i++ ) {
    1382                if ( !( list.item( i ) instanceof Text ) ) {
    1383                    if ( !( list.item( i ) instanceof Comment ) ) {
    1384                        Element en = dest.getOwnerDocument().createElementNS( list.item( i ).getNamespaceURI(),
    1385                                                                              list.item( i ).getNodeName() );
    1386                        if ( list.item( i ).getNodeValue() != null ) {
    1387                            en.setNodeValue( list.item( i ).getNodeValue() );
    1388                        }
    1389                        Node n = copyNode( list.item( i ), en );
    1390                        dest.appendChild( n );
    1391                    }
    1392                } else if ( ( list.item( i ) instanceof CDATASection ) ) {
    1393                    CDATASection cd = dest.getOwnerDocument().createCDATASection( list.item( i ).getNodeValue() );
    1394                    dest.appendChild( cd );
    1395                } else {
    1396                    Text tn = dest.getOwnerDocument().createTextNode( list.item( i ).getNodeValue() );
    1397                    dest.appendChild( tn );
    1398                }
    1399            }
    1400            return dest;
    1401        }
    1402
    1403        /**
    1404         * Appends a node to an element.
    1405         * <p>
    1406         * The node can be from the same document or a different one (it is automatically imported, if necessary).
    1407         *
    1408         * @param source
    1409         * @param dest
    1410         * @return the element that is appended to
    1411         */
    1412        public static Node insertNodeInto( Node source, Node dest ) {
    1413            Node n = dest.getOwnerDocument().importNode( source, true );
    1414            dest.appendChild( n );
    1415            return dest;
    1416        }
    1417
    1418        /**
    1419         * Returns the first child element of the submitted node.
    1420         *
    1421         * @param node
    1422         * @return the first child element of the submitted node.
    1423         */
    1424        public static Element getFirstChildElement( Node node ) {
    1425            NodeList nl = node.getChildNodes();
    1426            Element element = null;
    1427            if ( ( nl != null ) && ( nl.getLength() > 0 ) ) {
    1428                for ( int i = 0; i < nl.getLength(); i++ ) {
    1429                    if ( nl.item( i ) instanceof Element ) {
    1430                        element = (Element) nl.item( i );
    1431                        break;
    1432                    }
    1433                }
    1434            }
    1435            return element;
    1436        }
    1437
    1438        /**
    1439         * @deprecated Returns the first child element of the submitted node that matches the given local name.
    1440         *
    1441         * @param node
    1442         * @param name
    1443         * @return the child element
    1444         */
    1445        @Deprecated
    1446        public static Element getChildElement( Node node, String name ) {
    1447            NodeList nl = node.getChildNodes();
    1448            Element element = null;
    1449            Element childElement = null;
    1450            if ( ( nl != null ) && ( nl.getLength() > 0 ) ) {
    1451                for ( int i = 0; i < nl.getLength(); i++ ) {
    1452                    if ( nl.item( i ) instanceof Element ) {
    1453                        element = (Element) nl.item( i );
    1454
    1455                        if ( element.getNodeName().equals( name ) ) {
    1456                            childElement = element;
    1457
    1458                            break;
    1459                        }
    1460                    }
    1461                }
    1462            }
    1463            return childElement;
    1464        }
    1465
    1466        /**
    1467         * Returns all child elements of the given node.
    1468         *
    1469         * @param node
    1470         * @return all child elements of the given node.
    1471         */
    1472        public static ElementList getChildElements( Node node ) {
    1473            NodeList children = node.getChildNodes();
    1474            ElementList list = new ElementList();
    1475            for ( int i = 0; i < children.getLength(); i++ ) {
    1476                if ( children.item( i ).getNodeType() == Node.ELEMENT_NODE ) {
    1477                    list.elements.add( (Element) children.item( i ) );
    1478                }
    1479            }
    1480            return list;
    1481        }
    1482
    1483        /**
    1484         * sets the value of an existing node
    1485         *
    1486         * @param target
    1487         * @param nodeValue
    1488         */
    1489        public static void setNodeValue( Element target, String nodeValue ) {
    1490            NodeList nl = target.getChildNodes();
    1491            for ( int i = 0; i < nl.getLength(); i++ ) {
    1492                target.removeChild( nl.item( i ) );
    1493            }
    1494            Text text = target.getOwnerDocument().createTextNode( nodeValue );
    1495            target.appendChild( text );
    1496        }
    1497
    1498        /**
    1499         * Creates a new <code>Element</code> node from the given parameters and appends it to the also specified
    1500         * <code>Element</code>. Adds a text node to the newly generated <code>Element</code> as well.
    1501         *
    1502         * @param element
    1503         *            <code>Element</code> that the new <code>Element</code> is appended to
    1504         * @param namespaceURI
    1505         *            use null for default namespace
    1506         * @param name
    1507         *            qualified name
    1508         * @param nodeValue
    1509         *            value for a text node that is appended to the generated element
    1510         * @return the appended <code>Element</code> node
    1511         */
    1512        public static Element appendElement( Element element, URI namespaceURI, String name, String nodeValue ) {
    1513            String namespace = namespaceURI == null ? null : namespaceURI.toString();
    1514            Element newElement = element.getOwnerDocument().createElementNS( namespace, name );
    1515            if ( nodeValue != null && !nodeValue.equals( "" ) )
    1516                newElement.appendChild( element.getOwnerDocument().createTextNode( nodeValue ) );
    1517            element.appendChild( newElement );
    1518            return newElement;
    1519        }
    1520
    1521        /**
    1522         * @param xml
    1523         * @return the formatted string, or the original XML string if something went wrong
    1524         */
    1525        public static String getAsPrettyString( String xml ) {
    1526            try {
    1527                Element e = getStringFragmentAsElement( xml );
    1528                return new XMLFragment( e ).getAsPrettyString();
    1529            } catch ( SAXException e ) {
    1530                LOG.logError( "Unknown error", e );
    1531            } catch ( IOException e ) {
    1532                LOG.logError( "Unknown error", e );
    1533            }
    1534            return xml;
    1535        }
    1536
    1537        /**
    1538         * Converts an XML fragment string into a DOM node. The fragment should NOT contain the XML prolog. Appends ALL
    1539         * default namespace bindings for ease of use with broken fragments.
    1540         *
    1541         * @param fragment
    1542         * @return a DOM element
    1543         * @throws IOException
    1544         * @throws SAXException
    1545         */
    1546        public static Element getStringFragmentAsElement( String fragment )
    1547                                throws SAXException, IOException {
    1548            StringBuffer xml = new StringBuffer( "<?xml version=\"1.0\"?>" );
    1549
    1550            xml.append( "<bogus " );
    1551            // append ALL namespaces
    1552            Map<String, URI> map = CommonNamespaces.getNamespaceContext().getNamespaceMap();
    1553            for ( String pre : map.keySet() ) {
    1554                if ( pre.equals( "xmlns" ) ) {
    1555                    continue;
    1556                }
    1557                xml.append( "xmlns:" ).append( pre ).append( "='" );
    1558                xml.append( map.get( pre ).toString() ).append( "' " );
    1559            }
    1560            xml.append( ">" );
    1561            xml.append( fragment );
    1562            xml.append( "</bogus>" );
    1563
    1564            StringReader in = new StringReader( xml.toString() );
    1565
    1566            XMLFragment doc = new XMLFragment( in, "http://www.systemid.org" );
    1567            return (Element) doc.getRootElement().getFirstChild();
    1568        }
    1569
    1570        /**
    1571         * Works like {@link #getStringFragmentAsElement}, but does not throw exceptions and immediately imports the node
    1572         * into another document.
    1573         *
    1574         * @param fragment
    1575         * @param doc
    1576         * @return an element in the new document
    1577         */
    1578        public static Element importStringFragment( String fragment, Document doc ) {
    1579            try {
    1580                Element e = getStringFragmentAsElement( fragment );
    1581                return (Element) doc.importNode( e, true );
    1582            } catch ( SAXException e ) {
    1583                LOG.logError( "Could not convert String to XML.", e );
    1584            } catch ( IOException e ) {
    1585                LOG.logError( "Could not convert String to XML.", e );
    1586            }
    1587
    1588            return null;
    1589        }
    1590
    1591        /**
    1592         * This method escapes Strings for XML by creating a DOM document, setting the text in an attribute, exporting it to
    1593         * text and extracting the escaped string. That means that it's slow, and the method of property using DOM to create
    1594         * the XML is a million times better. But if you're forced to use StringBuffers, here you have a little helper.
    1595         *
    1596         * @param str
    1597         * @return the escaped string
    1598         */
    1599        public static String escape( String str ) {
    1600            XMLFragment doc = new XMLFragment( new QualifiedName( "dummy" ) );
    1601            doc.getRootElement().setAttribute( "dummy", str );
    1602            String s = doc.getAsString().substring( 52 );
    1603            return s.substring( 0, s.length() - 3 );
    1604        }
    1605
    1606    }