1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 2001-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * 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 * $Id: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid Exp $ 22 */ 23 package com.sun.org.apache.xml.internal.serializer; 24 25 import java.io.IOException; 26 import java.io.OutputStream; 27 import java.io.UnsupportedEncodingException; 28 import java.io.Writer; 29 import java.util.Properties; 30 import java.util.StringTokenizer; 31 import java.util.Vector; 32 33 import javax.xml.transform.ErrorListener; 34 import javax.xml.transform.OutputKeys; 35 import javax.xml.transform.Transformer; 36 import javax.xml.transform.TransformerException; 37 38 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; 39 import com.sun.org.apache.xml.internal.serializer.utils.Utils; 40 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException; 41 import org.w3c.dom.Node; 42 import org.xml.sax.Attributes; 43 import org.xml.sax.ContentHandler; 44 import org.xml.sax.SAXException; 45 46 //import com.sun.media.sound.IESecurity; 47 48 /** 49 * This abstract class is a base class for other stream 50 * serializers (xml, html, text ...) that write output to a stream. 51 * 52 * @xsl.usage internal 53 */ 54 abstract public class ToStream extends SerializerBase 55 { 56 57 private static final String COMMENT_BEGIN = "<!--"; 58 private static final String COMMENT_END = "-->"; 59 60 /** Stack to keep track of disabling output escaping. */ 61 protected BoolStack m_disableOutputEscapingStates = new BoolStack(); 62 63 64 /** 65 * The encoding information associated with this serializer. 66 * Although initially there is no encoding, 67 * there is a dummy EncodingInfo object that will say 68 * that every character is in the encoding. This is useful 69 * for a serializer that is in temporary output state and has 70 * no associated encoding. A serializer in final output state 71 * will have an encoding, and will worry about whether 72 * single chars or surrogate pairs of high/low chars form 73 * characters in the output encoding. 74 */ 75 EncodingInfo m_encodingInfo = new EncodingInfo(null,null); 76 77 /** 78 * Method reference to the sun.io.CharToByteConverter#canConvert method 79 * for this encoding. Invalid if m_charToByteConverter is null. 80 */ 81 java.lang.reflect.Method m_canConvertMeth; 82 83 84 85 /** 86 * Boolean that tells if we already tried to get the converter. 87 */ 88 boolean m_triedToGetConverter = false; 89 90 91 /** 92 * Opaque reference to the sun.io.CharToByteConverter for this 93 * encoding. 94 */ 95 Object m_charToByteConverter = null; 96 97 98 /** 99 * Stack to keep track of whether or not we need to 100 * preserve whitespace. 101 * 102 * Used to push/pop values used for the field m_ispreserve, but 103 * m_ispreserve is only relevant if m_doIndent is true. 104 * If m_doIndent is false this field has no impact. 105 * 106 */ 107 protected BoolStack m_preserves = new BoolStack(); 108 109 /** 110 * State flag to tell if preservation of whitespace 111 * is important. 112 * 113 * Used only in shouldIndent() but only if m_doIndent is true. 114 * If m_doIndent is false this flag has no impact. 115 * 116 */ 117 protected boolean m_ispreserve = false; 118 119 /** 120 * State flag that tells if the previous node processed 121 * was text, so we can tell if we should preserve whitespace. 122 * 123 * Used in endDocument() and shouldIndent() but 124 * only if m_doIndent is true. 125 * If m_doIndent is false this flag has no impact. 126 */ 127 protected boolean m_isprevtext = false; 128 129 /** 130 * The maximum character size before we have to resort 131 * to escaping. 132 */ 133 protected int m_maxCharacter = Encodings.getLastPrintable(); 134 135 136 /** 137 * The system line separator for writing out line breaks. 138 * The default value is from the system property, 139 * but this value can be set through the xsl:output 140 * extension attribute xalan:line-separator. 141 */ 142 protected char[] m_lineSep = 143 System.getProperty("line.separator").toCharArray(); 144 145 /** 146 * True if the the system line separator is to be used. 147 */ 148 protected boolean m_lineSepUse = true; 149 150 /** 151 * The length of the line seperator, since the write is done 152 * one character at a time. 153 */ 154 protected int m_lineSepLen = m_lineSep.length; 155 156 /** 157 * Map that tells which characters should have special treatment, and it 158 * provides character to entity name lookup. 159 */ 160 protected CharInfo m_charInfo; 161 162 /** True if we control the buffer, and we should flush the output on endDocument. */ 163 boolean m_shouldFlush = true; 164 165 /** 166 * Add space before '/>' for XHTML. 167 */ 168 protected boolean m_spaceBeforeClose = false; 169 170 /** 171 * Flag to signal that a newline should be added. 172 * 173 * Used only in indent() which is called only if m_doIndent is true. 174 * If m_doIndent is false this flag has no impact. 175 */ 176 boolean m_startNewLine; 177 178 /** 179 * Tells if we're in an internal document type subset. 180 */ 181 protected boolean m_inDoctype = false; 182 183 /** 184 * Flag to quickly tell if the encoding is UTF8. 185 */ 186 boolean m_isUTF8 = false; 187 188 /** The xsl:output properties. */ 189 protected Properties m_format; 190 191 /** 192 * remembers if we are in between the startCDATA() and endCDATA() callbacks 193 */ 194 protected boolean m_cdataStartCalled = false; 195 196 /** 197 * If this flag is true DTD entity references are not left as-is, 198 * which is exiting older behavior. 199 */ 200 private boolean m_expandDTDEntities = true; 201 202 203 /** 204 * Default constructor 205 */ 206 public ToStream() 207 { 208 } 209 210 /** 211 * This helper method to writes out "]]>" when closing a CDATA section. 212 * 213 * @throws org.xml.sax.SAXException 214 */ 215 protected void closeCDATA() throws org.xml.sax.SAXException 216 { 217 try 218 { 219 m_writer.write(CDATA_DELIMITER_CLOSE); 220 // write out a CDATA section closing "]]>" 221 m_cdataTagOpen = false; // Remember that we have done so. 222 } 223 catch (IOException e) 224 { 225 throw new SAXException(e); 226 } 227 } 228 229 /** 230 * Serializes the DOM node. Throws an exception only if an I/O 231 * exception occured while serializing. 232 * 233 * @param node Node to serialize. 234 * @throws IOException An I/O exception occured while serializing 235 */ 236 public void serialize(Node node) throws IOException 237 { 238 239 try 240 { 241 TreeWalker walker = 242 new TreeWalker(this); 243 244 walker.traverse(node); 245 } 246 catch (org.xml.sax.SAXException se) 247 { 248 throw new WrappedRuntimeException(se); 249 } 250 } 251 252 /** 253 * Return true if the character is the high member of a surrogate pair. 254 * 255 * NEEDSDOC @param c 256 * 257 * NEEDSDOC ($objectName$) @return 258 */ 259 static final boolean isUTF16Surrogate(char c) 260 { 261 return (c & 0xFC00) == 0xD800; 262 } 263 264 /** 265 * Taken from XSLTC 266 */ 267 private boolean m_escaping = true; 268 269 /** 270 * Flush the formatter's result stream. 271 * 272 * @throws org.xml.sax.SAXException 273 */ 274 protected final void flushWriter() throws org.xml.sax.SAXException 275 { 276 final java.io.Writer writer = m_writer; 277 if (null != writer) 278 { 279 try 280 { 281 if (writer instanceof WriterToUTF8Buffered) 282 { 283 if (m_shouldFlush) 284 ((WriterToUTF8Buffered) writer).flush(); 285 else 286 ((WriterToUTF8Buffered) writer).flushBuffer(); 287 } 288 if (writer instanceof WriterToASCI) 289 { 290 if (m_shouldFlush) 291 writer.flush(); 292 } 293 else 294 { 295 // Flush always. 296 // Not a great thing if the writer was created 297 // by this class, but don't have a choice. 298 writer.flush(); 299 } 300 } 301 catch (IOException ioe) 302 { 303 throw new org.xml.sax.SAXException(ioe); 304 } 305 } 306 } 307 308 /** 309 * Get the output stream where the events will be serialized to. 310 * 311 * @return reference to the result stream, or null of only a writer was 312 * set. 313 */ 314 public OutputStream getOutputStream() 315 { 316 317 if (m_writer instanceof WriterToUTF8Buffered) 318 return ((WriterToUTF8Buffered) m_writer).getOutputStream(); 319 if (m_writer instanceof WriterToASCI) 320 return ((WriterToASCI) m_writer).getOutputStream(); 321 else 322 return null; 323 } 324 325 // Implement DeclHandler 326 327 /** 328 * Report an element type declaration. 329 * 330 * <p>The content model will consist of the string "EMPTY", the 331 * string "ANY", or a parenthesised group, optionally followed 332 * by an occurrence indicator. The model will be normalized so 333 * that all whitespace is removed,and will include the enclosing 334 * parentheses.</p> 335 * 336 * @param name The element type name. 337 * @param model The content model as a normalized string. 338 * @exception SAXException The application may raise an exception. 339 */ 340 public void elementDecl(String name, String model) throws SAXException 341 { 342 // Do not inline external DTD 343 if (m_inExternalDTD) 344 return; 345 try 346 { 347 final java.io.Writer writer = m_writer; 348 DTDprolog(); 349 350 writer.write("<!ELEMENT "); 351 writer.write(name); 352 writer.write(' '); 353 writer.write(model); 354 writer.write('>'); 355 writer.write(m_lineSep, 0, m_lineSepLen); 356 } 357 catch (IOException e) 358 { 359 throw new SAXException(e); 360 } 361 362 } 363 364 /** 365 * Report an internal entity declaration. 366 * 367 * <p>Only the effective (first) declaration for each entity 368 * will be reported.</p> 369 * 370 * @param name The name of the entity. If it is a parameter 371 * entity, the name will begin with '%'. 372 * @param value The replacement text of the entity. 373 * @exception SAXException The application may raise an exception. 374 * @see #externalEntityDecl 375 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 376 */ 377 public void internalEntityDecl(String name, String value) 378 throws SAXException 379 { 380 // Do not inline external DTD 381 if (m_inExternalDTD) 382 return; 383 try 384 { 385 DTDprolog(); 386 outputEntityDecl(name, value); 387 } 388 catch (IOException e) 389 { 390 throw new SAXException(e); 391 } 392 393 } 394 395 /** 396 * Output the doc type declaration. 397 * 398 * @param name non-null reference to document type name. 399 * NEEDSDOC @param value 400 * 401 * @throws org.xml.sax.SAXException 402 */ 403 void outputEntityDecl(String name, String value) throws IOException 404 { 405 final java.io.Writer writer = m_writer; 406 writer.write("<!ENTITY "); 407 writer.write(name); 408 writer.write(" \""); 409 writer.write(value); 410 writer.write("\">"); 411 writer.write(m_lineSep, 0, m_lineSepLen); 412 } 413 414 /** 415 * Output a system-dependent line break. 416 * 417 * @throws org.xml.sax.SAXException 418 */ 419 protected final void outputLineSep() throws IOException 420 { 421 422 m_writer.write(m_lineSep, 0, m_lineSepLen); 423 } 424 425 /** 426 * Specifies an output format for this serializer. It the 427 * serializer has already been associated with an output format, 428 * it will switch to the new format. This method should not be 429 * called while the serializer is in the process of serializing 430 * a document. 431 * 432 * @param format The output format to use 433 */ 434 public void setOutputFormat(Properties format) 435 { 436 437 boolean shouldFlush = m_shouldFlush; 438 439 init(m_writer, format, false, false); 440 441 m_shouldFlush = shouldFlush; 442 } 443 444 /** 445 * Initialize the serializer with the specified writer and output format. 446 * Must be called before calling any of the serialize methods. 447 * This method can be called multiple times and the xsl:output properties 448 * passed in the 'format' parameter are accumulated across calls. 449 * 450 * @param writer The writer to use 451 * @param format The output format 452 * @param shouldFlush True if the writer should be flushed at EndDocument. 453 */ 454 private synchronized void init( 455 Writer writer, 456 Properties format, 457 boolean defaultProperties, 458 boolean shouldFlush) 459 { 460 461 m_shouldFlush = shouldFlush; 462 463 464 // if we are tracing events we need to trace what 465 // characters are written to the output writer. 466 if (m_tracer != null 467 && !(writer instanceof SerializerTraceWriter) ) 468 m_writer = new SerializerTraceWriter(writer, m_tracer); 469 else 470 m_writer = writer; 471 472 473 m_format = format; 474 // m_cdataSectionNames = 475 // OutputProperties.getQNameProperties( 476 // OutputKeys.CDATA_SECTION_ELEMENTS, 477 // format); 478 setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format); 479 480 setIndentAmount( 481 OutputPropertyUtils.getIntProperty( 482 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, 483 format)); 484 setIndent( 485 OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format)); 486 487 { 488 String sep = 489 format.getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR); 490 if (sep != null) { 491 m_lineSep = sep.toCharArray(); 492 m_lineSepLen = sep.length(); 493 } 494 } 495 496 boolean shouldNotWriteXMLHeader = 497 OutputPropertyUtils.getBooleanProperty( 498 OutputKeys.OMIT_XML_DECLARATION, 499 format); 500 setOmitXMLDeclaration(shouldNotWriteXMLHeader); 501 setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM)); 502 String doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); 503 setDoctypePublic(doctypePublic); 504 505 // if standalone was explicitly specified 506 if (format.get(OutputKeys.STANDALONE) != null) 507 { 508 String val = format.getProperty(OutputKeys.STANDALONE); 509 if (defaultProperties) 510 setStandaloneInternal(val); 511 else 512 setStandalone(val); 513 } 514 515 setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE)); 516 517 if (null != doctypePublic) 518 { 519 if (doctypePublic.startsWith("-//W3C//DTD XHTML")) 520 m_spaceBeforeClose = true; 521 } 522 523 /* 524 * This code is added for XML 1.1 Version output. 525 */ 526 String version = getVersion(); 527 if (null == version) 528 { 529 version = format.getProperty(OutputKeys.VERSION); 530 setVersion(version); 531 } 532 533 // initCharsMap(); 534 String encoding = getEncoding(); 535 if (null == encoding) 536 { 537 encoding = 538 Encodings.getMimeEncoding( 539 format.getProperty(OutputKeys.ENCODING)); 540 setEncoding(encoding); 541 } 542 543 m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING); 544 545 // Access this only from the Hashtable level... we don't want to 546 // get default properties. 547 String entitiesFileName = 548 (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES); 549 550 if (null != entitiesFileName) 551 { 552 553 String method = 554 (String) format.get(OutputKeys.METHOD); 555 556 m_charInfo = CharInfo.getCharInfo(entitiesFileName, method); 557 } 558 559 } 560 561 /** 562 * Initialize the serializer with the specified writer and output format. 563 * Must be called before calling any of the serialize methods. 564 * 565 * @param writer The writer to use 566 * @param format The output format 567 */ 568 private synchronized void init(Writer writer, Properties format) 569 { 570 init(writer, format, false, false); 571 } 572 /** 573 * Initialize the serializer with the specified output stream and output 574 * format. Must be called before calling any of the serialize methods. 575 * 576 * @param output The output stream to use 577 * @param format The output format 578 * @param defaultProperties true if the properties are the default 579 * properties 580 * 581 * @throws UnsupportedEncodingException The encoding specified in the 582 * output format is not supported 583 */ 584 protected synchronized void init( 585 OutputStream output, 586 Properties format, 587 boolean defaultProperties) 588 throws UnsupportedEncodingException 589 { 590 591 String encoding = getEncoding(); 592 if (encoding == null) 593 { 594 // if not already set then get it from the properties 595 encoding = 596 Encodings.getMimeEncoding( 597 format.getProperty(OutputKeys.ENCODING)); 598 setEncoding(encoding); 599 } 600 601 if (encoding.equalsIgnoreCase("UTF-8")) 602 { 603 m_isUTF8 = true; 604 // if (output instanceof java.io.BufferedOutputStream) 605 // { 606 // init(new WriterToUTF8(output), format, defaultProperties, true); 607 // } 608 // else if (output instanceof java.io.FileOutputStream) 609 // { 610 // init(new WriterToUTF8Buffered(output), format, defaultProperties, true); 611 // } 612 // else 613 // { 614 // // Not sure what to do in this case. I'm going to be conservative 615 // // and not buffer. 616 // init(new WriterToUTF8(output), format, defaultProperties, true); 617 // } 618 619 620 init( 621 new WriterToUTF8Buffered(output), 622 format, 623 defaultProperties, 624 true); 625 626 627 } 628 else if ( 629 encoding.equals("WINDOWS-1250") 630 || encoding.equals("US-ASCII") 631 || encoding.equals("ASCII")) 632 { 633 init(new WriterToASCI(output), format, defaultProperties, true); 634 } 635 else 636 { 637 Writer osw; 638 639 try 640 { 641 osw = Encodings.getWriter(output, encoding); 642 } 643 catch (UnsupportedEncodingException uee) 644 { 645 System.out.println( 646 "Warning: encoding \"" 647 + encoding 648 + "\" not supported" 649 + ", using " 650 + Encodings.DEFAULT_MIME_ENCODING); 651 652 encoding = Encodings.DEFAULT_MIME_ENCODING; 653 setEncoding(encoding); 654 osw = Encodings.getWriter(output, encoding); 655 } 656 657 init(osw, format, defaultProperties, true); 658 } 659 660 } 661 662 /** 663 * Returns the output format for this serializer. 664 * 665 * @return The output format in use 666 */ 667 public Properties getOutputFormat() 668 { 669 return m_format; 670 } 671 672 /** 673 * Specifies a writer to which the document should be serialized. 674 * This method should not be called while the serializer is in 675 * the process of serializing a document. 676 * 677 * @param writer The output writer stream 678 */ 679 public void setWriter(Writer writer) 680 { 681 // if we are tracing events we need to trace what 682 // characters are written to the output writer. 683 if (m_tracer != null 684 && !(writer instanceof SerializerTraceWriter) ) 685 m_writer = new SerializerTraceWriter(writer, m_tracer); 686 else 687 m_writer = writer; 688 } 689 690 /** 691 * Set if the operating systems end-of-line line separator should 692 * be used when serializing. If set false NL character 693 * (decimal 10) is left alone, otherwise the new-line will be replaced on 694 * output with the systems line separator. For example on UNIX this is 695 * NL, while on Windows it is two characters, CR NL, where CR is the 696 * carriage-return (decimal 13). 697 * 698 * @param use_sytem_line_break True if an input NL is replaced with the 699 * operating systems end-of-line separator. 700 * @return The previously set value of the serializer. 701 */ 702 public boolean setLineSepUse(boolean use_sytem_line_break) 703 { 704 boolean oldValue = m_lineSepUse; 705 m_lineSepUse = use_sytem_line_break; 706 return oldValue; 707 } 708 709 /** 710 * Specifies an output stream to which the document should be 711 * serialized. This method should not be called while the 712 * serializer is in the process of serializing a document. 713 * <p> 714 * The encoding specified in the output properties is used, or 715 * if no encoding was specified, the default for the selected 716 * output method. 717 * 718 * @param output The output stream 719 */ 720 public void setOutputStream(OutputStream output) 721 { 722 723 try 724 { 725 Properties format; 726 if (null == m_format) 727 format = 728 OutputPropertiesFactory.getDefaultMethodProperties( 729 Method.XML); 730 else 731 format = m_format; 732 init(output, format, true); 733 } 734 catch (UnsupportedEncodingException uee) 735 { 736 737 // Should have been warned in init, I guess... 738 } 739 } 740 741 /** 742 * @see SerializationHandler#setEscaping(boolean) 743 */ 744 public boolean setEscaping(boolean escape) 745 { 746 final boolean temp = m_escaping; 747 m_escaping = escape; 748 return temp; 749 750 } 751 752 753 /** 754 * Might print a newline character and the indentation amount 755 * of the given depth. 756 * 757 * @param depth the indentation depth (element nesting depth) 758 * 759 * @throws org.xml.sax.SAXException if an error occurs during writing. 760 */ 761 protected void indent(int depth) throws IOException 762 { 763 764 if (m_startNewLine) 765 outputLineSep(); 766 /* For m_indentAmount > 0 this extra test might be slower 767 * but Xalan's default value is 0, so this extra test 768 * will run faster in that situation. 769 */ 770 if (m_indentAmount > 0) 771 printSpace(depth * m_indentAmount); 772 773 } 774 775 /** 776 * Indent at the current element nesting depth. 777 * @throws IOException 778 */ 779 protected void indent() throws IOException 780 { 781 indent(m_elemContext.m_currentElemDepth); 782 } 783 /** 784 * Prints <var>n</var> spaces. 785 * @param n Number of spaces to print. 786 * 787 * @throws org.xml.sax.SAXException if an error occurs when writing. 788 */ 789 private void printSpace(int n) throws IOException 790 { 791 final java.io.Writer writer = m_writer; 792 for (int i = 0; i < n; i++) 793 { 794 writer.write(' '); 795 } 796 797 } 798 799 /** 800 * Report an attribute type declaration. 801 * 802 * <p>Only the effective (first) declaration for an attribute will 803 * be reported. The type will be one of the strings "CDATA", 804 * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", 805 * "ENTITIES", or "NOTATION", or a parenthesized token group with 806 * the separator "|" and all whitespace removed.</p> 807 * 808 * @param eName The name of the associated element. 809 * @param aName The name of the attribute. 810 * @param type A string representing the attribute type. 811 * @param valueDefault A string representing the attribute default 812 * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if 813 * none of these applies. 814 * @param value A string representing the attribute's default value, 815 * or null if there is none. 816 * @exception SAXException The application may raise an exception. 817 */ 818 public void attributeDecl( 819 String eName, 820 String aName, 821 String type, 822 String valueDefault, 823 String value) 824 throws SAXException 825 { 826 // Do not inline external DTD 827 if (m_inExternalDTD) 828 return; 829 try 830 { 831 final java.io.Writer writer = m_writer; 832 DTDprolog(); 833 834 writer.write("<!ATTLIST "); 835 writer.write(eName); 836 writer.write(' '); 837 838 writer.write(aName); 839 writer.write(' '); 840 writer.write(type); 841 if (valueDefault != null) 842 { 843 writer.write(' '); 844 writer.write(valueDefault); 845 } 846 847 //writer.write(" "); 848 //writer.write(value); 849 writer.write('>'); 850 writer.write(m_lineSep, 0, m_lineSepLen); 851 } 852 catch (IOException e) 853 { 854 throw new SAXException(e); 855 } 856 } 857 858 /** 859 * Get the character stream where the events will be serialized to. 860 * 861 * @return Reference to the result Writer, or null. 862 */ 863 public Writer getWriter() 864 { 865 return m_writer; 866 } 867 868 /** 869 * Report a parsed external entity declaration. 870 * 871 * <p>Only the effective (first) declaration for each entity 872 * will be reported.</p> 873 * 874 * @param name The name of the entity. If it is a parameter 875 * entity, the name will begin with '%'. 876 * @param publicId The declared public identifier of the entity, or 877 * null if none was declared. 878 * @param systemId The declared system identifier of the entity. 879 * @exception SAXException The application may raise an exception. 880 * @see #internalEntityDecl 881 * @see org.xml.sax.DTDHandler#unparsedEntityDecl 882 */ 883 public void externalEntityDecl( 884 String name, 885 String publicId, 886 String systemId) 887 throws SAXException 888 { 889 try { 890 DTDprolog(); 891 892 m_writer.write("<!ENTITY "); 893 m_writer.write(name); 894 if (publicId != null) { 895 m_writer.write(" PUBLIC \""); 896 m_writer.write(publicId); 897 898 } 899 else { 900 m_writer.write(" SYSTEM \""); 901 m_writer.write(systemId); 902 } 903 m_writer.write("\" >"); 904 m_writer.write(m_lineSep, 0, m_lineSepLen); 905 } catch (IOException e) { 906 // TODO Auto-generated catch block 907 e.printStackTrace(); 908 } 909 910 } 911 912 /** 913 * Tell if this character can be written without escaping. 914 */ 915 protected boolean escapingNotNeeded(char ch) 916 { 917 final boolean ret; 918 if (ch < 127) 919 { 920 // This is the old/fast code here, but is this 921 // correct for all encodings? 922 if (ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch)) 923 ret= true; 924 else 925 ret = false; 926 } 927 else { 928 ret = m_encodingInfo.isInEncoding(ch); 929 } 930 return ret; 931 } 932 933 /** 934 * Once a surrogate has been detected, write out the pair of 935 * characters if it is in the encoding, or if there is no 936 * encoding, otherwise write out an entity reference 937 * of the value of the unicode code point of the character 938 * represented by the high/low surrogate pair. 939 * <p> 940 * An exception is thrown if there is no low surrogate in the pair, 941 * because the array ends unexpectely, or if the low char is there 942 * but its value is such that it is not a low surrogate. 943 * 944 * @param c the first (high) part of the surrogate, which 945 * must be confirmed before calling this method. 946 * @param ch Character array. 947 * @param i position Where the surrogate was detected. 948 * @param end The end index of the significant characters. 949 * @return 0 if the pair of characters was written out as-is, 950 * the unicode code point of the character represented by 951 * the surrogate pair if an entity reference with that value 952 * was written out. 953 * 954 * @throws IOException 955 * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected. 956 */ 957 protected int writeUTF16Surrogate(char c, char ch[], int i, int end) 958 throws IOException 959 { 960 int codePoint = 0; 961 if (i + 1 >= end) 962 { 963 throw new IOException( 964 Utils.messages.createMessage( 965 MsgKey.ER_INVALID_UTF16_SURROGATE, 966 new Object[] { Integer.toHexString((int) c)})); 967 } 968 969 final char high = c; 970 final char low = ch[i+1]; 971 if (!Encodings.isLowUTF16Surrogate(low)) { 972 throw new IOException( 973 Utils.messages.createMessage( 974 MsgKey.ER_INVALID_UTF16_SURROGATE, 975 new Object[] { 976 Integer.toHexString((int) c) 977 + " " 978 + Integer.toHexString(low)})); 979 } 980 981 final java.io.Writer writer = m_writer; 982 983 // If we make it to here we have a valid high, low surrogate pair 984 if (m_encodingInfo.isInEncoding(c,low)) { 985 // If the character formed by the surrogate pair 986 // is in the encoding, so just write it out 987 writer.write(ch,i,2); 988 } 989 else { 990 // Don't know what to do with this char, it is 991 // not in the encoding and not a high char in 992 // a surrogate pair, so write out as an entity ref 993 final String encoding = getEncoding(); 994 if (encoding != null) { 995 /* The output encoding is known, 996 * so somthing is wrong. 997 */ 998 codePoint = Encodings.toCodePoint(high, low); 999 // not in the encoding, so write out a character reference 1000 writer.write('&'); 1001 writer.write('#'); 1002 writer.write(Integer.toString(codePoint)); 1003 writer.write(';'); 1004 } else { 1005 /* The output encoding is not known, 1006 * so just write it out as-is. 1007 */ 1008 writer.write(ch, i, 2); 1009 } 1010 } 1011 // non-zero only if character reference was written out. 1012 return codePoint; 1013 } 1014 1015 /** 1016 * Handle one of the default entities, return false if it 1017 * is not a default entity. 1018 * 1019 * @param ch character to be escaped. 1020 * @param i index into character array. 1021 * @param chars non-null reference to character array. 1022 * @param len length of chars. 1023 * @param fromTextNode true if the characters being processed 1024 * are from a text node, false if they are from an attribute value 1025 * @param escLF true if the linefeed should be escaped. 1026 * 1027 * @return i+1 if the character was written, else i. 1028 * 1029 * @throws java.io.IOException 1030 */ 1031 protected int accumDefaultEntity( 1032 java.io.Writer writer, 1033 char ch, 1034 int i, 1035 char[] chars, 1036 int len, 1037 boolean fromTextNode, 1038 boolean escLF) 1039 throws IOException 1040 { 1041 1042 if (!escLF && CharInfo.S_LINEFEED == ch) 1043 { 1044 writer.write(m_lineSep, 0, m_lineSepLen); 1045 } 1046 else 1047 { 1048 // if this is text node character and a special one of those, 1049 // or if this is a character from attribute value and a special one of those 1050 if ((fromTextNode && m_charInfo.isSpecialTextChar(ch)) || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch))) 1051 { 1052 String outputStringForChar = m_charInfo.getOutputStringForChar(ch); 1053 1054 if (null != outputStringForChar) 1055 { 1056 writer.write(outputStringForChar); 1057 } 1058 else 1059 return i; 1060 } 1061 else 1062 return i; 1063 } 1064 1065 return i + 1; 1066 1067 } 1068 /** 1069 * Normalize the characters, but don't escape. 1070 * 1071 * @param ch The characters from the XML document. 1072 * @param start The start position in the array. 1073 * @param length The number of characters to read from the array. 1074 * @param isCData true if a CDATA block should be built around the characters. 1075 * @param useSystemLineSeparator true if the operating systems 1076 * end-of-line separator should be output rather than a new-line character. 1077 * 1078 * @throws IOException 1079 * @throws org.xml.sax.SAXException 1080 */ 1081 void writeNormalizedChars( 1082 char ch[], 1083 int start, 1084 int length, 1085 boolean isCData, 1086 boolean useSystemLineSeparator) 1087 throws IOException, org.xml.sax.SAXException 1088 { 1089 final java.io.Writer writer = m_writer; 1090 int end = start + length; 1091 1092 for (int i = start; i < end; i++) 1093 { 1094 char c = ch[i]; 1095 1096 if (CharInfo.S_LINEFEED == c && useSystemLineSeparator) 1097 { 1098 writer.write(m_lineSep, 0, m_lineSepLen); 1099 } 1100 else if (isCData && (!escapingNotNeeded(c))) 1101 { 1102 // if (i != 0) 1103 if (m_cdataTagOpen) 1104 closeCDATA(); 1105 1106 // This needs to go into a function... 1107 if (Encodings.isHighUTF16Surrogate(c)) 1108 { 1109 writeUTF16Surrogate(c, ch, i, end); 1110 i++ ; // process two input characters 1111 } 1112 else 1113 { 1114 writer.write("&#"); 1115 1116 String intStr = Integer.toString((int) c); 1117 1118 writer.write(intStr); 1119 writer.write(';'); 1120 } 1121 1122 // if ((i != 0) && (i < (end - 1))) 1123 // if (!m_cdataTagOpen && (i < (end - 1))) 1124 // { 1125 // writer.write(CDATA_DELIMITER_OPEN); 1126 // m_cdataTagOpen = true; 1127 // } 1128 } 1129 else if ( 1130 isCData 1131 && ((i < (end - 2)) 1132 && (']' == c) 1133 && (']' == ch[i + 1]) 1134 && ('>' == ch[i + 2]))) 1135 { 1136 writer.write(CDATA_CONTINUE); 1137 1138 i += 2; 1139 } 1140 else 1141 { 1142 if (escapingNotNeeded(c)) 1143 { 1144 if (isCData && !m_cdataTagOpen) 1145 { 1146 writer.write(CDATA_DELIMITER_OPEN); 1147 m_cdataTagOpen = true; 1148 } 1149 writer.write(c); 1150 } 1151 1152 // This needs to go into a function... 1153 else if (Encodings.isHighUTF16Surrogate(c)) 1154 { 1155 if (m_cdataTagOpen) 1156 closeCDATA(); 1157 writeUTF16Surrogate(c, ch, i, end); 1158 i++; // process two input characters 1159 } 1160 else 1161 { 1162 if (m_cdataTagOpen) 1163 closeCDATA(); 1164 writer.write("&#"); 1165 1166 String intStr = Integer.toString((int) c); 1167 1168 writer.write(intStr); 1169 writer.write(';'); 1170 } 1171 } 1172 } 1173 1174 } 1175 1176 /** 1177 * Ends an un-escaping section. 1178 * 1179 * @see #startNonEscaping 1180 * 1181 * @throws org.xml.sax.SAXException 1182 */ 1183 public void endNonEscaping() throws org.xml.sax.SAXException 1184 { 1185 m_disableOutputEscapingStates.pop(); 1186 } 1187 1188 /** 1189 * Starts an un-escaping section. All characters printed within an un- 1190 * escaping section are printed as is, without escaping special characters 1191 * into entity references. Only XML and HTML serializers need to support 1192 * this method. 1193 * <p> The contents of the un-escaping section will be delivered through the 1194 * regular <tt>characters</tt> event. 1195 * 1196 * @throws org.xml.sax.SAXException 1197 */ 1198 public void startNonEscaping() throws org.xml.sax.SAXException 1199 { 1200 m_disableOutputEscapingStates.push(true); 1201 } 1202 1203 /** 1204 * Receive notification of cdata. 1205 * 1206 * <p>The Parser will call this method to report each chunk of 1207 * character data. SAX parsers may return all contiguous character 1208 * data in a single chunk, or they may split it into several 1209 * chunks; however, all of the characters in any single event 1210 * must come from the same external entity, so that the Locator 1211 * provides useful information.</p> 1212 * 1213 * <p>The application must not attempt to read from the array 1214 * outside of the specified range.</p> 1215 * 1216 * <p>Note that some parsers will report whitespace using the 1217 * ignorableWhitespace() method rather than this one (validating 1218 * parsers must do so).</p> 1219 * 1220 * @param ch The characters from the XML document. 1221 * @param start The start position in the array. 1222 * @param length The number of characters to read from the array. 1223 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1224 * wrapping another exception. 1225 * @see #ignorableWhitespace 1226 * @see org.xml.sax.Locator 1227 * 1228 * @throws org.xml.sax.SAXException 1229 */ 1230 protected void cdata(char ch[], int start, final int length) 1231 throws org.xml.sax.SAXException 1232 { 1233 1234 try 1235 { 1236 final int old_start = start; 1237 if (m_elemContext.m_startTagOpen) 1238 { 1239 closeStartTag(); 1240 m_elemContext.m_startTagOpen = false; 1241 } 1242 m_ispreserve = true; 1243 1244 if (shouldIndent()) 1245 indent(); 1246 1247 boolean writeCDataBrackets = 1248 (((length >= 1) && escapingNotNeeded(ch[start]))); 1249 1250 /* Write out the CDATA opening delimiter only if 1251 * we are supposed to, and if we are not already in 1252 * the middle of a CDATA section 1253 */ 1254 if (writeCDataBrackets && !m_cdataTagOpen) 1255 { 1256 m_writer.write(CDATA_DELIMITER_OPEN); 1257 m_cdataTagOpen = true; 1258 } 1259 1260 // writer.write(ch, start, length); 1261 if (isEscapingDisabled()) 1262 { 1263 charactersRaw(ch, start, length); 1264 } 1265 else 1266 writeNormalizedChars(ch, start, length, true, m_lineSepUse); 1267 1268 /* used to always write out CDATA closing delimiter here, 1269 * but now we delay, so that we can merge CDATA sections on output. 1270 * need to write closing delimiter later 1271 */ 1272 if (writeCDataBrackets) 1273 { 1274 /* if the CDATA section ends with ] don't leave it open 1275 * as there is a chance that an adjacent CDATA sections 1276 * starts with ]>. 1277 * We don't want to merge ]] with > , or ] with ]> 1278 */ 1279 if (ch[start + length - 1] == ']') 1280 closeCDATA(); 1281 } 1282 1283 // time to fire off CDATA event 1284 if (m_tracer != null) 1285 super.fireCDATAEvent(ch, old_start, length); 1286 } 1287 catch (IOException ioe) 1288 { 1289 throw new org.xml.sax.SAXException( 1290 Utils.messages.createMessage( 1291 MsgKey.ER_OIERROR, 1292 null), 1293 ioe); 1294 //"IO error", ioe); 1295 } 1296 } 1297 1298 /** 1299 * Tell if the character escaping should be disabled for the current state. 1300 * 1301 * @return true if the character escaping should be disabled. 1302 */ 1303 private boolean isEscapingDisabled() 1304 { 1305 return m_disableOutputEscapingStates.peekOrFalse(); 1306 } 1307 1308 /** 1309 * If available, when the disable-output-escaping attribute is used, 1310 * output raw text without escaping. 1311 * 1312 * @param ch The characters from the XML document. 1313 * @param start The start position in the array. 1314 * @param length The number of characters to read from the array. 1315 * 1316 * @throws org.xml.sax.SAXException 1317 */ 1318 protected void charactersRaw(char ch[], int start, int length) 1319 throws org.xml.sax.SAXException 1320 { 1321 1322 if (m_inEntityRef) 1323 return; 1324 try 1325 { 1326 if (m_elemContext.m_startTagOpen) 1327 { 1328 closeStartTag(); 1329 m_elemContext.m_startTagOpen = false; 1330 } 1331 1332 m_ispreserve = true; 1333 1334 m_writer.write(ch, start, length); 1335 } 1336 catch (IOException e) 1337 { 1338 throw new SAXException(e); 1339 } 1340 1341 } 1342 1343 /** 1344 * Receive notification of character data. 1345 * 1346 * <p>The Parser will call this method to report each chunk of 1347 * character data. SAX parsers may return all contiguous character 1348 * data in a single chunk, or they may split it into several 1349 * chunks; however, all of the characters in any single event 1350 * must come from the same external entity, so that the Locator 1351 * provides useful information.</p> 1352 * 1353 * <p>The application must not attempt to read from the array 1354 * outside of the specified range.</p> 1355 * 1356 * <p>Note that some parsers will report whitespace using the 1357 * ignorableWhitespace() method rather than this one (validating 1358 * parsers must do so).</p> 1359 * 1360 * @param chars The characters from the XML document. 1361 * @param start The start position in the array. 1362 * @param length The number of characters to read from the array. 1363 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1364 * wrapping another exception. 1365 * @see #ignorableWhitespace 1366 * @see org.xml.sax.Locator 1367 * 1368 * @throws org.xml.sax.SAXException 1369 */ 1370 public void characters(final char chars[], final int start, final int length) 1371 throws org.xml.sax.SAXException 1372 { 1373 // It does not make sense to continue with rest of the method if the number of 1374 // characters to read from array is 0. 1375 // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node 1376 // is created if string is empty. 1377 if (length == 0 || (m_inEntityRef && !m_expandDTDEntities)) 1378 return; 1379 if (m_elemContext.m_startTagOpen) 1380 { 1381 closeStartTag(); 1382 m_elemContext.m_startTagOpen = false; 1383 } 1384 else if (m_needToCallStartDocument) 1385 { 1386 startDocumentInternal(); 1387 } 1388 1389 if (m_cdataStartCalled || m_elemContext.m_isCdataSection) 1390 { 1391 /* either due to startCDATA() being called or due to 1392 * cdata-section-elements atribute, we need this as cdata 1393 */ 1394 cdata(chars, start, length); 1395 1396 return; 1397 } 1398 1399 if (m_cdataTagOpen) 1400 closeCDATA(); 1401 // the check with _escaping is a bit of a hack for XLSTC 1402 1403 if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping)) 1404 { 1405 charactersRaw(chars, start, length); 1406 1407 // time to fire off characters generation event 1408 if (m_tracer != null) 1409 super.fireCharEvent(chars, start, length); 1410 1411 return; 1412 } 1413 1414 if (m_elemContext.m_startTagOpen) 1415 { 1416 closeStartTag(); 1417 m_elemContext.m_startTagOpen = false; 1418 } 1419 1420 1421 try 1422 { 1423 int i; 1424 char ch1; 1425 int startClean; 1426 1427 // skip any leading whitspace 1428 // don't go off the end and use a hand inlined version 1429 // of isWhitespace(ch) 1430 final int end = start + length; 1431 int lastDirty = start - 1; // last character that needed processing 1432 for (i = start; 1433 ((i < end) 1434 && ((ch1 = chars[i]) == 0x20 1435 || (ch1 == 0xA && m_lineSepUse) 1436 || ch1 == 0xD 1437 || ch1 == 0x09)); 1438 i++) 1439 { 1440 /* 1441 * We are processing leading whitespace, but are doing the same 1442 * processing for dirty characters here as for non-whitespace. 1443 * 1444 */ 1445 if (!m_charInfo.isTextASCIIClean(ch1)) 1446 { 1447 lastDirty = processDirty(chars,end, i,ch1, lastDirty, true); 1448 i = lastDirty; 1449 } 1450 } 1451 /* If there is some non-whitespace, mark that we may need 1452 * to preserve this. This is only important if we have indentation on. 1453 */ 1454 if (i < end) 1455 m_ispreserve = true; 1456 1457 1458 // int lengthClean; // number of clean characters in a row 1459 // final boolean[] isAsciiClean = m_charInfo.getASCIIClean(); 1460 1461 final boolean isXML10 = XMLVERSION10.equals(getVersion()); 1462 // we've skipped the leading whitespace, now deal with the rest 1463 for (; i < end; i++) 1464 { 1465 { 1466 // A tight loop to skip over common clean chars 1467 // This tight loop makes it easier for the JIT 1468 // to optimize. 1469 char ch2; 1470 while (i<end 1471 && ((ch2 = chars[i])<127) 1472 && m_charInfo.isTextASCIIClean(ch2)) 1473 i++; 1474 if (i == end) 1475 break; 1476 } 1477 1478 final char ch = chars[i]; 1479 /* The check for isCharacterInC0orC1Ranger and 1480 * isNELorLSEPCharacter has been added 1481 * to support Control Characters in XML 1.1 1482 */ 1483 if (!isCharacterInC0orC1Range(ch) && 1484 (isXML10 || !isNELorLSEPCharacter(ch)) && 1485 (escapingNotNeeded(ch) && (!m_charInfo.isSpecialTextChar(ch))) 1486 || ('"' == ch)) 1487 { 1488 ; // a character needing no special processing 1489 } 1490 else 1491 { 1492 lastDirty = processDirty(chars,end, i, ch, lastDirty, true); 1493 i = lastDirty; 1494 } 1495 } 1496 1497 // we've reached the end. Any clean characters at the 1498 // end of the array than need to be written out? 1499 startClean = lastDirty + 1; 1500 if (i > startClean) 1501 { 1502 int lengthClean = i - startClean; 1503 m_writer.write(chars, startClean, lengthClean); 1504 } 1505 1506 // For indentation purposes, mark that we've just writen text out 1507 m_isprevtext = true; 1508 } 1509 catch (IOException e) 1510 { 1511 throw new SAXException(e); 1512 } 1513 1514 // time to fire off characters generation event 1515 if (m_tracer != null) 1516 super.fireCharEvent(chars, start, length); 1517 } 1518 /** 1519 * This method checks if a given character is between C0 or C1 range 1520 * of Control characters. 1521 * This method is added to support Control Characters for XML 1.1 1522 * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method 1523 * return false. Since they are whitespace characters, no special processing is needed. 1524 * 1525 * @param ch 1526 * @return boolean 1527 */ 1528 private static boolean isCharacterInC0orC1Range(char ch) 1529 { 1530 if(ch == 0x09 || ch == 0x0A || ch == 0x0D) 1531 return false; 1532 else 1533 return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F); 1534 } 1535 /** 1536 * This method checks if a given character either NEL (0x85) or LSEP (0x2028) 1537 * These are new end of line charcters added in XML 1.1. These characters must be 1538 * written as Numeric Character References (NCR) in XML 1.1 output document. 1539 * 1540 * @param ch 1541 * @return boolean 1542 */ 1543 private static boolean isNELorLSEPCharacter(char ch) 1544 { 1545 return (ch == 0x85 || ch == 0x2028); 1546 } 1547 /** 1548 * Process a dirty character and any preeceding clean characters 1549 * that were not yet processed. 1550 * @param chars array of characters being processed 1551 * @param end one (1) beyond the last character 1552 * in chars to be processed 1553 * @param i the index of the dirty character 1554 * @param ch the character in chars[i] 1555 * @param lastDirty the last dirty character previous to i 1556 * @param fromTextNode true if the characters being processed are 1557 * from a text node, false if they are from an attribute value. 1558 * @return the index of the last character processed 1559 */ 1560 private int processDirty( 1561 char[] chars, 1562 int end, 1563 int i, 1564 char ch, 1565 int lastDirty, 1566 boolean fromTextNode) throws IOException 1567 { 1568 int startClean = lastDirty + 1; 1569 // if we have some clean characters accumulated 1570 // process them before the dirty one. 1571 if (i > startClean) 1572 { 1573 int lengthClean = i - startClean; 1574 m_writer.write(chars, startClean, lengthClean); 1575 } 1576 1577 // process the "dirty" character 1578 if (CharInfo.S_LINEFEED == ch && fromTextNode) 1579 { 1580 m_writer.write(m_lineSep, 0, m_lineSepLen); 1581 } 1582 else 1583 { 1584 startClean = 1585 accumDefaultEscape( 1586 m_writer, 1587 (char)ch, 1588 i, 1589 chars, 1590 end, 1591 fromTextNode, 1592 false); 1593 i = startClean - 1; 1594 } 1595 // Return the index of the last character that we just processed 1596 // which is a dirty character. 1597 return i; 1598 } 1599 1600 /** 1601 * Receive notification of character data. 1602 * 1603 * @param s The string of characters to process. 1604 * 1605 * @throws org.xml.sax.SAXException 1606 */ 1607 public void characters(String s) throws org.xml.sax.SAXException 1608 { 1609 if (m_inEntityRef && !m_expandDTDEntities) 1610 return; 1611 final int length = s.length(); 1612 if (length > m_charsBuff.length) 1613 { 1614 m_charsBuff = new char[length * 2 + 1]; 1615 } 1616 s.getChars(0, length, m_charsBuff, 0); 1617 characters(m_charsBuff, 0, length); 1618 } 1619 1620 /** 1621 * Escape and writer.write a character. 1622 * 1623 * @param ch character to be escaped. 1624 * @param i index into character array. 1625 * @param chars non-null reference to character array. 1626 * @param len length of chars. 1627 * @param fromTextNode true if the characters being processed are 1628 * from a text node, false if the characters being processed are from 1629 * an attribute value. 1630 * @param escLF true if the linefeed should be escaped. 1631 * 1632 * @return i+1 if a character was written, i+2 if two characters 1633 * were written out, else return i. 1634 * 1635 * @throws org.xml.sax.SAXException 1636 */ 1637 protected int accumDefaultEscape( 1638 Writer writer, 1639 char ch, 1640 int i, 1641 char[] chars, 1642 int len, 1643 boolean fromTextNode, 1644 boolean escLF) 1645 throws IOException 1646 { 1647 1648 int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF); 1649 1650 if (i == pos) 1651 { 1652 if (Encodings.isHighUTF16Surrogate(ch)) 1653 { 1654 1655 // Should be the UTF-16 low surrogate of the hig/low pair. 1656 char next; 1657 // Unicode code point formed from the high/low pair. 1658 int codePoint = 0; 1659 1660 if (i + 1 >= len) 1661 { 1662 throw new IOException( 1663 Utils.messages.createMessage( 1664 MsgKey.ER_INVALID_UTF16_SURROGATE, 1665 new Object[] { Integer.toHexString(ch)})); 1666 //"Invalid UTF-16 surrogate detected: " 1667 1668 //+Integer.toHexString(ch)+ " ?"); 1669 } 1670 else 1671 { 1672 next = chars[++i]; 1673 1674 if (!(Encodings.isLowUTF16Surrogate(next))) 1675 throw new IOException( 1676 Utils.messages.createMessage( 1677 MsgKey 1678 .ER_INVALID_UTF16_SURROGATE, 1679 new Object[] { 1680 Integer.toHexString(ch) 1681 + " " 1682 + Integer.toHexString(next)})); 1683 //"Invalid UTF-16 surrogate detected: " 1684 1685 //+Integer.toHexString(ch)+" "+Integer.toHexString(next)); 1686 codePoint = Encodings.toCodePoint(ch,next); 1687 } 1688 1689 writer.write("&#"); 1690 writer.write(Integer.toString(codePoint)); 1691 writer.write(';'); 1692 pos += 2; // count the two characters that went into writing out this entity 1693 } 1694 else 1695 { 1696 /* This if check is added to support control characters in XML 1.1. 1697 * If a character is a Control Character within C0 and C1 range, it is desirable 1698 * to write it out as Numeric Character Reference(NCR) regardless of XML Version 1699 * being used for output document. 1700 */ 1701 if (isCharacterInC0orC1Range(ch) || 1702 (XMLVERSION11.equals(getVersion()) && isNELorLSEPCharacter(ch))) 1703 { 1704 writer.write("&#"); 1705 writer.write(Integer.toString(ch)); 1706 writer.write(';'); 1707 } 1708 else if ((!escapingNotNeeded(ch) || 1709 ( (fromTextNode && m_charInfo.isSpecialTextChar(ch)) 1710 || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch)))) 1711 && m_elemContext.m_currentElemDepth > 0) 1712 { 1713 writer.write("&#"); 1714 writer.write(Integer.toString(ch)); 1715 writer.write(';'); 1716 } 1717 else 1718 { 1719 writer.write(ch); 1720 } 1721 pos++; // count the single character that was processed 1722 } 1723 1724 } 1725 return pos; 1726 } 1727 1728 /** 1729 * Receive notification of the beginning of an element, although this is a 1730 * SAX method additional namespace or attribute information can occur before 1731 * or after this call, that is associated with this element. 1732 * 1733 * 1734 * @param namespaceURI The Namespace URI, or the empty string if the 1735 * element has no Namespace URI or if Namespace 1736 * processing is not being performed. 1737 * @param localName The local name (without prefix), or the 1738 * empty string if Namespace processing is not being 1739 * performed. 1740 * @param name The element type name. 1741 * @param atts The attributes attached to the element, if any. 1742 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1743 * wrapping another exception. 1744 * @see org.xml.sax.ContentHandler#startElement 1745 * @see org.xml.sax.ContentHandler#endElement 1746 * @see org.xml.sax.AttributeList 1747 * 1748 * @throws org.xml.sax.SAXException 1749 */ 1750 public void startElement( 1751 String namespaceURI, 1752 String localName, 1753 String name, 1754 Attributes atts) 1755 throws org.xml.sax.SAXException 1756 { 1757 if (m_inEntityRef) 1758 return; 1759 1760 if (m_needToCallStartDocument) 1761 { 1762 startDocumentInternal(); 1763 m_needToCallStartDocument = false; 1764 } 1765 else if (m_cdataTagOpen) 1766 closeCDATA(); 1767 try 1768 { 1769 if ((true == m_needToOutputDocTypeDecl) 1770 && (null != getDoctypeSystem())) 1771 { 1772 outputDocTypeDecl(name, true); 1773 } 1774 1775 m_needToOutputDocTypeDecl = false; 1776 1777 /* before we over-write the current elementLocalName etc. 1778 * lets close out the old one (if we still need to) 1779 */ 1780 if (m_elemContext.m_startTagOpen) 1781 { 1782 closeStartTag(); 1783 m_elemContext.m_startTagOpen = false; 1784 } 1785 1786 if (namespaceURI != null) 1787 ensurePrefixIsDeclared(namespaceURI, name); 1788 1789 m_ispreserve = false; 1790 1791 if (shouldIndent() && m_startNewLine) 1792 { 1793 indent(); 1794 } 1795 1796 m_startNewLine = true; 1797 1798 final java.io.Writer writer = m_writer; 1799 writer.write('<'); 1800 writer.write(name); 1801 } 1802 catch (IOException e) 1803 { 1804 throw new SAXException(e); 1805 } 1806 1807 // process the attributes now, because after this SAX call they might be gone 1808 if (atts != null) 1809 addAttributes(atts); 1810 1811 m_elemContext = m_elemContext.push(namespaceURI,localName,name); 1812 m_isprevtext = false; 1813 1814 if (m_tracer != null){ 1815 firePseudoAttributes(); 1816 } 1817 1818 } 1819 1820 /** 1821 * Receive notification of the beginning of an element, additional 1822 * namespace or attribute information can occur before or after this call, 1823 * that is associated with this element. 1824 * 1825 * 1826 * @param elementNamespaceURI The Namespace URI, or the empty string if the 1827 * element has no Namespace URI or if Namespace 1828 * processing is not being performed. 1829 * @param elementLocalName The local name (without prefix), or the 1830 * empty string if Namespace processing is not being 1831 * performed. 1832 * @param elementName The element type name. 1833 * @throws org.xml.sax.SAXException Any SAX exception, possibly 1834 * wrapping another exception. 1835 * @see org.xml.sax.ContentHandler#startElement 1836 * @see org.xml.sax.ContentHandler#endElement 1837 * @see org.xml.sax.AttributeList 1838 * 1839 * @throws org.xml.sax.SAXException 1840 */ 1841 public void startElement( 1842 String elementNamespaceURI, 1843 String elementLocalName, 1844 String elementName) 1845 throws SAXException 1846 { 1847 startElement(elementNamespaceURI, elementLocalName, elementName, null); 1848 } 1849 1850 public void startElement(String elementName) throws SAXException 1851 { 1852 startElement(null, null, elementName, null); 1853 } 1854 1855 /** 1856 * Output the doc type declaration. 1857 * 1858 * @param name non-null reference to document type name. 1859 * NEEDSDOC @param closeDecl 1860 * 1861 * @throws java.io.IOException 1862 */ 1863 void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException 1864 { 1865 if (m_cdataTagOpen) 1866 closeCDATA(); 1867 try 1868 { 1869 final java.io.Writer writer = m_writer; 1870 writer.write("<!DOCTYPE "); 1871 writer.write(name); 1872 1873 String doctypePublic = getDoctypePublic(); 1874 if (null != doctypePublic) 1875 { 1876 writer.write(" PUBLIC \""); 1877 writer.write(doctypePublic); 1878 writer.write('\"'); 1879 } 1880 1881 String doctypeSystem = getDoctypeSystem(); 1882 if (null != doctypeSystem) 1883 { 1884 if (null == doctypePublic) 1885 writer.write(" SYSTEM \""); 1886 else 1887 writer.write(" \""); 1888 1889 writer.write(doctypeSystem); 1890 1891 if (closeDecl) 1892 { 1893 writer.write("\">"); 1894 writer.write(m_lineSep, 0, m_lineSepLen); 1895 closeDecl = false; // done closing 1896 } 1897 else 1898 writer.write('\"'); 1899 } 1900 boolean dothis = false; 1901 if (dothis) 1902 { 1903 // at one point this code seemed right, 1904 // but not anymore - Brian M. 1905 if (closeDecl) 1906 { 1907 writer.write('>'); 1908 writer.write(m_lineSep, 0, m_lineSepLen); 1909 } 1910 } 1911 } 1912 catch (IOException e) 1913 { 1914 throw new SAXException(e); 1915 } 1916 } 1917 1918 /** 1919 * Process the attributes, which means to write out the currently 1920 * collected attributes to the writer. The attributes are not 1921 * cleared by this method 1922 * 1923 * @param writer the writer to write processed attributes to. 1924 * @param nAttrs the number of attributes in m_attributes 1925 * to be processed 1926 * 1927 * @throws java.io.IOException 1928 * @throws org.xml.sax.SAXException 1929 */ 1930 public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException 1931 { 1932 /* real SAX attributes are not passed in, so process the 1933 * attributes that were collected after the startElement call. 1934 * _attribVector is a "cheap" list for Stream serializer output 1935 * accumulated over a series of calls to attribute(name,value) 1936 */ 1937 String encoding = getEncoding(); 1938 for (int i = 0; i < nAttrs; i++) 1939 { 1940 // elementAt is JDK 1.1.8 1941 final String name = m_attributes.getQName(i); 1942 final String value = m_attributes.getValue(i); 1943 writer.write(' '); 1944 writer.write(name); 1945 writer.write("=\""); 1946 writeAttrString(writer, value, encoding); 1947 writer.write('\"'); 1948 } 1949 } 1950 1951 /** 1952 * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>, 1953 * and UTF-16 surrogates for chracter references <CODE>&#xnn</CODE>. 1954 * 1955 * @param string String to convert to XML format. 1956 * @param encoding CURRENTLY NOT IMPLEMENTED. 1957 * 1958 * @throws java.io.IOException 1959 */ 1960 public void writeAttrString( 1961 Writer writer, 1962 String string, 1963 String encoding) 1964 throws IOException 1965 { 1966 final int len = string.length(); 1967 if (len > m_attrBuff.length) 1968 { 1969 m_attrBuff = new char[len*2 + 1]; 1970 } 1971 string.getChars(0,len, m_attrBuff, 0); 1972 final char[] stringChars = m_attrBuff; 1973 1974 for (int i = 0; i < len; ) 1975 { 1976 char ch = stringChars[i]; 1977 if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch))) 1978 { 1979 writer.write(ch); 1980 i++; 1981 } 1982 else 1983 { // I guess the parser doesn't normalize cr/lf in attributes. -sb 1984 // if ((CharInfo.S_CARRIAGERETURN == ch) 1985 // && ((i + 1) < len) 1986 // && (CharInfo.S_LINEFEED == stringChars[i + 1])) 1987 // { 1988 // i++; 1989 // ch = CharInfo.S_LINEFEED; 1990 // } 1991 1992 i = accumDefaultEscape(writer, ch, i, stringChars, len, false, true); 1993 } 1994 } 1995 1996 } 1997 1998 /** 1999 * Receive notification of the end of an element. 2000 * 2001 * 2002 * @param namespaceURI The Namespace URI, or the empty string if the 2003 * element has no Namespace URI or if Namespace 2004 * processing is not being performed. 2005 * @param localName The local name (without prefix), or the 2006 * empty string if Namespace processing is not being 2007 * performed. 2008 * @param name The element type name 2009 * @throws org.xml.sax.SAXException Any SAX exception, possibly 2010 * wrapping another exception. 2011 * 2012 * @throws org.xml.sax.SAXException 2013 */ 2014 public void endElement(String namespaceURI, String localName, String name) 2015 throws org.xml.sax.SAXException 2016 { 2017 2018 if (m_inEntityRef) 2019 return; 2020 2021 // namespaces declared at the current depth are no longer valid 2022 // so get rid of them 2023 m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null); 2024 2025 try 2026 { 2027 final java.io.Writer writer = m_writer; 2028 if (m_elemContext.m_startTagOpen) 2029 { 2030 if (m_tracer != null) 2031 super.fireStartElem(m_elemContext.m_elementName); 2032 int nAttrs = m_attributes.getLength(); 2033 if (nAttrs > 0) 2034 { 2035 processAttributes(m_writer, nAttrs); 2036 // clear attributes object for re-use with next element 2037 m_attributes.clear(); 2038 } 2039 if (m_spaceBeforeClose) 2040 writer.write(" />"); 2041 else 2042 writer.write("/>"); 2043 /* don't need to pop cdataSectionState because 2044 * this element ended so quickly that we didn't get 2045 * to push the state. 2046 */ 2047 2048 } 2049 else 2050 { 2051 if (m_cdataTagOpen) 2052 closeCDATA(); 2053 2054 if (shouldIndent()) 2055 indent(m_elemContext.m_currentElemDepth - 1); 2056 writer.write('<'); 2057 writer.write('/'); 2058 writer.write(name); 2059 writer.write('>'); 2060 } 2061 } 2062 catch (IOException e) 2063 { 2064 throw new SAXException(e); 2065 } 2066 2067 if (!m_elemContext.m_startTagOpen && m_doIndent) 2068 { 2069 m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop(); 2070 } 2071 2072 m_isprevtext = false; 2073 2074 // fire off the end element event 2075 if (m_tracer != null) 2076 super.fireEndElem(name); 2077 m_elemContext = m_elemContext.m_prev; 2078 } 2079 2080 /** 2081 * Receive notification of the end of an element. 2082 * @param name The element type name 2083 * @throws org.xml.sax.SAXException Any SAX exception, possibly 2084 * wrapping another exception. 2085 */ 2086 public void endElement(String name) throws org.xml.sax.SAXException 2087 { 2088 endElement(null, null, name); 2089 } 2090 2091 /** 2092 * Begin the scope of a prefix-URI Namespace mapping 2093 * just before another element is about to start. 2094 * This call will close any open tags so that the prefix mapping 2095 * will not apply to the current element, but the up comming child. 2096 * 2097 * @see org.xml.sax.ContentHandler#startPrefixMapping 2098 * 2099 * @param prefix The Namespace prefix being declared. 2100 * @param uri The Namespace URI the prefix is mapped to. 2101 * 2102 * @throws org.xml.sax.SAXException The client may throw 2103 * an exception during processing. 2104 * 2105 */ 2106 public void startPrefixMapping(String prefix, String uri) 2107 throws org.xml.sax.SAXException 2108 { 2109 // the "true" causes the flush of any open tags 2110 startPrefixMapping(prefix, uri, true); 2111 } 2112 2113 /** 2114 * Handle a prefix/uri mapping, which is associated with a startElement() 2115 * that is soon to follow. Need to close any open start tag to make 2116 * sure than any name space attributes due to this event are associated wih 2117 * the up comming element, not the current one. 2118 * @see ExtendedContentHandler#startPrefixMapping 2119 * 2120 * @param prefix The Namespace prefix being declared. 2121 * @param uri The Namespace URI the prefix is mapped to. 2122 * @param shouldFlush true if any open tags need to be closed first, this 2123 * will impact which element the mapping applies to (open parent, or its up 2124 * comming child) 2125 * @return returns true if the call made a change to the current 2126 * namespace information, false if it did not change anything, e.g. if the 2127 * prefix/namespace mapping was already in scope from before. 2128 * 2129 * @throws org.xml.sax.SAXException The client may throw 2130 * an exception during processing. 2131 * 2132 * 2133 */ 2134 public boolean startPrefixMapping( 2135 String prefix, 2136 String uri, 2137 boolean shouldFlush) 2138 throws org.xml.sax.SAXException 2139 { 2140 2141 /* Remember the mapping, and at what depth it was declared 2142 * This is one greater than the current depth because these 2143 * mappings will apply to the next depth. This is in 2144 * consideration that startElement() will soon be called 2145 */ 2146 2147 boolean pushed; 2148 int pushDepth; 2149 if (shouldFlush) 2150 { 2151 flushPending(); 2152 // the prefix mapping applies to the child element (one deeper) 2153 pushDepth = m_elemContext.m_currentElemDepth + 1; 2154 } 2155 else 2156 { 2157 // the prefix mapping applies to the current element 2158 pushDepth = m_elemContext.m_currentElemDepth; 2159 } 2160 pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth); 2161 2162 if (pushed) 2163 { 2164 /* Brian M.: don't know if we really needto do this. The 2165 * callers of this object should have injected both 2166 * startPrefixMapping and the attributes. We are 2167 * just covering our butt here. 2168 */ 2169 String name; 2170 if (EMPTYSTRING.equals(prefix)) 2171 { 2172 name = "xmlns"; 2173 addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false); 2174 } 2175 else 2176 { 2177 if (!EMPTYSTRING.equals(uri)) 2178 // hack for XSLTC attribset16 test 2179 { // that maps ns1 prefix to "" URI 2180 name = "xmlns:" + prefix; 2181 2182 /* for something like xmlns:abc="w3.pretend.org" 2183 * the uri is the value, that is why we pass it in the 2184 * value, or 5th slot of addAttributeAlways() 2185 */ 2186 addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false); 2187 } 2188 } 2189 } 2190 return pushed; 2191 } 2192 2193 /** 2194 * Receive notification of an XML comment anywhere in the document. This 2195 * callback will be used for comments inside or outside the document 2196 * element, including comments in the external DTD subset (if read). 2197 * @param ch An array holding the characters in the comment. 2198 * @param start The starting position in the array. 2199 * @param length The number of characters to use from the array. 2200 * @throws org.xml.sax.SAXException The application may raise an exception. 2201 */ 2202 public void comment(char ch[], int start, int length) 2203 throws org.xml.sax.SAXException 2204 { 2205 2206 int start_old = start; 2207 if (m_inEntityRef) 2208 return; 2209 if (m_elemContext.m_startTagOpen) 2210 { 2211 closeStartTag(); 2212 m_elemContext.m_startTagOpen = false; 2213 } 2214 else if (m_needToCallStartDocument) 2215 { 2216 startDocumentInternal(); 2217 m_needToCallStartDocument = false; 2218 } 2219 2220 try 2221 { 2222 if (shouldIndent() && m_isStandalone) 2223 indent(); 2224 2225 final int limit = start + length; 2226 boolean wasDash = false; 2227 if (m_cdataTagOpen) 2228 closeCDATA(); 2229 2230 if (shouldIndent() && !m_isStandalone) 2231 indent(); 2232 2233 final java.io.Writer writer = m_writer; 2234 writer.write(COMMENT_BEGIN); 2235 // Detect occurrences of two consecutive dashes, handle as necessary. 2236 for (int i = start; i < limit; i++) 2237 { 2238 if (wasDash && ch[i] == '-') 2239 { 2240 writer.write(ch, start, i - start); 2241 writer.write(" -"); 2242 start = i + 1; 2243 } 2244 wasDash = (ch[i] == '-'); 2245 } 2246 2247 // if we have some chars in the comment 2248 if (length > 0) 2249 { 2250 // Output the remaining characters (if any) 2251 final int remainingChars = (limit - start); 2252 if (remainingChars > 0) 2253 writer.write(ch, start, remainingChars); 2254 // Protect comment end from a single trailing dash 2255 if (ch[limit - 1] == '-') 2256 writer.write(' '); 2257 } 2258 writer.write(COMMENT_END); 2259 } 2260 catch (IOException e) 2261 { 2262 throw new SAXException(e); 2263 } 2264 2265 /* 2266 * Don't write out any indentation whitespace now, 2267 * because there may be non-whitespace text after this. 2268 * 2269 * Simply mark that at this point if we do decide 2270 * to indent that we should 2271 * add a newline on the end of the current line before 2272 * the indentation at the start of the next line. 2273 */ 2274 m_startNewLine = true; 2275 // time to generate comment event 2276 if (m_tracer != null) 2277 super.fireCommentEvent(ch, start_old,length); 2278 } 2279 2280 /** 2281 * Report the end of a CDATA section. 2282 * @throws org.xml.sax.SAXException The application may raise an exception. 2283 * 2284 * @see #startCDATA 2285 */ 2286 public void endCDATA() throws org.xml.sax.SAXException 2287 { 2288 if (m_cdataTagOpen) 2289 closeCDATA(); 2290 m_cdataStartCalled = false; 2291 } 2292 2293 /** 2294 * Report the end of DTD declarations. 2295 * @throws org.xml.sax.SAXException The application may raise an exception. 2296 * @see #startDTD 2297 */ 2298 public void endDTD() throws org.xml.sax.SAXException 2299 { 2300 try 2301 { 2302 // Don't output doctype declaration until startDocumentInternal 2303 // has been called. Otherwise, it can appear before XML decl. 2304 if (m_needToCallStartDocument) { 2305 return; 2306 } 2307 2308 if (m_needToOutputDocTypeDecl) 2309 { 2310 outputDocTypeDecl(m_elemContext.m_elementName, false); 2311 m_needToOutputDocTypeDecl = false; 2312 } 2313 final java.io.Writer writer = m_writer; 2314 if (!m_inDoctype) 2315 writer.write("]>"); 2316 else 2317 { 2318 writer.write('>'); 2319 } 2320 2321 writer.write(m_lineSep, 0, m_lineSepLen); 2322 } 2323 catch (IOException e) 2324 { 2325 throw new SAXException(e); 2326 } 2327 2328 } 2329 2330 /** 2331 * End the scope of a prefix-URI Namespace mapping. 2332 * @see org.xml.sax.ContentHandler#endPrefixMapping 2333 * 2334 * @param prefix The prefix that was being mapping. 2335 * @throws org.xml.sax.SAXException The client may throw 2336 * an exception during processing. 2337 */ 2338 public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException 2339 { // do nothing 2340 } 2341 2342 /** 2343 * Receive notification of ignorable whitespace in element content. 2344 * 2345 * Not sure how to get this invoked quite yet. 2346 * 2347 * @param ch The characters from the XML document. 2348 * @param start The start position in the array. 2349 * @param length The number of characters to read from the array. 2350 * @throws org.xml.sax.SAXException Any SAX exception, possibly 2351 * wrapping another exception. 2352 * @see #characters 2353 * 2354 * @throws org.xml.sax.SAXException 2355 */ 2356 public void ignorableWhitespace(char ch[], int start, int length) 2357 throws org.xml.sax.SAXException 2358 { 2359 2360 if (0 == length) 2361 return; 2362 characters(ch, start, length); 2363 } 2364 2365 /** 2366 * Receive notification of a skipped entity. 2367 * @see org.xml.sax.ContentHandler#skippedEntity 2368 * 2369 * @param name The name of the skipped entity. If it is a 2370 * parameter entity, the name will begin with '%', 2371 * and if it is the external DTD subset, it will be the string 2372 * "[dtd]". 2373 * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping 2374 * another exception. 2375 */ 2376 public void skippedEntity(String name) throws org.xml.sax.SAXException 2377 { // TODO: Should handle 2378 } 2379 2380 /** 2381 * Report the start of a CDATA section. 2382 * 2383 * @throws org.xml.sax.SAXException The application may raise an exception. 2384 * @see #endCDATA 2385 */ 2386 public void startCDATA() throws org.xml.sax.SAXException 2387 { 2388 m_cdataStartCalled = true; 2389 } 2390 2391 /** 2392 * Report the beginning of an entity. 2393 * 2394 * The start and end of the document entity are not reported. 2395 * The start and end of the external DTD subset are reported 2396 * using the pseudo-name "[dtd]". All other events must be 2397 * properly nested within start/end entity events. 2398 * 2399 * @param name The name of the entity. If it is a parameter 2400 * entity, the name will begin with '%'. 2401 * @throws org.xml.sax.SAXException The application may raise an exception. 2402 * @see #endEntity 2403 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl 2404 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl 2405 */ 2406 public void startEntity(String name) throws org.xml.sax.SAXException 2407 { 2408 if (name.equals("[dtd]")) 2409 m_inExternalDTD = true; 2410 2411 if (!m_expandDTDEntities && !m_inExternalDTD) { 2412 /* Only leave the entity as-is if 2413 * we've been told not to expand them 2414 * and this is not the magic [dtd] name. 2415 */ 2416 startNonEscaping(); 2417 characters("&" + name + ';'); 2418 endNonEscaping(); 2419 } 2420 2421 m_inEntityRef = true; 2422 } 2423 2424 /** 2425 * For the enclosing elements starting tag write out 2426 * out any attributes followed by ">" 2427 * 2428 * @throws org.xml.sax.SAXException 2429 */ 2430 protected void closeStartTag() throws SAXException 2431 { 2432 if (m_elemContext.m_startTagOpen) 2433 { 2434 2435 try 2436 { 2437 if (m_tracer != null) 2438 super.fireStartElem(m_elemContext.m_elementName); 2439 int nAttrs = m_attributes.getLength(); 2440 if (nAttrs > 0) 2441 { 2442 processAttributes(m_writer, nAttrs); 2443 // clear attributes object for re-use with next element 2444 m_attributes.clear(); 2445 } 2446 m_writer.write('>'); 2447 } 2448 catch (IOException e) 2449 { 2450 throw new SAXException(e); 2451 } 2452 2453 /* whether Xalan or XSLTC, we have the prefix mappings now, so 2454 * lets determine if the current element is specified in the cdata- 2455 * section-elements list. 2456 */ 2457 if (m_cdataSectionElements != null) 2458 m_elemContext.m_isCdataSection = isCdataSection(); 2459 2460 if (m_doIndent) 2461 { 2462 m_isprevtext = false; 2463 m_preserves.push(m_ispreserve); 2464 } 2465 } 2466 2467 } 2468 2469 /** 2470 * Report the start of DTD declarations, if any. 2471 * 2472 * Any declarations are assumed to be in the internal subset unless 2473 * otherwise indicated. 2474 * 2475 * @param name The document type name. 2476 * @param publicId The declared public identifier for the 2477 * external DTD subset, or null if none was declared. 2478 * @param systemId The declared system identifier for the 2479 * external DTD subset, or null if none was declared. 2480 * @throws org.xml.sax.SAXException The application may raise an 2481 * exception. 2482 * @see #endDTD 2483 * @see #startEntity 2484 */ 2485 public void startDTD(String name, String publicId, String systemId) 2486 throws org.xml.sax.SAXException 2487 { 2488 setDoctypeSystem(systemId); 2489 setDoctypePublic(publicId); 2490 2491 m_elemContext.m_elementName = name; 2492 m_inDoctype = true; 2493 } 2494 2495 /** 2496 * Returns the m_indentAmount. 2497 * @return int 2498 */ 2499 public int getIndentAmount() 2500 { 2501 return m_indentAmount; 2502 } 2503 2504 /** 2505 * Sets the m_indentAmount. 2506 * 2507 * @param m_indentAmount The m_indentAmount to set 2508 */ 2509 public void setIndentAmount(int m_indentAmount) 2510 { 2511 this.m_indentAmount = m_indentAmount; 2512 } 2513 2514 /** 2515 * Tell if, based on space preservation constraints and the doIndent property, 2516 * if an indent should occur. 2517 * 2518 * @return True if an indent should occur. 2519 */ 2520 protected boolean shouldIndent() 2521 { 2522 return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone); 2523 } 2524 2525 /** 2526 * Searches for the list of qname properties with the specified key in the 2527 * property list. If the key is not found in this property list, the default 2528 * property list, and its defaults, recursively, are then checked. The 2529 * method returns <code>null</code> if the property is not found. 2530 * 2531 * @param key the property key. 2532 * @param props the list of properties to search in. 2533 * 2534 * Sets the vector of local-name/URI pairs of the cdata section elements 2535 * specified in the cdata-section-elements property. 2536 * 2537 * This method is essentially a copy of getQNameProperties() from 2538 * OutputProperties. Eventually this method should go away and a call 2539 * to setCdataSectionElements(Vector v) should be made directly. 2540 */ 2541 private void setCdataSectionElements(String key, Properties props) 2542 { 2543 2544 String s = props.getProperty(key); 2545 2546 if (null != s) 2547 { 2548 // Vector of URI/LocalName pairs 2549 Vector v = new Vector(); 2550 int l = s.length(); 2551 boolean inCurly = false; 2552 StringBuffer buf = new StringBuffer(); 2553 2554 // parse through string, breaking on whitespaces. I do this instead 2555 // of a tokenizer so I can track whitespace inside of curly brackets, 2556 // which theoretically shouldn't happen if they contain legal URLs. 2557 for (int i = 0; i < l; i++) 2558 { 2559 char c = s.charAt(i); 2560 2561 if (Character.isWhitespace(c)) 2562 { 2563 if (!inCurly) 2564 { 2565 if (buf.length() > 0) 2566 { 2567 addCdataSectionElement(buf.toString(), v); 2568 buf.setLength(0); 2569 } 2570 continue; 2571 } 2572 } 2573 else if ('{' == c) 2574 inCurly = true; 2575 else if ('}' == c) 2576 inCurly = false; 2577 2578 buf.append(c); 2579 } 2580 2581 if (buf.length() > 0) 2582 { 2583 addCdataSectionElement(buf.toString(), v); 2584 buf.setLength(0); 2585 } 2586 // call the official, public method to set the collected names 2587 setCdataSectionElements(v); 2588 } 2589 2590 } 2591 2592 /** 2593 * Adds a URI/LocalName pair of strings to the list. 2594 * 2595 * @param URI_and_localName String of the form "{uri}local" or "local" 2596 * 2597 * @return a QName object 2598 */ 2599 private void addCdataSectionElement(String URI_and_localName, Vector v) 2600 { 2601 2602 StringTokenizer tokenizer = 2603 new StringTokenizer(URI_and_localName, "{}", false); 2604 String s1 = tokenizer.nextToken(); 2605 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; 2606 2607 if (null == s2) 2608 { 2609 // add null URI and the local name 2610 v.addElement(null); 2611 v.addElement(s1); 2612 } 2613 else 2614 { 2615 // add URI, then local name 2616 v.addElement(s1); 2617 v.addElement(s2); 2618 } 2619 } 2620 2621 /** 2622 * Remembers the cdata sections specified in the cdata-section-elements. 2623 * The "official way to set URI and localName pairs. 2624 * This method should be used by both Xalan and XSLTC. 2625 * 2626 * @param URI_and_localNames a vector of pairs of Strings (URI/local) 2627 */ 2628 public void setCdataSectionElements(Vector URI_and_localNames) 2629 { 2630 m_cdataSectionElements = URI_and_localNames; 2631 } 2632 2633 /** 2634 * Makes sure that the namespace URI for the given qualified attribute name 2635 * is declared. 2636 * @param ns the namespace URI 2637 * @param rawName the qualified name 2638 * @return returns null if no action is taken, otherwise it returns the 2639 * prefix used in declaring the namespace. 2640 * @throws SAXException 2641 */ 2642 protected String ensureAttributesNamespaceIsDeclared( 2643 String ns, 2644 String localName, 2645 String rawName) 2646 throws org.xml.sax.SAXException 2647 { 2648 2649 if (ns != null && ns.length() > 0) 2650 { 2651 2652 // extract the prefix in front of the raw name 2653 int index = 0; 2654 String prefixFromRawName = 2655 (index = rawName.indexOf(":")) < 0 2656 ? "" 2657 : rawName.substring(0, index); 2658 2659 if (index > 0) 2660 { 2661 // we have a prefix, lets see if it maps to a namespace 2662 String uri = m_prefixMap.lookupNamespace(prefixFromRawName); 2663 if (uri != null && uri.equals(ns)) 2664 { 2665 // the prefix in the raw name is already maps to the given namespace uri 2666 // so we don't need to do anything 2667 return null; 2668 } 2669 else 2670 { 2671 // The uri does not map to the prefix in the raw name, 2672 // so lets make the mapping. 2673 this.startPrefixMapping(prefixFromRawName, ns, false); 2674 this.addAttribute( 2675 "http://www.w3.org/2000/xmlns/", 2676 prefixFromRawName, 2677 "xmlns:" + prefixFromRawName, 2678 "CDATA", 2679 ns, false); 2680 return prefixFromRawName; 2681 } 2682 } 2683 else 2684 { 2685 // we don't have a prefix in the raw name. 2686 // Does the URI map to a prefix already? 2687 String prefix = m_prefixMap.lookupPrefix(ns); 2688 if (prefix == null) 2689 { 2690 // uri is not associated with a prefix, 2691 // so lets generate a new prefix to use 2692 prefix = m_prefixMap.generateNextPrefix(); 2693 this.startPrefixMapping(prefix, ns, false); 2694 this.addAttribute( 2695 "http://www.w3.org/2000/xmlns/", 2696 prefix, 2697 "xmlns:" + prefix, 2698 "CDATA", 2699 ns, false); 2700 } 2701 2702 return prefix; 2703 2704 } 2705 } 2706 return null; 2707 } 2708 2709 void ensurePrefixIsDeclared(String ns, String rawName) 2710 throws org.xml.sax.SAXException 2711 { 2712 2713 if (ns != null && ns.length() > 0) 2714 { 2715 int index; 2716 final boolean no_prefix = ((index = rawName.indexOf(":")) < 0); 2717 String prefix = (no_prefix) ? "" : rawName.substring(0, index); 2718 2719 if (null != prefix) 2720 { 2721 String foundURI = m_prefixMap.lookupNamespace(prefix); 2722 2723 if ((null == foundURI) || !foundURI.equals(ns)) 2724 { 2725 this.startPrefixMapping(prefix, ns); 2726 2727 // Bugzilla1133: Generate attribute as well as namespace event. 2728 // SAX does expect both. 2729 2730 this.addAttributeAlways( 2731 "http://www.w3.org/2000/xmlns/", 2732 no_prefix ? "xmlns" : prefix, // local name 2733 no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname 2734 "CDATA", 2735 ns, 2736 false); 2737 } 2738 2739 } 2740 } 2741 } 2742 2743 /** 2744 * This method flushes any pending events, which can be startDocument() 2745 * closing the opening tag of an element, or closing an open CDATA section. 2746 */ 2747 public void flushPending() throws SAXException 2748 { 2749 if (m_needToCallStartDocument) 2750 { 2751 startDocumentInternal(); 2752 m_needToCallStartDocument = false; 2753 } 2754 if (m_elemContext.m_startTagOpen) 2755 { 2756 closeStartTag(); 2757 m_elemContext.m_startTagOpen = false; 2758 } 2759 2760 if (m_cdataTagOpen) 2761 { 2762 closeCDATA(); 2763 m_cdataTagOpen = false; 2764 } 2765 } 2766 2767 public void setContentHandler(ContentHandler ch) 2768 { 2769 // this method is really only useful in the ToSAXHandler classes but it is 2770 // in the interface. If the method defined here is ever called 2771 // we are probably in trouble. 2772 } 2773 2774 /** 2775 * Adds the given attribute to the set of attributes, even if there is 2776 * no currently open element. This is useful if a SAX startPrefixMapping() 2777 * should need to add an attribute before the element name is seen. 2778 * 2779 * This method is a copy of its super classes method, except that some 2780 * tracing of events is done. This is so the tracing is only done for 2781 * stream serializers, not for SAX ones. 2782 * 2783 * @param uri the URI of the attribute 2784 * @param localName the local name of the attribute 2785 * @param rawName the qualified name of the attribute 2786 * @param type the type of the attribute (probably CDATA) 2787 * @param value the value of the attribute 2788 * @param xslAttribute true if this attribute is coming from an xsl:attribute element. 2789 * @return true if the attribute value was added, 2790 * false if the attribute already existed and the value was 2791 * replaced with the new value. 2792 */ 2793 public boolean addAttributeAlways( 2794 String uri, 2795 String localName, 2796 String rawName, 2797 String type, 2798 String value, 2799 boolean xslAttribute) 2800 { 2801 boolean was_added; 2802 int index; 2803 //if (uri == null || localName == null || uri.length() == 0) 2804 index = m_attributes.getIndex(rawName); 2805 // Don't use 'localName' as it gives incorrect value, rely only on 'rawName' 2806 /*else { 2807 index = m_attributes.getIndex(uri, localName); 2808 }*/ 2809 if (index >= 0) 2810 { 2811 String old_value = null; 2812 if (m_tracer != null) 2813 { 2814 old_value = m_attributes.getValue(index); 2815 if (value.equals(old_value)) 2816 old_value = null; 2817 } 2818 2819 /* We've seen the attribute before. 2820 * We may have a null uri or localName, but all we really 2821 * want to re-set is the value anyway. 2822 */ 2823 m_attributes.setValue(index, value); 2824 was_added = false; 2825 if (old_value != null){ 2826 firePseudoAttributes(); 2827 } 2828 2829 } 2830 else 2831 { 2832 // the attribute doesn't exist yet, create it 2833 if (xslAttribute) 2834 { 2835 /* 2836 * This attribute is from an xsl:attribute element so we take some care in 2837 * adding it, e.g. 2838 * <elem1 foo:attr1="1" xmlns:foo="uri1"> 2839 * <xsl:attribute name="foo:attr2">2</xsl:attribute> 2840 * </elem1> 2841 * 2842 * We are adding attr1 and attr2 both as attributes of elem1, 2843 * and this code is adding attr2 (the xsl:attribute ). 2844 * We could have a collision with the prefix like in the example above. 2845 */ 2846 2847 // In the example above, is there a prefix like foo ? 2848 final int colonIndex = rawName.indexOf(':'); 2849 if (colonIndex > 0) 2850 { 2851 String prefix = rawName.substring(0,colonIndex); 2852 NamespaceMappings.MappingRecord existing_mapping = m_prefixMap.getMappingFromPrefix(prefix); 2853 2854 /* Before adding this attribute (foo:attr2), 2855 * is the prefix for it (foo) already mapped at the current depth? 2856 */ 2857 if (existing_mapping != null 2858 && existing_mapping.m_declarationDepth == m_elemContext.m_currentElemDepth 2859 && !existing_mapping.m_uri.equals(uri)) 2860 { 2861 /* 2862 * There is an existing mapping of this prefix, 2863 * it differs from the one we need, 2864 * and unfortunately it is at the current depth so we 2865 * can not over-ride it. 2866 */ 2867 2868 /* 2869 * Are we lucky enough that an existing other prefix maps to this URI ? 2870 */ 2871 prefix = m_prefixMap.lookupPrefix(uri); 2872 if (prefix == null) 2873 { 2874 /* Unfortunately there is no existing prefix that happens to map to ours, 2875 * so to avoid a prefix collision we must generated a new prefix to use. 2876 * This is OK because the prefix URI mapping 2877 * defined in the xsl:attribute is short in scope, 2878 * just the xsl:attribute element itself, 2879 * and at this point in serialization the body of the 2880 * xsl:attribute, if any, is just a String. Right? 2881 * . . . I sure hope so - Brian M. 2882 */ 2883 prefix = m_prefixMap.generateNextPrefix(); 2884 } 2885 2886 rawName = prefix + ':' + localName; 2887 } 2888 } 2889 2890 try 2891 { 2892 /* This is our last chance to make sure the namespace for this 2893 * attribute is declared, especially if we just generated an alternate 2894 * prefix to avoid a collision (the new prefix/rawName will go out of scope 2895 * soon and be lost ... last chance here. 2896 */ 2897 String prefixUsed = 2898 ensureAttributesNamespaceIsDeclared( 2899 uri, 2900 localName, 2901 rawName); 2902 } 2903 catch (SAXException e) 2904 { 2905 // TODO Auto-generated catch block 2906 e.printStackTrace(); 2907 } 2908 } 2909 m_attributes.addAttribute(uri, localName, rawName, type, value); 2910 was_added = true; 2911 if (m_tracer != null){ 2912 firePseudoAttributes(); 2913 } 2914 } 2915 return was_added; 2916 } 2917 2918 /** 2919 * To fire off the pseudo characters of attributes, as they currently 2920 * exist. This method should be called everytime an attribute is added, 2921 * or when an attribute value is changed, or an element is created. 2922 */ 2923 2924 protected void firePseudoAttributes() 2925 { 2926 if (m_tracer != null) 2927 { 2928 try 2929 { 2930 // flush out the "<elemName" if not already flushed 2931 m_writer.flush(); 2932 2933 // make a StringBuffer to write the name="value" pairs to. 2934 StringBuffer sb = new StringBuffer(); 2935 int nAttrs = m_attributes.getLength(); 2936 if (nAttrs > 0) 2937 { 2938 // make a writer that internally appends to the same 2939 // StringBuffer 2940 java.io.Writer writer = 2941 new ToStream.WritertoStringBuffer(sb); 2942 2943 processAttributes(writer, nAttrs); 2944 // Don't clear the attributes! 2945 // We only want to see what would be written out 2946 // at this point, we don't want to loose them. 2947 } 2948 sb.append('>'); // the potential > after the attributes. 2949 // convert the StringBuffer to a char array and 2950 // emit the trace event that these characters "might" 2951 // be written 2952 char ch[] = sb.toString().toCharArray(); 2953 m_tracer.fireGenerateEvent( 2954 SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS, 2955 ch, 2956 0, 2957 ch.length); 2958 } 2959 catch (IOException ioe) 2960 { 2961 // ignore ? 2962 } 2963 catch (SAXException se) 2964 { 2965 // ignore ? 2966 } 2967 } 2968 } 2969 2970 /** 2971 * This inner class is used only to collect attribute values 2972 * written by the method writeAttrString() into a string buffer. 2973 * In this manner trace events, and the real writing of attributes will use 2974 * the same code. 2975 */ 2976 private class WritertoStringBuffer extends java.io.Writer 2977 { 2978 final private StringBuffer m_stringbuf; 2979 /** 2980 * @see java.io.Writer#write(char[], int, int) 2981 */ 2982 WritertoStringBuffer(StringBuffer sb) 2983 { 2984 m_stringbuf = sb; 2985 } 2986 2987 public void write(char[] arg0, int arg1, int arg2) throws IOException 2988 { 2989 m_stringbuf.append(arg0, arg1, arg2); 2990 } 2991 /** 2992 * @see java.io.Writer#flush() 2993 */ 2994 public void flush() throws IOException 2995 { 2996 } 2997 /** 2998 * @see java.io.Writer#close() 2999 */ 3000 public void close() throws IOException 3001 { 3002 } 3003 3004 public void write(int i) 3005 { 3006 m_stringbuf.append((char) i); 3007 } 3008 3009 public void write(String s) 3010 { 3011 m_stringbuf.append(s); 3012 } 3013 } 3014 3015 /** 3016 * @see SerializationHandler#setTransformer(Transformer) 3017 */ 3018 public void setTransformer(Transformer transformer) { 3019 super.setTransformer(transformer); 3020 if (m_tracer != null 3021 && !(m_writer instanceof SerializerTraceWriter) ) 3022 m_writer = new SerializerTraceWriter(m_writer, m_tracer); 3023 3024 3025 } 3026 /** 3027 * Try's to reset the super class and reset this class for 3028 * re-use, so that you don't need to create a new serializer 3029 * (mostly for performance reasons). 3030 * 3031 * @return true if the class was successfuly reset. 3032 */ 3033 public boolean reset() 3034 { 3035 boolean wasReset = false; 3036 if (super.reset()) 3037 { 3038 resetToStream(); 3039 wasReset = true; 3040 } 3041 return wasReset; 3042 } 3043 3044 /** 3045 * Reset all of the fields owned by ToStream class 3046 * 3047 */ 3048 private void resetToStream() 3049 { 3050 this.m_cdataStartCalled = false; 3051 /* The stream is being reset. It is one of 3052 * ToXMLStream, ToHTMLStream ... and this type can't be changed 3053 * so neither should m_charInfo which is associated with the 3054 * type of Stream. Just leave m_charInfo as-is for the next re-use. 3055 * 3056 */ 3057 // this.m_charInfo = null; // don't set to null 3058 3059 this.m_disableOutputEscapingStates.clear(); 3060 3061 this.m_escaping = true; 3062 // Leave m_format alone for now - Brian M. 3063 // this.m_format = null; 3064 this.m_inDoctype = false; 3065 this.m_ispreserve = false; 3066 this.m_ispreserve = false; 3067 this.m_isprevtext = false; 3068 this.m_isUTF8 = false; // ?? used anywhere ?? 3069 this.m_preserves.clear(); 3070 this.m_shouldFlush = true; 3071 this.m_spaceBeforeClose = false; 3072 this.m_startNewLine = false; 3073 this.m_lineSepUse = true; 3074 // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !! 3075 // this.m_writer = null; 3076 this.m_expandDTDEntities = true; 3077 3078 } 3079 3080 /** 3081 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute. 3082 * @param encoding the character encoding 3083 */ 3084 public void setEncoding(String encoding) 3085 { 3086 String old = getEncoding(); 3087 super.setEncoding(encoding); 3088 if (old == null || !old.equals(encoding)) { 3089 // If we have changed the setting of the 3090 m_encodingInfo = Encodings.getEncodingInfo(encoding); 3091 3092 if (encoding != null && m_encodingInfo.name == null) { 3093 // We tried to get an EncodingInfo for Object for the given 3094 // encoding, but it came back with an internall null name 3095 // so the encoding is not supported by the JDK, issue a message. 3096 String msg = Utils.messages.createMessage( 3097 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ encoding }); 3098 try 3099 { 3100 // Prepare to issue the warning message 3101 Transformer tran = super.getTransformer(); 3102 if (tran != null) { 3103 ErrorListener errHandler = tran.getErrorListener(); 3104 // Issue the warning message 3105 if (null != errHandler && m_sourceLocator != null) 3106 errHandler.warning(new TransformerException(msg, m_sourceLocator)); 3107 else 3108 System.out.println(msg); 3109 } 3110 else 3111 System.out.println(msg); 3112 } 3113 catch (Exception e){} 3114 } 3115 } 3116 return; 3117 } 3118 3119 /** 3120 * Simple stack for boolean values. 3121 * 3122 * This class is a copy of the one in com.sun.org.apache.xml.internal.utils. 3123 * It exists to cut the serializers dependancy on that package. 3124 * A minor changes from that package are: 3125 * doesn't implement Clonable 3126 * 3127 * @xsl.usage internal 3128 */ 3129 static final class BoolStack 3130 { 3131 3132 /** Array of boolean values */ 3133 private boolean m_values[]; 3134 3135 /** Array size allocated */ 3136 private int m_allocatedSize; 3137 3138 /** Index into the array of booleans */ 3139 private int m_index; 3140 3141 /** 3142 * Default constructor. Note that the default 3143 * block size is very small, for small lists. 3144 */ 3145 public BoolStack() 3146 { 3147 this(32); 3148 } 3149 3150 /** 3151 * Construct a IntVector, using the given block size. 3152 * 3153 * @param size array size to allocate 3154 */ 3155 public BoolStack(int size) 3156 { 3157 3158 m_allocatedSize = size; 3159 m_values = new boolean[size]; 3160 m_index = -1; 3161 } 3162 3163 /** 3164 * Get the length of the list. 3165 * 3166 * @return Current length of the list 3167 */ 3168 public final int size() 3169 { 3170 return m_index + 1; 3171 } 3172 3173 /** 3174 * Clears the stack. 3175 * 3176 */ 3177 public final void clear() 3178 { 3179 m_index = -1; 3180 } 3181 3182 /** 3183 * Pushes an item onto the top of this stack. 3184 * 3185 * 3186 * @param val the boolean to be pushed onto this stack. 3187 * @return the <code>item</code> argument. 3188 */ 3189 public final boolean push(boolean val) 3190 { 3191 3192 if (m_index == m_allocatedSize - 1) 3193 grow(); 3194 3195 return (m_values[++m_index] = val); 3196 } 3197 3198 /** 3199 * Removes the object at the top of this stack and returns that 3200 * object as the value of this function. 3201 * 3202 * @return The object at the top of this stack. 3203 * @throws EmptyStackException if this stack is empty. 3204 */ 3205 public final boolean pop() 3206 { 3207 return m_values[m_index--]; 3208 } 3209 3210 /** 3211 * Removes the object at the top of this stack and returns the 3212 * next object at the top as the value of this function. 3213 * 3214 * 3215 * @return Next object to the top or false if none there 3216 */ 3217 public final boolean popAndTop() 3218 { 3219 3220 m_index--; 3221 3222 return (m_index >= 0) ? m_values[m_index] : false; 3223 } 3224 3225 /** 3226 * Set the item at the top of this stack 3227 * 3228 * 3229 * @param b Object to set at the top of this stack 3230 */ 3231 public final void setTop(boolean b) 3232 { 3233 m_values[m_index] = b; 3234 } 3235 3236 /** 3237 * Looks at the object at the top of this stack without removing it 3238 * from the stack. 3239 * 3240 * @return the object at the top of this stack. 3241 * @throws EmptyStackException if this stack is empty. 3242 */ 3243 public final boolean peek() 3244 { 3245 return m_values[m_index]; 3246 } 3247 3248 /** 3249 * Looks at the object at the top of this stack without removing it 3250 * from the stack. If the stack is empty, it returns false. 3251 * 3252 * @return the object at the top of this stack. 3253 */ 3254 public final boolean peekOrFalse() 3255 { 3256 return (m_index > -1) ? m_values[m_index] : false; 3257 } 3258 3259 /** 3260 * Looks at the object at the top of this stack without removing it 3261 * from the stack. If the stack is empty, it returns true. 3262 * 3263 * @return the object at the top of this stack. 3264 */ 3265 public final boolean peekOrTrue() 3266 { 3267 return (m_index > -1) ? m_values[m_index] : true; 3268 } 3269 3270 /** 3271 * Tests if this stack is empty. 3272 * 3273 * @return <code>true</code> if this stack is empty; 3274 * <code>false</code> otherwise. 3275 */ 3276 public boolean isEmpty() 3277 { 3278 return (m_index == -1); 3279 } 3280 3281 /** 3282 * Grows the size of the stack 3283 * 3284 */ 3285 private void grow() 3286 { 3287 3288 m_allocatedSize *= 2; 3289 3290 boolean newVector[] = new boolean[m_allocatedSize]; 3291 3292 System.arraycopy(m_values, 0, newVector, 0, m_index + 1); 3293 3294 m_values = newVector; 3295 } 3296 } 3297 3298 // Implement DTDHandler 3299 /** 3300 * If this method is called, the serializer is used as a 3301 * DTDHandler, which changes behavior how the serializer 3302 * handles document entities. 3303 * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String) 3304 */ 3305 public void notationDecl(String name, String pubID, String sysID) throws SAXException { 3306 // TODO Auto-generated method stub 3307 try { 3308 DTDprolog(); 3309 3310 m_writer.write("<!NOTATION "); 3311 m_writer.write(name); 3312 if (pubID != null) { 3313 m_writer.write(" PUBLIC \""); 3314 m_writer.write(pubID); 3315 3316 } 3317 else { 3318 m_writer.write(" SYSTEM \""); 3319 m_writer.write(sysID); 3320 } 3321 m_writer.write("\" >"); 3322 m_writer.write(m_lineSep, 0, m_lineSepLen); 3323 } catch (IOException e) { 3324 // TODO Auto-generated catch block 3325 e.printStackTrace(); 3326 } 3327 } 3328 3329 /** 3330 * If this method is called, the serializer is used as a 3331 * DTDHandler, which changes behavior how the serializer 3332 * handles document entities. 3333 * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String) 3334 */ 3335 public void unparsedEntityDecl(String name, String pubID, String sysID, String notationName) throws SAXException { 3336 // TODO Auto-generated method stub 3337 try { 3338 DTDprolog(); 3339 3340 m_writer.write("<!ENTITY "); 3341 m_writer.write(name); 3342 if (pubID != null) { 3343 m_writer.write(" PUBLIC \""); 3344 m_writer.write(pubID); 3345 3346 } 3347 else { 3348 m_writer.write(" SYSTEM \""); 3349 m_writer.write(sysID); 3350 } 3351 m_writer.write("\" NDATA "); 3352 m_writer.write(notationName); 3353 m_writer.write(" >"); 3354 m_writer.write(m_lineSep, 0, m_lineSepLen); 3355 } catch (IOException e) { 3356 // TODO Auto-generated catch block 3357 e.printStackTrace(); 3358 } 3359 } 3360 3361 /** 3362 * A private helper method to output the 3363 * @throws SAXException 3364 * @throws IOException 3365 */ 3366 private void DTDprolog() throws SAXException, IOException { 3367 final java.io.Writer writer = m_writer; 3368 if (m_needToOutputDocTypeDecl) 3369 { 3370 outputDocTypeDecl(m_elemContext.m_elementName, false); 3371 m_needToOutputDocTypeDecl = false; 3372 } 3373 if (m_inDoctype) 3374 { 3375 writer.write(" ["); 3376 writer.write(m_lineSep, 0, m_lineSepLen); 3377 m_inDoctype = false; 3378 } 3379 } 3380 3381 /** 3382 * If set to false the serializer does not expand DTD entities, 3383 * but leaves them as is, the default value is true; 3384 */ 3385 public void setDTDEntityExpansion(boolean expand) { 3386 m_expandDTDEntities = expand; 3387 } 3388 }