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