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