1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 22 23 // Sep 14, 2000: 24 // Fixed problem with namespace handling. Contributed by 25 // David Blondeau <blondeau@intalio.com> 26 // Sep 14, 2000: 27 // Fixed serializer to report IO exception directly, instead at 28 // the end of document processing. 29 // Reported by Patrick Higgins <phiggins@transzap.com> 30 // Aug 21, 2000: 31 // Fixed bug in startDocument not calling prepare. 32 // Reported by Mikael Staldal <d96-mst-ingen-reklam@d.kth.se> 33 // Aug 21, 2000: 34 // Added ability to omit DOCTYPE declaration. 35 36 37 package com.sun.org.apache.xml.internal.serialize; 38 39 import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter; 40 import com.sun.org.apache.xerces.internal.util.NamespaceSupport; 41 import com.sun.org.apache.xerces.internal.util.SymbolTable; 42 import com.sun.org.apache.xerces.internal.util.XMLChar; 43 import com.sun.org.apache.xerces.internal.util.XMLSymbols; 44 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 45 import java.io.IOException; 46 import java.io.OutputStream; 47 import java.io.Writer; 48 import java.util.Map; 49 import org.w3c.dom.Attr; 50 import org.w3c.dom.DOMError; 51 import org.w3c.dom.Element; 52 import org.w3c.dom.NamedNodeMap; 53 import org.w3c.dom.Node; 54 import org.w3c.dom.traversal.NodeFilter; 55 import org.xml.sax.AttributeList; 56 import org.xml.sax.Attributes; 57 import org.xml.sax.SAXException; 58 import org.xml.sax.helpers.AttributesImpl; 59 60 /** 61 * Implements an XML serializer supporting both DOM and SAX pretty 62 * serializing. For usage instructions see {@link Serializer}. 63 * <p> 64 * If an output stream is used, the encoding is taken from the 65 * output format (defaults to <tt>UTF-8</tt>). If a writer is 66 * used, make sure the writer uses the same encoding (if applies) 67 * as specified in the output format. 68 * <p> 69 * The serializer supports both DOM and SAX. SAX serializing is done by firing 70 * SAX events and using the serializer as a document handler. DOM serializing is done 71 * by calling {@link #serialize(Document)} or by using DOM Level 3 72 * {@link org.w3c.dom.ls.DOMSerializer} and 73 * serializing with {@link org.w3c.dom.ls.DOMSerializer#write}, 74 * {@link org.w3c.dom.ls.DOMSerializer#writeToString}. 75 * <p> 76 * If an I/O exception occurs while serializing, the serializer 77 * will not throw an exception directly, but only throw it 78 * at the end of serializing (either DOM or SAX's {@link 79 * org.xml.sax.DocumentHandler#endDocument}. 80 * <p> 81 * For elements that are not specified as whitespace preserving, 82 * the serializer will potentially break long text lines at space 83 * boundaries, indent lines, and serialize elements on separate 84 * lines. Line terminators will be regarded as spaces, and 85 * spaces at beginning of line will be stripped. 86 * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a> 87 * @author <a href="mailto:rahul.srivastava@sun.com">Rahul Srivastava</a> 88 * @author Elena Litani IBM 89 * @see Serializer 90 */ 91 public class XMLSerializer 92 extends BaseMarkupSerializer { 93 94 // 95 // constants 96 // 97 98 protected static final boolean DEBUG = false; 99 100 // 101 // data 102 // 103 104 // 105 // DOM Level 3 implementation: variables intialized in DOMSerializerImpl 106 // 107 108 /** stores namespaces in scope */ 109 protected NamespaceSupport fNSBinder; 110 111 /** stores all namespace bindings on the current element */ 112 protected NamespaceSupport fLocalNSBinder; 113 114 /** symbol table for serialization */ 115 protected SymbolTable fSymbolTable; 116 117 protected final static String PREFIX = "NS"; 118 119 /** 120 * Controls whether namespace fixup should be performed during 121 * the serialization. 122 * NOTE: if this field is set to true the following 123 * fields need to be initialized: fNSBinder, fLocalNSBinder, fSymbolTable, 124 * XMLSymbols.EMPTY_STRING, fXmlSymbol, fXmlnsSymbol 125 */ 126 protected boolean fNamespaces = false; 127 128 /** 129 * Controls whether namespace prefixes will be printed out during serialization 130 */ 131 protected boolean fNamespacePrefixes = true; 132 133 134 private boolean fPreserveSpace; 135 136 137 /** 138 * Constructs a new serializer. The serializer cannot be used without 139 * calling {@link #setOutputCharStream} or {@link #setOutputByteStream} 140 * first. 141 */ XMLSerializer()142 public XMLSerializer() { 143 super( new OutputFormat( Method.XML, null, false ) ); 144 } 145 146 147 /** 148 * Constructs a new serializer. The serializer cannot be used without 149 * calling {@link #setOutputCharStream} or {@link #setOutputByteStream} 150 * first. 151 */ XMLSerializer( OutputFormat format )152 public XMLSerializer( OutputFormat format ) { 153 super( format != null ? format : new OutputFormat( Method.XML, null, false ) ); 154 _format.setMethod( Method.XML ); 155 } 156 157 158 /** 159 * Constructs a new serializer that writes to the specified writer 160 * using the specified output format. If <tt>format</tt> is null, 161 * will use a default output format. 162 * 163 * @param writer The writer to use 164 * @param format The output format to use, null for the default 165 */ XMLSerializer( Writer writer, OutputFormat format )166 public XMLSerializer( Writer writer, OutputFormat format ) { 167 super( format != null ? format : new OutputFormat( Method.XML, null, false ) ); 168 _format.setMethod( Method.XML ); 169 setOutputCharStream( writer ); 170 } 171 172 173 /** 174 * Constructs a new serializer that writes to the specified output 175 * stream using the specified output format. If <tt>format</tt> 176 * is null, will use a default output format. 177 * 178 * @param output The output stream to use 179 * @param format The output format to use, null for the default 180 */ XMLSerializer( OutputStream output, OutputFormat format )181 public XMLSerializer( OutputStream output, OutputFormat format ) { 182 super( format != null ? format : new OutputFormat( Method.XML, null, false ) ); 183 _format.setMethod( Method.XML ); 184 setOutputByteStream( output ); 185 } 186 187 setOutputFormat( OutputFormat format )188 public void setOutputFormat( OutputFormat format ) { 189 super.setOutputFormat( format != null ? format : new OutputFormat( Method.XML, null, false ) ); 190 } 191 192 193 /** 194 * This methods turns on namespace fixup algorithm during 195 * DOM serialization. 196 * @see org.w3c.dom.ls.DOMSerializer 197 * 198 * @param namespaces 199 */ setNamespaces(boolean namespaces)200 public void setNamespaces (boolean namespaces){ 201 fNamespaces = namespaces; 202 if (fNSBinder == null) { 203 fNSBinder = new NamespaceSupport(); 204 fLocalNSBinder = new NamespaceSupport(); 205 fSymbolTable = new SymbolTable(); 206 } 207 } 208 209 //-----------------------------------------// 210 // SAX content handler serializing methods // 211 //-----------------------------------------// 212 213 startElement( String namespaceURI, String localName, String rawName, Attributes attrs )214 public void startElement( String namespaceURI, String localName, 215 String rawName, Attributes attrs ) 216 throws SAXException 217 { 218 int i; 219 boolean preserveSpace; 220 ElementState state; 221 String name; 222 String value; 223 boolean addNSAttr = false; 224 225 if (DEBUG) { 226 System.out.println("==>startElement("+namespaceURI+","+localName+ 227 ","+rawName+")"); 228 } 229 230 try { 231 if (_printer == null) { 232 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null); 233 throw new IllegalStateException(msg); 234 } 235 236 state = getElementState(); 237 if (isDocumentState()) { 238 // If this is the root element handle it differently. 239 // If the first root element in the document, serialize 240 // the document's DOCTYPE. Space preserving defaults 241 // to that of the output format. 242 if (! _started) 243 startDocument( ( localName == null || localName.length() == 0 ) ? rawName : localName ); 244 } else { 245 // For any other element, if first in parent, then 246 // close parent's opening tag and use the parnet's 247 // space preserving. 248 if (state.empty) 249 _printer.printText( '>' ); 250 // Must leave CData section first 251 if (state.inCData) { 252 _printer.printText( "]]>" ); 253 state.inCData = false; 254 } 255 // Indent this element on a new line if the first 256 // content of the parent element or immediately 257 // following an element or a comment 258 if (_indenting && ! state.preserveSpace && 259 ( state.empty || state.afterElement || state.afterComment)) 260 _printer.breakLine(); 261 } 262 preserveSpace = state.preserveSpace; 263 264 //We remove the namespaces from the attributes list so that they will 265 //be in _prefixes 266 attrs = extractNamespaces(attrs); 267 268 // Do not change the current element state yet. 269 // This only happens in endElement(). 270 if (rawName == null || rawName.length() == 0) { 271 if (localName == null) { 272 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoName", null); 273 throw new SAXException(msg); 274 } 275 if (namespaceURI != null && ! namespaceURI.equals( "" )) { 276 String prefix; 277 prefix = getPrefix( namespaceURI ); 278 if (prefix != null && prefix.length() > 0) 279 rawName = prefix + ":" + localName; 280 else 281 rawName = localName; 282 } else 283 rawName = localName; 284 addNSAttr = true; 285 } 286 287 _printer.printText( '<' ); 288 _printer.printText( rawName ); 289 _printer.indent(); 290 291 // For each attribute print it's name and value as one part, 292 // separated with a space so the element can be broken on 293 // multiple lines. 294 if (attrs != null) { 295 for (i = 0 ; i < attrs.getLength() ; ++i) { 296 _printer.printSpace(); 297 298 name = attrs.getQName( i ); 299 if (name != null && name.length() == 0) { 300 String prefix; 301 String attrURI; 302 303 name = attrs.getLocalName( i ); 304 attrURI = attrs.getURI( i ); 305 if (( attrURI != null && attrURI.length() != 0 ) && 306 ( namespaceURI == null || namespaceURI.length() == 0 || 307 ! attrURI.equals( namespaceURI ) )) { 308 prefix = getPrefix( attrURI ); 309 if (prefix != null && prefix.length() > 0) 310 name = prefix + ":" + name; 311 } 312 } 313 314 value = attrs.getValue( i ); 315 if (value == null) 316 value = ""; 317 _printer.printText( name ); 318 _printer.printText( "=\"" ); 319 printEscaped( value ); 320 _printer.printText( '"' ); 321 322 // If the attribute xml:space exists, determine whether 323 // to preserve spaces in this and child nodes based on 324 // its value. 325 if (name.equals( "xml:space" )) { 326 if (value.equals( "preserve" )) 327 preserveSpace = true; 328 else 329 preserveSpace = _format.getPreserveSpace(); 330 } 331 } 332 } 333 334 if (_prefixes != null) { 335 for (Map.Entry<String, String> entry : _prefixes.entrySet()) { 336 _printer.printSpace(); 337 value = entry.getKey(); //The prefixes map uses the URI value as key. 338 name = entry.getValue(); //and prefix name as value 339 if (name.length() == 0) { 340 _printer.printText( "xmlns=\"" ); 341 printEscaped( value ); 342 _printer.printText( '"' ); 343 } else { 344 _printer.printText( "xmlns:" ); 345 _printer.printText( name ); 346 _printer.printText( "=\"" ); 347 printEscaped( value ); 348 _printer.printText( '"' ); 349 } 350 } 351 } 352 353 // Now it's time to enter a new element state 354 // with the tag name and space preserving. 355 // We still do not change the curent element state. 356 state = enterElementState( namespaceURI, localName, rawName, preserveSpace ); 357 name = ( localName == null || localName.length() == 0 ) ? rawName : namespaceURI + "^" + localName; 358 state.doCData = _format.isCDataElement( name ); 359 state.unescaped = _format.isNonEscapingElement( name ); 360 } catch (IOException except) { 361 throw new SAXException( except ); 362 } 363 } 364 365 endElement( String namespaceURI, String localName, String rawName )366 public void endElement( String namespaceURI, String localName, 367 String rawName ) 368 throws SAXException 369 { 370 try { 371 endElementIO( namespaceURI, localName, rawName ); 372 } catch (IOException except) { 373 throw new SAXException( except ); 374 } 375 } 376 377 endElementIO( String namespaceURI, String localName, String rawName )378 public void endElementIO( String namespaceURI, String localName, 379 String rawName ) 380 throws IOException 381 { 382 ElementState state; 383 if (DEBUG) { 384 System.out.println("==>endElement: " +rawName); 385 } 386 // Works much like content() with additions for closing 387 // an element. Note the different checks for the closed 388 // element's state and the parent element's state. 389 _printer.unindent(); 390 state = getElementState(); 391 if (state.empty) { 392 _printer.printText( "/>" ); 393 } else { 394 // Must leave CData section first 395 if (state.inCData) 396 _printer.printText( "]]>" ); 397 // This element is not empty and that last content was 398 // another element, so print a line break before that 399 // last element and this element's closing tag. 400 if (_indenting && ! state.preserveSpace && (state.afterElement || state.afterComment)) 401 _printer.breakLine(); 402 _printer.printText( "</" ); 403 _printer.printText( state.rawName ); 404 _printer.printText( '>' ); 405 } 406 // Leave the element state and update that of the parent 407 // (if we're not root) to not empty and after element. 408 state = leaveElementState(); 409 state.afterElement = true; 410 state.afterComment = false; 411 state.empty = false; 412 if (isDocumentState()) 413 _printer.flush(); 414 } 415 416 417 //------------------------------------------// 418 // SAX document handler serializing methods // 419 //------------------------------------------// 420 421 startElement( String tagName, AttributeList attrs )422 public void startElement( String tagName, AttributeList attrs ) 423 throws SAXException 424 { 425 int i; 426 boolean preserveSpace; 427 ElementState state; 428 String name; 429 String value; 430 431 432 if (DEBUG) { 433 System.out.println("==>startElement("+tagName+")"); 434 } 435 436 try { 437 if (_printer == null) { 438 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.SERIALIZER_DOMAIN, "NoWriterSupplied", null); 439 throw new IllegalStateException(msg); 440 } 441 442 state = getElementState(); 443 if (isDocumentState()) { 444 // If this is the root element handle it differently. 445 // If the first root element in the document, serialize 446 // the document's DOCTYPE. Space preserving defaults 447 // to that of the output format. 448 if (! _started) 449 startDocument( tagName ); 450 } else { 451 // For any other element, if first in parent, then 452 // close parent's opening tag and use the parnet's 453 // space preserving. 454 if (state.empty) 455 _printer.printText( '>' ); 456 // Must leave CData section first 457 if (state.inCData) { 458 _printer.printText( "]]>" ); 459 state.inCData = false; 460 } 461 // Indent this element on a new line if the first 462 // content of the parent element or immediately 463 // following an element. 464 if (_indenting && ! state.preserveSpace && 465 ( state.empty || state.afterElement || state.afterComment)) 466 _printer.breakLine(); 467 } 468 preserveSpace = state.preserveSpace; 469 470 // Do not change the current element state yet. 471 // This only happens in endElement(). 472 473 _printer.printText( '<' ); 474 _printer.printText( tagName ); 475 _printer.indent(); 476 477 // For each attribute print it's name and value as one part, 478 // separated with a space so the element can be broken on 479 // multiple lines. 480 if (attrs != null) { 481 for (i = 0 ; i < attrs.getLength() ; ++i) { 482 _printer.printSpace(); 483 name = attrs.getName( i ); 484 value = attrs.getValue( i ); 485 if (value != null) { 486 _printer.printText( name ); 487 _printer.printText( "=\"" ); 488 printEscaped( value ); 489 _printer.printText( '"' ); 490 } 491 492 // If the attribute xml:space exists, determine whether 493 // to preserve spaces in this and child nodes based on 494 // its value. 495 if (name.equals( "xml:space" )) { 496 if (value.equals( "preserve" )) 497 preserveSpace = true; 498 else 499 preserveSpace = _format.getPreserveSpace(); 500 } 501 } 502 } 503 // Now it's time to enter a new element state 504 // with the tag name and space preserving. 505 // We still do not change the curent element state. 506 state = enterElementState( null, null, tagName, preserveSpace ); 507 state.doCData = _format.isCDataElement( tagName ); 508 state.unescaped = _format.isNonEscapingElement( tagName ); 509 } catch (IOException except) { 510 throw new SAXException( except ); 511 } 512 513 } 514 515 endElement( String tagName )516 public void endElement( String tagName ) 517 throws SAXException 518 { 519 endElement( null, null, tagName ); 520 } 521 522 523 524 //------------------------------------------// 525 // Generic node serializing methods methods // 526 //------------------------------------------// 527 528 529 /** 530 * Called to serialize the document's DOCTYPE by the root element. 531 * The document type declaration must name the root element, 532 * but the root element is only known when that element is serialized, 533 * and not at the start of the document. 534 * <p> 535 * This method will check if it has not been called before ({@link #_started}), 536 * will serialize the document type declaration, and will serialize all 537 * pre-root comments and PIs that were accumulated in the document 538 * (see {@link #serializePreRoot}). Pre-root will be serialized even if 539 * this is not the first root element of the document. 540 */ startDocument( String rootTagName )541 protected void startDocument( String rootTagName ) 542 throws IOException 543 { 544 int i; 545 String dtd; 546 547 dtd = _printer.leaveDTD(); 548 if (! _started) { 549 550 if (! _format.getOmitXMLDeclaration()) { 551 StringBuffer buffer; 552 553 // Serialize the document declaration appreaing at the head 554 // of very XML document (unless asked not to). 555 buffer = new StringBuffer( "<?xml version=\"" ); 556 if (_format.getVersion() != null) 557 buffer.append( _format.getVersion() ); 558 else 559 buffer.append( "1.0" ); 560 buffer.append( '"' ); 561 String format_encoding = _format.getEncoding(); 562 if (format_encoding != null) { 563 buffer.append( " encoding=\"" ); 564 buffer.append( format_encoding ); 565 buffer.append( '"' ); 566 } 567 if (_format.getStandalone() && _docTypeSystemId == null && 568 _docTypePublicId == null) 569 buffer.append( " standalone=\"yes\"" ); 570 buffer.append( "?>" ); 571 _printer.printText( buffer ); 572 _printer.breakLine(); 573 } 574 575 if (! _format.getOmitDocumentType()) { 576 if (_docTypeSystemId != null) { 577 // System identifier must be specified to print DOCTYPE. 578 // If public identifier is specified print 'PUBLIC 579 // <public> <system>', if not, print 'SYSTEM <system>'. 580 _printer.printText( "<!DOCTYPE " ); 581 _printer.printText( rootTagName ); 582 if (_docTypePublicId != null) { 583 _printer.printText( " PUBLIC " ); 584 printDoctypeURL( _docTypePublicId ); 585 if (_indenting) { 586 _printer.breakLine(); 587 for (i = 0 ; i < 18 + rootTagName.length() ; ++i) 588 _printer.printText( " " ); 589 } else 590 _printer.printText( " " ); 591 printDoctypeURL( _docTypeSystemId ); 592 } else { 593 _printer.printText( " SYSTEM " ); 594 printDoctypeURL( _docTypeSystemId ); 595 } 596 597 // If we accumulated any DTD contents while printing. 598 // this would be the place to print it. 599 if (dtd != null && dtd.length() > 0) { 600 _printer.printText( " [" ); 601 printText( dtd, true, true ); 602 _printer.printText( ']' ); 603 } 604 605 _printer.printText( ">" ); 606 _printer.breakLine(); 607 } else if (dtd != null && dtd.length() > 0) { 608 _printer.printText( "<!DOCTYPE " ); 609 _printer.printText( rootTagName ); 610 _printer.printText( " [" ); 611 printText( dtd, true, true ); 612 _printer.printText( "]>" ); 613 _printer.breakLine(); 614 } 615 } 616 } 617 _started = true; 618 // Always serialize these, even if not te first root element. 619 serializePreRoot(); 620 } 621 622 623 /** 624 * Called to serialize a DOM element. Equivalent to calling {@link 625 * #startElement}, {@link #endElement} and serializing everything 626 * inbetween, but better optimized. 627 */ serializeElement( Element elem )628 protected void serializeElement( Element elem ) 629 throws IOException 630 { 631 Attr attr; 632 NamedNodeMap attrMap; 633 int i; 634 Node child; 635 ElementState state; 636 String name; 637 String value; 638 String tagName; 639 640 String prefix, localUri; 641 String uri; 642 if (fNamespaces) { 643 // local binder stores namespace declaration 644 // that has been printed out during namespace fixup of 645 // the current element 646 fLocalNSBinder.reset(); 647 648 // add new namespace context 649 fNSBinder.pushContext(); 650 } 651 652 if (DEBUG) { 653 System.out.println("==>startElement: " +elem.getNodeName() +" ns="+elem.getNamespaceURI()); 654 } 655 tagName = elem.getTagName(); 656 state = getElementState(); 657 if (isDocumentState()) { 658 // If this is the root element handle it differently. 659 // If the first root element in the document, serialize 660 // the document's DOCTYPE. Space preserving defaults 661 // to that of the output format. 662 663 if (! _started) { 664 startDocument( tagName); 665 } 666 } else { 667 // For any other element, if first in parent, then 668 // close parent's opening tag and use the parent's 669 // space preserving. 670 if (state.empty) 671 _printer.printText( '>' ); 672 // Must leave CData section first 673 if (state.inCData) { 674 _printer.printText( "]]>" ); 675 state.inCData = false; 676 } 677 // Indent this element on a new line if the first 678 // content of the parent element or immediately 679 // following an element. 680 if (_indenting && ! state.preserveSpace && 681 ( state.empty || state.afterElement || state.afterComment)) 682 _printer.breakLine(); 683 } 684 685 // Do not change the current element state yet. 686 // This only happens in endElement(). 687 fPreserveSpace = state.preserveSpace; 688 689 690 int length = 0; 691 attrMap = null; 692 // retrieve attributes 693 if (elem.hasAttributes()) { 694 attrMap = elem.getAttributes(); 695 length = attrMap.getLength(); 696 } 697 698 if (!fNamespaces) { // no namespace fixup should be performed 699 700 // serialize element name 701 _printer.printText( '<' ); 702 _printer.printText( tagName ); 703 _printer.indent(); 704 705 // For each attribute print it's name and value as one part, 706 // separated with a space so the element can be broken on 707 // multiple lines. 708 for ( i = 0 ; i < length ; ++i ) { 709 attr = (Attr) attrMap.item( i ); 710 name = attr.getName(); 711 value = attr.getValue(); 712 if ( value == null ) 713 value = ""; 714 printAttribute (name, value, attr.getSpecified(), attr); 715 } 716 } else { // do namespace fixup 717 718 // REVISIT: some optimization could probably be done to avoid traversing 719 // attributes twice. 720 // 721 722 // --------------------------------------- 723 // record all valid namespace declarations 724 // before attempting to fix element's namespace 725 // --------------------------------------- 726 727 for (i = 0;i < length;i++) { 728 729 attr = (Attr) attrMap.item( i ); 730 uri = attr.getNamespaceURI(); 731 // check if attribute is a namespace decl 732 if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) { 733 734 value = attr.getNodeValue(); 735 if (value == null) { 736 value=XMLSymbols.EMPTY_STRING; 737 } 738 739 if (value.equals(NamespaceContext.XMLNS_URI)) { 740 if (fDOMErrorHandler != null) { 741 String msg = DOMMessageFormatter.formatMessage( 742 DOMMessageFormatter.XML_DOMAIN,"CantBindXMLNS",null ); 743 modifyDOMError(msg, DOMError.SEVERITY_ERROR, null, attr); 744 boolean continueProcess = fDOMErrorHandler.handleError(fDOMError); 745 if (!continueProcess) { 746 // stop the namespace fixup and validation 747 throw new RuntimeException( 748 DOMMessageFormatter.formatMessage( 749 DOMMessageFormatter.SERIALIZER_DOMAIN, 750 "SerializationStopped", null)); 751 } 752 } 753 } else { 754 prefix = attr.getPrefix(); 755 prefix = (prefix == null || 756 prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); 757 String localpart = fSymbolTable.addSymbol( attr.getLocalName()); 758 if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix 759 value = fSymbolTable.addSymbol(value); 760 // record valid decl 761 if (value.length() != 0) { 762 fNSBinder.declarePrefix(localpart, value); 763 } else { 764 // REVISIT: issue error on invalid declarations 765 // xmlns:foo = "" 766 } 767 continue; 768 } else { // xmlns 769 // empty prefix is always bound ("" or some string) 770 771 value = fSymbolTable.addSymbol(value); 772 fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, value); 773 continue; 774 } 775 } // end-else: valid declaration 776 } // end-if: namespace declaration 777 } // end-for 778 779 //----------------------- 780 // get element uri/prefix 781 //----------------------- 782 uri = elem.getNamespaceURI(); 783 prefix = elem.getPrefix(); 784 785 //---------------------- 786 // output element name 787 //---------------------- 788 // REVISIT: this could be removed if we always convert empty string to null 789 // for the namespaces. 790 if ((uri !=null && prefix !=null ) && uri.length() == 0 && prefix.length()!=0) { 791 // uri is an empty string and element has some prefix 792 // the namespace alg later will fix up the namespace attributes 793 // remove element prefix 794 prefix = null; 795 _printer.printText( '<' ); 796 _printer.printText( elem.getLocalName() ); 797 _printer.indent(); 798 } else { 799 _printer.printText( '<' ); 800 _printer.printText( tagName ); 801 _printer.indent(); 802 } 803 804 805 // --------------------------------------------------------- 806 // Fix up namespaces for element: per DOM L3 807 // Need to consider the following cases: 808 // 809 // case 1: <foo:elem xmlns:ns1="myURI" xmlns="default"/> 810 // Assume "foo", "ns1" are declared on the parent. We should not miss 811 // redeclaration for both "ns1" and default namespace. To solve this 812 // we add a local binder that stores declaration only for current element. 813 // This way we avoid outputing duplicate declarations for the same element 814 // as well as we are not omitting redeclarations. 815 // 816 // case 2: <elem xmlns="" xmlns="default"/> 817 // We need to bind default namespace to empty string, to be able to 818 // omit duplicate declarations for the same element 819 // 820 // case 3: <xsl:stylesheet xmlns:xsl="http://xsl"> 821 // We create another element body bound to the "http://xsl" namespace 822 // as well as namespace attribute rebounding xsl to another namespace. 823 // <xsl:body xmlns:xsl="http://another"> 824 // Need to make sure that the new namespace decl value is changed to 825 // "http://xsl" 826 // 827 // --------------------------------------------------------- 828 // check if prefix/namespace is correct for current element 829 // --------------------------------------------------------- 830 831 832 if (uri != null) { // Element has a namespace 833 uri = fSymbolTable.addSymbol(uri); 834 prefix = (prefix == null || 835 prefix.length() == 0) ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); 836 if (fNSBinder.getURI(prefix) == uri) { 837 // The xmlns:prefix=namespace or xmlns="default" was declared at parent. 838 // The binder always stores mapping of empty prefix to "". 839 // (NOTE: local binder does not store this kind of binding!) 840 // Thus the case where element was declared with uri="" (with or without a prefix) 841 // will be covered here. 842 843 } else { 844 // the prefix is either undeclared 845 // or 846 // conflict: the prefix is bound to another URI 847 if (fNamespacePrefixes) { 848 printNamespaceAttr(prefix, uri); 849 } 850 fLocalNSBinder.declarePrefix(prefix, uri); 851 fNSBinder.declarePrefix(prefix, uri); 852 } 853 } else { // Element has no namespace 854 if (elem.getLocalName() == null) { 855 // DOM Level 1 node! 856 if (fDOMErrorHandler != null) { 857 String msg = DOMMessageFormatter.formatMessage( 858 DOMMessageFormatter.DOM_DOMAIN, "NullLocalElementName", 859 new Object[]{elem.getNodeName()}); 860 modifyDOMError(msg,DOMError.SEVERITY_ERROR, null, elem); 861 boolean continueProcess = fDOMErrorHandler.handleError(fDOMError); 862 // REVISIT: should we terminate upon request? 863 if (!continueProcess) { 864 throw new RuntimeException( 865 DOMMessageFormatter.formatMessage( 866 DOMMessageFormatter.SERIALIZER_DOMAIN, 867 "SerializationStopped", null)); 868 } 869 } 870 } else { // uri=null and no colon (DOM L2 node) 871 uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING); 872 873 if (uri !=null && uri.length() > 0) { 874 // there is a default namespace decl that is bound to 875 // non-zero length uri, output xmlns="" 876 if (fNamespacePrefixes) { 877 printNamespaceAttr(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); 878 } 879 fLocalNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); 880 fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); 881 } 882 } 883 } 884 885 886 // ----------------------------------------- 887 // Fix up namespaces for attributes: per DOM L3 888 // check if prefix/namespace is correct the attributes 889 // ----------------------------------------- 890 891 for (i = 0; i < length; i++) { 892 893 attr = (Attr) attrMap.item( i ); 894 value = attr.getValue(); 895 name = attr.getNodeName(); 896 897 uri = attr.getNamespaceURI(); 898 899 // Fix attribute that was declared with a prefix and namespace="" 900 if (uri !=null && uri.length() == 0) { 901 uri=null; 902 // we must remove prefix for this attribute 903 name=attr.getLocalName(); 904 } 905 906 if (DEBUG) { 907 System.out.println("==>process attribute: "+attr.getNodeName()); 908 } 909 // make sure that value is never null. 910 if (value == null) { 911 value=XMLSymbols.EMPTY_STRING; 912 } 913 914 if (uri != null) { // attribute has namespace !=null 915 prefix = attr.getPrefix(); 916 prefix = prefix == null ? XMLSymbols.EMPTY_STRING :fSymbolTable.addSymbol(prefix); 917 String localpart = fSymbolTable.addSymbol( attr.getLocalName()); 918 919 920 921 // --------------------------------------------------- 922 // print namespace declarations namespace declarations 923 // --------------------------------------------------- 924 if (uri != null && uri.equals(NamespaceContext.XMLNS_URI)) { 925 // check if we need to output this declaration 926 prefix = attr.getPrefix(); 927 prefix = (prefix == null || 928 prefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(prefix); 929 localpart = fSymbolTable.addSymbol( attr.getLocalName()); 930 if (prefix == XMLSymbols.PREFIX_XMLNS) { //xmlns:prefix 931 localUri = fLocalNSBinder.getURI(localpart); // local prefix mapping 932 value = fSymbolTable.addSymbol(value); 933 if (value.length() != 0 ) { 934 if (localUri == null) { 935 // declaration was not printed while fixing element namespace binding 936 937 // If the DOM Level 3 namespace-prefixes feature is set to false 938 // do not print xmlns attributes 939 if (fNamespacePrefixes) { 940 printNamespaceAttr(localpart, value); 941 } 942 943 // case 4: <elem xmlns:xx="foo" xx:attr=""/> 944 // where attribute is bound to "bar". 945 // If the xmlns:xx is output here first, later we should not 946 // redeclare "xx" prefix. Instead we would pick up different prefix 947 // for the attribute. 948 // final: <elem xmlns:xx="foo" NS1:attr="" xmlns:NS1="bar"/> 949 fLocalNSBinder.declarePrefix(localpart, value); 950 } 951 } else { 952 // REVISIT: issue error on invalid declarations 953 // xmlns:foo = "" 954 } 955 continue; 956 } else { // xmlns 957 // empty prefix is always bound ("" or some string) 958 959 uri = fNSBinder.getURI(XMLSymbols.EMPTY_STRING); 960 localUri=fLocalNSBinder.getURI(XMLSymbols.EMPTY_STRING); 961 value = fSymbolTable.addSymbol(value); 962 if (localUri == null ){ 963 // declaration was not printed while fixing element namespace binding 964 if (fNamespacePrefixes) { 965 printNamespaceAttr(XMLSymbols.EMPTY_STRING, value); 966 } 967 // case 4 does not apply here since attributes can't use 968 // default namespace 969 } 970 continue; 971 } 972 973 } 974 uri = fSymbolTable.addSymbol(uri); 975 976 // find if for this prefix a URI was already declared 977 String declaredURI = fNSBinder.getURI(prefix); 978 979 if (prefix == XMLSymbols.EMPTY_STRING || declaredURI != uri) { 980 // attribute has no prefix (default namespace decl does not apply to attributes) 981 // OR 982 // attribute prefix is not declared 983 // OR 984 // conflict: attr URI does not match the prefix in scope 985 986 name = attr.getNodeName(); 987 // Find if any prefix for attributes namespace URI is available 988 // in the scope 989 String declaredPrefix = fNSBinder.getPrefix(uri); 990 991 if (declaredPrefix !=null && declaredPrefix !=XMLSymbols.EMPTY_STRING) { 992 // use the prefix that was found 993 prefix = declaredPrefix; 994 name=prefix+":"+localpart; 995 } else { 996 if (DEBUG) { 997 System.out.println("==> cound not find prefix for the attribute: " +prefix); 998 } 999 1000 if (prefix != XMLSymbols.EMPTY_STRING && fLocalNSBinder.getURI(prefix) == null) { 1001 // the current prefix is not null and it has no in scope declaration 1002 1003 // use this prefix 1004 } else { 1005 // find a prefix following the pattern "NS" +index (starting at 1) 1006 // make sure this prefix is not declared in the current scope. 1007 int counter = 1; 1008 prefix = fSymbolTable.addSymbol(PREFIX + counter++); 1009 while (fLocalNSBinder.getURI(prefix)!=null) { 1010 prefix = fSymbolTable.addSymbol(PREFIX +counter++); 1011 } 1012 name=prefix+":"+localpart; 1013 } 1014 // add declaration for the new prefix 1015 if (fNamespacePrefixes) { 1016 printNamespaceAttr(prefix, uri); 1017 } 1018 value = fSymbolTable.addSymbol(value); 1019 fLocalNSBinder.declarePrefix(prefix, value); 1020 fNSBinder.declarePrefix(prefix, uri); 1021 } 1022 1023 // change prefix for this attribute 1024 } 1025 1026 printAttribute (name, (value==null)?XMLSymbols.EMPTY_STRING:value, attr.getSpecified(), attr); 1027 } else { // attribute uri == null 1028 if (attr.getLocalName() == null) { 1029 if (fDOMErrorHandler != null) { 1030 String msg = DOMMessageFormatter.formatMessage( 1031 DOMMessageFormatter.DOM_DOMAIN, 1032 "NullLocalAttrName", new Object[]{attr.getNodeName()}); 1033 modifyDOMError(msg, DOMError.SEVERITY_ERROR, null, attr); 1034 boolean continueProcess = fDOMErrorHandler.handleError(fDOMError); 1035 if (!continueProcess) { 1036 // stop the namespace fixup and validation 1037 throw new RuntimeException( 1038 DOMMessageFormatter.formatMessage( 1039 DOMMessageFormatter.SERIALIZER_DOMAIN, 1040 "SerializationStopped", null)); 1041 } 1042 } 1043 printAttribute (name, value, attr.getSpecified(), attr); 1044 } else { // uri=null and no colon 1045 1046 // no fix up is needed: default namespace decl does not 1047 // apply to attributes 1048 printAttribute (name, value, attr.getSpecified(), attr); 1049 } 1050 } 1051 } // end loop for attributes 1052 1053 }// end namespace fixup algorithm 1054 1055 1056 // If element has children, then serialize them, otherwise 1057 // serialize en empty tag. 1058 if (elem.hasChildNodes()) { 1059 // Enter an element state, and serialize the children 1060 // one by one. Finally, end the element. 1061 state = enterElementState( null, null, tagName, fPreserveSpace ); 1062 state.doCData = _format.isCDataElement( tagName ); 1063 state.unescaped = _format.isNonEscapingElement( tagName ); 1064 child = elem.getFirstChild(); 1065 while (child != null) { 1066 serializeNode( child ); 1067 child = child.getNextSibling(); 1068 } 1069 if (fNamespaces) { 1070 fNSBinder.popContext(); 1071 } 1072 endElementIO( null, null, tagName ); 1073 } else { 1074 if (DEBUG) { 1075 System.out.println("==>endElement: " +elem.getNodeName()); 1076 } 1077 if (fNamespaces) { 1078 fNSBinder.popContext(); 1079 } 1080 _printer.unindent(); 1081 _printer.printText( "/>" ); 1082 // After element but parent element is no longer empty. 1083 state.afterElement = true; 1084 state.afterComment = false; 1085 state.empty = false; 1086 if (isDocumentState()) 1087 _printer.flush(); 1088 } 1089 } 1090 1091 1092 1093 /** 1094 * Serializes a namespace attribute with the given prefix and value for URI. 1095 * In case prefix is empty will serialize default namespace declaration. 1096 * 1097 * @param prefix 1098 * @param uri 1099 * @exception IOException 1100 */ 1101 printNamespaceAttr(String prefix, String uri)1102 private void printNamespaceAttr(String prefix, String uri) throws IOException{ 1103 _printer.printSpace(); 1104 if (prefix == XMLSymbols.EMPTY_STRING) { 1105 if (DEBUG) { 1106 System.out.println("=>add xmlns=\""+uri+"\" declaration"); 1107 } 1108 _printer.printText( XMLSymbols.PREFIX_XMLNS ); 1109 } else { 1110 if (DEBUG) { 1111 System.out.println("=>add xmlns:"+prefix+"=\""+uri+"\" declaration"); 1112 } 1113 _printer.printText( "xmlns:"+prefix ); 1114 } 1115 _printer.printText( "=\"" ); 1116 printEscaped( uri ); 1117 _printer.printText( '"' ); 1118 } 1119 1120 1121 1122 /** 1123 * Prints attribute. 1124 * NOTE: xml:space attribute modifies output format 1125 * 1126 * @param name 1127 * @param value 1128 * @param isSpecified 1129 * @exception IOException 1130 */ printAttribute(String name, String value, boolean isSpecified, Attr attr)1131 private void printAttribute (String name, String value, boolean isSpecified, Attr attr) throws IOException{ 1132 1133 if (isSpecified || (features & DOMSerializerImpl.DISCARDDEFAULT) == 0) { 1134 if (fDOMFilter !=null && 1135 (fDOMFilter.getWhatToShow() & NodeFilter.SHOW_ATTRIBUTE)!= 0) { 1136 short code = fDOMFilter.acceptNode(attr); 1137 switch (code) { 1138 case NodeFilter.FILTER_REJECT: 1139 case NodeFilter.FILTER_SKIP: { 1140 return; 1141 } 1142 default: { 1143 // fall through 1144 } 1145 } 1146 } 1147 _printer.printSpace(); 1148 _printer.printText( name ); 1149 _printer.printText( "=\"" ); 1150 printEscaped( value ); 1151 _printer.printText( '"' ); 1152 } 1153 1154 // If the attribute xml:space exists, determine whether 1155 // to preserve spaces in this and child nodes based on 1156 // its value. 1157 if (name.equals( "xml:space" )) { 1158 if (value.equals( "preserve" )) 1159 fPreserveSpace = true; 1160 else 1161 fPreserveSpace = _format.getPreserveSpace(); 1162 } 1163 } 1164 getEntityRef( int ch )1165 protected String getEntityRef( int ch ) { 1166 // Encode special XML characters into the equivalent character references. 1167 // These five are defined by default for all XML documents. 1168 switch (ch) { 1169 case '<': 1170 return "lt"; 1171 case '>': 1172 return "gt"; 1173 case '"': 1174 return "quot"; 1175 case '\'': 1176 return "apos"; 1177 case '&': 1178 return "amp"; 1179 } 1180 return null; 1181 } 1182 1183 1184 /** Retrieve and remove the namespaces declarations from the list of attributes. 1185 * 1186 */ extractNamespaces( Attributes attrs )1187 private Attributes extractNamespaces( Attributes attrs ) 1188 throws SAXException 1189 { 1190 AttributesImpl attrsOnly; 1191 String rawName; 1192 int i; 1193 int indexColon; 1194 String prefix; 1195 int length; 1196 1197 if (attrs == null) { 1198 return null; 1199 } 1200 length = attrs.getLength(); 1201 attrsOnly = new AttributesImpl( attrs ); 1202 1203 for (i = length - 1 ; i >= 0 ; --i) { 1204 rawName = attrsOnly.getQName( i ); 1205 1206 //We have to exclude the namespaces declarations from the attributes 1207 //Append only when the feature http://xml.org/sax/features/namespace-prefixes" 1208 //is TRUE 1209 if (rawName.startsWith( "xmlns" )) { 1210 if (rawName.length() == 5) { 1211 startPrefixMapping( "", attrs.getValue( i ) ); 1212 attrsOnly.removeAttribute( i ); 1213 } else if (rawName.charAt(5) == ':') { 1214 startPrefixMapping(rawName.substring(6), attrs.getValue(i)); 1215 attrsOnly.removeAttribute( i ); 1216 } 1217 } 1218 } 1219 return attrsOnly; 1220 } 1221 1222 // 1223 // Printing attribute value 1224 // printEscaped(String source)1225 protected void printEscaped(String source) throws IOException { 1226 int length = source.length(); 1227 for (int i = 0; i < length; ++i) { 1228 int ch = source.charAt(i); 1229 if (!XMLChar.isValid(ch)) { 1230 if (++i < length) { 1231 surrogates(ch, source.charAt(i)); 1232 } else { 1233 fatalError("The character '" + (char) ch + "' is an invalid XML character"); 1234 } 1235 continue; 1236 } 1237 // escape NL, CR, TAB 1238 if (ch == '\n' || ch == '\r' || ch == '\t') { 1239 printHex(ch); 1240 } else if (ch == '<') { 1241 _printer.printText("<"); 1242 } else if (ch == '&') { 1243 _printer.printText("&"); 1244 } else if (ch == '"') { 1245 _printer.printText("""); 1246 } else if ((ch >= ' ' && _encodingInfo.isPrintable((char) ch))) { 1247 _printer.printText((char) ch); 1248 } else { 1249 printHex(ch); 1250 } 1251 } 1252 } 1253 1254 /** print text data */ printXMLChar( int ch)1255 protected void printXMLChar( int ch) throws IOException { 1256 if (ch == '\r') { 1257 printHex(ch); 1258 } else if ( ch == '<') { 1259 _printer.printText("<"); 1260 } else if (ch == '&') { 1261 _printer.printText("&"); 1262 } else if (ch == '>'){ 1263 // character sequence "]]>" can't appear in content, therefore 1264 // we should escape '>' 1265 _printer.printText(">"); 1266 } else if ( ch == '\n' || ch == '\t' || 1267 ( ch >= ' ' && _encodingInfo.isPrintable((char)ch))) { 1268 _printer.printText((char)ch); 1269 } else { 1270 printHex(ch); 1271 } 1272 } 1273 printText( String text, boolean preserveSpace, boolean unescaped )1274 protected void printText( String text, boolean preserveSpace, boolean unescaped ) 1275 throws IOException { 1276 int index; 1277 char ch; 1278 int length = text.length(); 1279 if ( preserveSpace ) { 1280 // Preserving spaces: the text must print exactly as it is, 1281 // without breaking when spaces appear in the text and without 1282 // consolidating spaces. If a line terminator is used, a line 1283 // break will occur. 1284 for ( index = 0 ; index < length ; ++index ) { 1285 ch = text.charAt( index ); 1286 if (!XMLChar.isValid(ch)) { 1287 // check if it is surrogate 1288 if (++index <length) { 1289 surrogates(ch, text.charAt(index)); 1290 } else { 1291 fatalError("The character '"+(char)ch+"' is an invalid XML character"); 1292 } 1293 continue; 1294 } 1295 if ( unescaped ) { 1296 _printer.printText( ch ); 1297 } else 1298 printXMLChar( ch ); 1299 } 1300 } else { 1301 // Not preserving spaces: print one part at a time, and 1302 // use spaces between parts to break them into different 1303 // lines. Spaces at beginning of line will be stripped 1304 // by printing mechanism. Line terminator is treated 1305 // no different than other text part. 1306 for ( index = 0 ; index < length ; ++index ) { 1307 ch = text.charAt( index ); 1308 if (!XMLChar.isValid(ch)) { 1309 // check if it is surrogate 1310 if (++index <length) { 1311 surrogates(ch, text.charAt(index)); 1312 } else { 1313 fatalError("The character '"+(char)ch+"' is an invalid XML character"); 1314 } 1315 continue; 1316 } 1317 1318 if ( unescaped ) 1319 _printer.printText( ch ); 1320 else 1321 printXMLChar( ch); 1322 } 1323 } 1324 } 1325 1326 1327 printText( char[] chars, int start, int length, boolean preserveSpace, boolean unescaped )1328 protected void printText( char[] chars, int start, int length, 1329 boolean preserveSpace, boolean unescaped ) throws IOException { 1330 int index; 1331 char ch; 1332 1333 if ( preserveSpace ) { 1334 // Preserving spaces: the text must print exactly as it is, 1335 // without breaking when spaces appear in the text and without 1336 // consolidating spaces. If a line terminator is used, a line 1337 // break will occur. 1338 while ( length-- > 0 ) { 1339 ch = chars[start++]; 1340 if (!XMLChar.isValid(ch)) { 1341 // check if it is surrogate 1342 if ( length-- > 0 ) { 1343 surrogates(ch, chars[start++]); 1344 } else { 1345 fatalError("The character '"+(char)ch+"' is an invalid XML character"); 1346 } 1347 continue; 1348 } 1349 if ( unescaped ) 1350 _printer.printText( ch ); 1351 else 1352 printXMLChar( ch ); 1353 } 1354 } else { 1355 // Not preserving spaces: print one part at a time, and 1356 // use spaces between parts to break them into different 1357 // lines. Spaces at beginning of line will be stripped 1358 // by printing mechanism. Line terminator is treated 1359 // no different than other text part. 1360 while ( length-- > 0 ) { 1361 ch = chars[start++]; 1362 if (!XMLChar.isValid(ch)) { 1363 // check if it is surrogate 1364 if ( length-- > 0 ) { 1365 surrogates(ch, chars[start++]); 1366 } else { 1367 fatalError("The character '"+(char)ch+"' is an invalid XML character"); 1368 } 1369 continue; 1370 } 1371 if ( unescaped ) 1372 _printer.printText( ch ); 1373 else 1374 printXMLChar( ch ); 1375 } 1376 } 1377 } 1378 1379 1380 /** 1381 * DOM Level 3: 1382 * Check a node to determine if it contains unbound namespace prefixes. 1383 * 1384 * @param node The node to check for unbound namespace prefices 1385 */ checkUnboundNamespacePrefixedNode(Node node)1386 protected void checkUnboundNamespacePrefixedNode (Node node) throws IOException{ 1387 1388 if (fNamespaces) { 1389 1390 if (DEBUG) { 1391 System.out.println("==>serializeNode("+node.getNodeName()+") [Entity Reference - Namespaces on]"); 1392 System.out.println("==>Declared Prefix Count: " + fNSBinder.getDeclaredPrefixCount()); 1393 System.out.println("==>Node Name: " + node.getNodeName()); 1394 System.out.println("==>First Child Node Name: " + node.getFirstChild().getNodeName()); 1395 System.out.println("==>First Child Node Prefix: " + node.getFirstChild().getPrefix()); 1396 System.out.println("==>First Child Node NamespaceURI: " + node.getFirstChild().getNamespaceURI()); 1397 } 1398 1399 1400 Node child, next; 1401 for (child = node.getFirstChild(); child != null; child = next) { 1402 next = child.getNextSibling(); 1403 if (DEBUG) { 1404 System.out.println("==>serializeNode("+child.getNodeName()+") [Child Node]"); 1405 System.out.println("==>serializeNode("+child.getPrefix()+") [Child Node Prefix]"); 1406 } 1407 1408 //If a NamespaceURI is not declared for the current 1409 //node's prefix, raise a fatal error. 1410 String prefix = child.getPrefix(); 1411 prefix = (prefix == null || 1412 prefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(prefix); 1413 if (fNSBinder.getURI(prefix) == null && prefix != null) { 1414 fatalError("The replacement text of the entity node '" 1415 + node.getNodeName() 1416 + "' contains an element node '" 1417 + child.getNodeName() 1418 + "' with an undeclared prefix '" 1419 + prefix + "'."); 1420 } 1421 1422 if (child.getNodeType() == Node.ELEMENT_NODE) { 1423 1424 NamedNodeMap attrs = child.getAttributes(); 1425 1426 for (int i = 0; i< attrs.getLength(); i++ ) { 1427 1428 String attrPrefix = attrs.item(i).getPrefix(); 1429 attrPrefix = (attrPrefix == null || 1430 attrPrefix.length() == 0) ? XMLSymbols.EMPTY_STRING : fSymbolTable.addSymbol(attrPrefix); 1431 if (fNSBinder.getURI(attrPrefix) == null && attrPrefix != null) { 1432 fatalError("The replacement text of the entity node '" 1433 + node.getNodeName() 1434 + "' contains an element node '" 1435 + child.getNodeName() 1436 + "' with an attribute '" 1437 + attrs.item(i).getNodeName() 1438 + "' an undeclared prefix '" 1439 + attrPrefix + "'."); 1440 } 1441 1442 } 1443 1444 } 1445 1446 if (child.hasChildNodes()) { 1447 checkUnboundNamespacePrefixedNode(child); 1448 } 1449 } 1450 } 1451 } 1452 reset()1453 public boolean reset() { 1454 super.reset(); 1455 if (fNSBinder != null){ 1456 fNSBinder.reset(); 1457 // during serialization always have a mapping to empty string 1458 // so we assume there is a declaration. 1459 fNSBinder.declarePrefix(XMLSymbols.EMPTY_STRING, XMLSymbols.EMPTY_STRING); 1460 } 1461 return true; 1462 } 1463 1464 } 1465