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