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