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