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