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