001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_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: apoth $ 082 * 083 * @version $Revision: 27386 $, $Date: 2010-10-19 13:13:49 +0200 (Di, 19 Okt 2010) $ 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 /** 1607 * This method escapes Strings for XML by creating a DOM document, setting the text in an attribute, exporting it to 1608 * text and extracting the escaped string. That means that it's slow, and the method of property using DOM to create 1609 * the XML is a million times better. But if you're forced to use StringBuffers, here you have a little helper. 1610 * 1611 * @param str 1612 * @return the escaped string 1613 */ 1614 public static String escape( Node root) { 1615 return new XMLFragment( (Element)root ).getAsString(); 1616 } 1617 1618 }