001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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 }