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