1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.serializer; 23 24 import java.io.IOException; 25 26 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; 27 import com.sun.org.apache.xml.internal.serializer.utils.Utils; 28 import org.xml.sax.Attributes; 29 import org.xml.sax.SAXException; 30 31 /** 32 * This class is not a public API. 33 * It is only public because it is used in other packages. 34 * This class converts SAX or SAX-like calls to a 35 * serialized document for xsl:output method of "text". 36 * @xsl.usage internal 37 */ 38 public final class ToTextStream extends ToStream 39 { 40 41 42 /** 43 * Default constructor. 44 */ 45 public ToTextStream() 46 { 47 super(); 48 } 49 50 51 52 /** 53 * Receive notification of the beginning of a document. 54 * 55 * <p>The SAX parser will invoke this method only once, before any 56 * other methods in this interface or in DTDHandler (except for 57 * setDocumentLocator).</p> 58 * 59 * @throws org.xml.sax.SAXException Any SAX exception, possibly 60 * wrapping another exception. 61 * 62 * @throws org.xml.sax.SAXException 63 */ 64 protected void startDocumentInternal() throws org.xml.sax.SAXException 65 { 66 super.startDocumentInternal(); 67 68 m_needToCallStartDocument = false; 69 70 // No action for the moment. 71 } 72 73 /** 74 * Receive notification of the end of a document. 75 * 76 * <p>The SAX parser will invoke this method only once, and it will 77 * be the last method invoked during the parse. The parser shall 78 * not invoke this method until it has either abandoned parsing 79 * (because of an unrecoverable error) or reached the end of 80 * input.</p> 81 * 82 * @throws org.xml.sax.SAXException Any SAX exception, possibly 83 * wrapping another exception. 84 * 85 * @throws org.xml.sax.SAXException 86 */ 87 public void endDocument() throws org.xml.sax.SAXException 88 { 89 flushPending(); 90 flushWriter(); 91 if (m_tracer != null) 92 super.fireEndDoc(); 93 } 94 95 /** 96 * Receive notification of the beginning of an element. 97 * 98 * <p>The Parser will invoke this method at the beginning of every 99 * element in the XML document; there will be a corresponding 100 * endElement() event for every startElement() event (even when the 101 * element is empty). All of the element's content will be 102 * reported, in order, before the corresponding endElement() 103 * event.</p> 104 * 105 * <p>If the element name has a namespace prefix, the prefix will 106 * still be attached. Note that the attribute list provided will 107 * contain only attributes with explicit values (specified or 108 * defaulted): #IMPLIED attributes will be omitted.</p> 109 * 110 * 111 * @param namespaceURI The Namespace URI, or the empty string if the 112 * element has no Namespace URI or if Namespace 113 * processing is not being performed. 114 * @param localName The local name (without prefix), or the 115 * empty string if Namespace processing is not being 116 * performed. 117 * @param name The qualified name (with prefix), or the 118 * empty string if qualified names are not available. 119 * @param atts The attributes attached to the element, if any. 120 * @throws org.xml.sax.SAXException Any SAX exception, possibly 121 * wrapping another exception. 122 * @see #endElement 123 * @see org.xml.sax.AttributeList 124 * 125 * @throws org.xml.sax.SAXException 126 */ 127 public void startElement( 128 String namespaceURI, String localName, String name, Attributes atts) 129 throws org.xml.sax.SAXException 130 { 131 // time to fire off startElement event 132 if (m_tracer != null) { 133 super.fireStartElem(name); 134 this.firePseudoAttributes(); 135 } 136 return; 137 } 138 139 /** 140 * Receive notification of the end of an element. 141 * 142 * <p>The SAX parser will invoke this method at the end of every 143 * element in the XML document; there will be a corresponding 144 * startElement() event for every endElement() event (even when the 145 * element is empty).</p> 146 * 147 * <p>If the element name has a namespace prefix, the prefix will 148 * still be attached to the name.</p> 149 * 150 * 151 * @param namespaceURI The Namespace URI, or the empty string if the 152 * element has no Namespace URI or if Namespace 153 * processing is not being performed. 154 * @param localName The local name (without prefix), or the 155 * empty string if Namespace processing is not being 156 * performed. 157 * @param name The qualified name (with prefix), or the 158 * empty string if qualified names are not available. 159 * @throws org.xml.sax.SAXException Any SAX exception, possibly 160 * wrapping another exception. 161 * 162 * @throws org.xml.sax.SAXException 163 */ 164 public void endElement(String namespaceURI, String localName, String name) 165 throws org.xml.sax.SAXException 166 { 167 if (m_tracer != null) 168 super.fireEndElem(name); 169 } 170 171 /** 172 * Receive notification of character data. 173 * 174 * <p>The Parser will call this method to report each chunk of 175 * character data. SAX parsers may return all contiguous character 176 * data in a single chunk, or they may split it into several 177 * chunks; however, all of the characters in any single event 178 * must come from the same external entity, so that the Locator 179 * provides useful information.</p> 180 * 181 * <p>The application must not attempt to read from the array 182 * outside of the specified range.</p> 183 * 184 * <p>Note that some parsers will report whitespace using the 185 * ignorableWhitespace() method rather than this one (validating 186 * parsers must do so).</p> 187 * 188 * @param ch The characters from the XML document. 189 * @param start The start position in the array. 190 * @param length The number of characters to read from the array. 191 * @throws org.xml.sax.SAXException Any SAX exception, possibly 192 * wrapping another exception. 193 * @see #ignorableWhitespace 194 * @see org.xml.sax.Locator 195 */ 196 public void characters(char ch[], int start, int length) 197 throws org.xml.sax.SAXException 198 { 199 200 flushPending(); 201 202 try 203 { 204 if (inTemporaryOutputState()) { 205 /* leave characters un-processed as we are 206 * creating temporary output, the output generated by 207 * this serializer will be input to a final serializer 208 * later on and it will do the processing in final 209 * output state (not temporary output state). 210 * 211 * A "temporary" ToTextStream serializer is used to 212 * evaluate attribute value templates (for example), 213 * and the result of evaluating such a thing 214 * is fed into a final serializer later on. 215 */ 216 m_writer.write(ch, start, length); 217 } 218 else { 219 // In final output state we do process the characters! 220 writeNormalizedChars(ch, start, length, m_lineSepUse); 221 } 222 223 if (m_tracer != null) 224 super.fireCharEvent(ch, start, length); 225 } 226 catch(IOException ioe) 227 { 228 throw new SAXException(ioe); 229 } 230 } 231 232 /** 233 * If available, when the disable-output-escaping attribute is used, 234 * output raw text without escaping. 235 * 236 * @param ch The characters from the XML document. 237 * @param start The start position in the array. 238 * @param length The number of characters to read from the array. 239 * 240 * @throws org.xml.sax.SAXException Any SAX exception, possibly 241 * wrapping another exception. 242 */ 243 public void charactersRaw(char ch[], int start, int length) 244 throws org.xml.sax.SAXException 245 { 246 247 try 248 { 249 writeNormalizedChars(ch, start, length, m_lineSepUse); 250 } 251 catch(IOException ioe) 252 { 253 throw new SAXException(ioe); 254 } 255 } 256 257 /** 258 * Normalize the characters, but don't escape. Different from 259 * SerializerToXML#writeNormalizedChars because it does not attempt to do 260 * XML escaping at all. 261 * 262 * @param ch The characters from the XML document. 263 * @param start The start position in the array. 264 * @param length The number of characters to read from the array. 265 * @param useLineSep true if the operating systems 266 * end-of-line separator should be output rather than a new-line character. 267 * 268 * @throws IOException 269 * @throws org.xml.sax.SAXException 270 */ 271 void writeNormalizedChars( 272 final char ch[], 273 final int start, 274 final int length, 275 final boolean useLineSep) 276 throws IOException, org.xml.sax.SAXException 277 { 278 final String encoding = getEncoding(); 279 final java.io.Writer writer = m_writer; 280 final int end = start + length; 281 282 /* copy a few "constants" before the loop for performance */ 283 final char S_LINEFEED = CharInfo.S_LINEFEED; 284 285 // This for() loop always increments i by one at the end 286 // of the loop. Additional increments of i adjust for when 287 // two input characters (a high/low UTF16 surrogate pair) 288 // are processed. 289 for (int i = start; i < end; i++) { 290 final char c = ch[i]; 291 292 if (S_LINEFEED == c && useLineSep) { 293 writer.write(m_lineSep, 0, m_lineSepLen); 294 // one input char processed 295 } else if (m_encodingInfo.isInEncoding(c)) { 296 writer.write(c); 297 // one input char processed 298 } else if (Encodings.isHighUTF16Surrogate(c)) { 299 final int codePoint = writeUTF16Surrogate(c, ch, i, end); 300 if (codePoint != 0) { 301 // I think we can just emit the message, 302 // not crash and burn. 303 final String integralValue = Integer.toString(codePoint); 304 final String msg = Utils.messages.createMessage( 305 MsgKey.ER_ILLEGAL_CHARACTER, 306 new Object[] { integralValue, encoding }); 307 308 //Older behavior was to throw the message, 309 //but newer gentler behavior is to write a message to System.err 310 //throw new SAXException(msg); 311 System.err.println(msg); 312 313 } 314 i++; // two input chars processed 315 } else { 316 // Don't know what to do with this char, it is 317 // not in the encoding and not a high char in 318 // a surrogate pair, so write out as an entity ref 319 if (encoding != null) { 320 /* The output encoding is known, 321 * so somthing is wrong. 322 */ 323 324 // not in the encoding, so write out a character reference 325 writer.write('&'); 326 writer.write('#'); 327 writer.write(Integer.toString(c)); 328 writer.write(';'); 329 330 // I think we can just emit the message, 331 // not crash and burn. 332 final String integralValue = Integer.toString(c); 333 final String msg = Utils.messages.createMessage( 334 MsgKey.ER_ILLEGAL_CHARACTER, 335 new Object[] { integralValue, encoding }); 336 337 //Older behavior was to throw the message, 338 //but newer gentler behavior is to write a message to System.err 339 //throw new SAXException(msg); 340 System.err.println(msg); 341 } else { 342 /* The output encoding is not known, 343 * so just write it out as-is. 344 */ 345 writer.write(c); 346 } 347 348 // one input char was processed 349 } 350 } 351 } 352 353 /** 354 * Receive notification of cdata. 355 * 356 * <p>The Parser will call this method to report each chunk of 357 * character data. SAX parsers may return all contiguous character 358 * data in a single chunk, or they may split it into several 359 * chunks; however, all of the characters in any single event 360 * must come from the same external entity, so that the Locator 361 * provides useful information.</p> 362 * 363 * <p>The application must not attempt to read from the array 364 * outside of the specified range.</p> 365 * 366 * <p>Note that some parsers will report whitespace using the 367 * ignorableWhitespace() method rather than this one (validating 368 * parsers must do so).</p> 369 * 370 * @param ch The characters from the XML document. 371 * @param start The start position in the array. 372 * @param length The number of characters to read from the array. 373 * @throws org.xml.sax.SAXException Any SAX exception, possibly 374 * wrapping another exception. 375 * @see #ignorableWhitespace 376 * @see org.xml.sax.Locator 377 */ 378 public void cdata(char ch[], int start, int length) 379 throws org.xml.sax.SAXException 380 { 381 try 382 { 383 writeNormalizedChars(ch, start, length, m_lineSepUse); 384 if (m_tracer != null) 385 super.fireCDATAEvent(ch, start, length); 386 } 387 catch(IOException ioe) 388 { 389 throw new SAXException(ioe); 390 } 391 } 392 393 /** 394 * Receive notification of ignorable whitespace in element content. 395 * 396 * <p>Validating Parsers must use this method to report each chunk 397 * of ignorable whitespace (see the W3C XML 1.0 recommendation, 398 * section 2.10): non-validating parsers may also use this method 399 * if they are capable of parsing and using content models.</p> 400 * 401 * <p>SAX parsers may return all contiguous whitespace in a single 402 * chunk, or they may split it into several chunks; however, all of 403 * the characters in any single event must come from the same 404 * external entity, so that the Locator provides useful 405 * information.</p> 406 * 407 * <p>The application must not attempt to read from the array 408 * outside of the specified range.</p> 409 * 410 * @param ch The characters from the XML document. 411 * @param start The start position in the array. 412 * @param length The number of characters to read from the array. 413 * @throws org.xml.sax.SAXException Any SAX exception, possibly 414 * wrapping another exception. 415 * @see #characters 416 * 417 * @throws org.xml.sax.SAXException 418 */ 419 public void ignorableWhitespace(char ch[], int start, int length) 420 throws org.xml.sax.SAXException 421 { 422 423 try 424 { 425 writeNormalizedChars(ch, start, length, m_lineSepUse); 426 } 427 catch(IOException ioe) 428 { 429 throw new SAXException(ioe); 430 } 431 } 432 433 /** 434 * Receive notification of a processing instruction. 435 * 436 * <p>The Parser will invoke this method once for each processing 437 * instruction found: note that processing instructions may occur 438 * before or after the main document element.</p> 439 * 440 * <p>A SAX parser should never report an XML declaration (XML 1.0, 441 * section 2.8) or a text declaration (XML 1.0, section 4.3.1) 442 * using this method.</p> 443 * 444 * @param target The processing instruction target. 445 * @param data The processing instruction data, or null if 446 * none was supplied. 447 * @throws org.xml.sax.SAXException Any SAX exception, possibly 448 * wrapping another exception. 449 * 450 * @throws org.xml.sax.SAXException 451 */ 452 public void processingInstruction(String target, String data) 453 throws org.xml.sax.SAXException 454 { 455 // flush anything pending first 456 flushPending(); 457 458 if (m_tracer != null) 459 super.fireEscapingEvent(target, data); 460 } 461 462 /** 463 * Called when a Comment is to be constructed. 464 * Note that Xalan will normally invoke the other version of this method. 465 * %REVIEW% In fact, is this one ever needed, or was it a mistake? 466 * 467 * @param data The comment data. 468 * @throws org.xml.sax.SAXException Any SAX exception, possibly 469 * wrapping another exception. 470 */ 471 public void comment(String data) throws org.xml.sax.SAXException 472 { 473 final int length = data.length(); 474 if (length > m_charsBuff.length) 475 { 476 m_charsBuff = new char[length*2 + 1]; 477 } 478 data.getChars(0, length, m_charsBuff, 0); 479 comment(m_charsBuff, 0, length); 480 } 481 482 /** 483 * Report an XML comment anywhere in the document. 484 * 485 * This callback will be used for comments inside or outside the 486 * document element, including comments in the external DTD 487 * subset (if read). 488 * 489 * @param ch An array holding the characters in the comment. 490 * @param start The starting position in the array. 491 * @param length The number of characters to use from the array. 492 * @throws org.xml.sax.SAXException The application may raise an exception. 493 */ 494 public void comment(char ch[], int start, int length) 495 throws org.xml.sax.SAXException 496 { 497 498 flushPending(); 499 if (m_tracer != null) 500 super.fireCommentEvent(ch, start, length); 501 } 502 503 /** 504 * Receive notivication of a entityReference. 505 * 506 * @param name non-null reference to the name of the entity. 507 * 508 * @throws org.xml.sax.SAXException 509 */ 510 public void entityReference(String name) throws org.xml.sax.SAXException 511 { 512 if (m_tracer != null) 513 super.fireEntityReference(name); 514 } 515 516 /** 517 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) 518 */ 519 public void addAttribute( 520 String uri, 521 String localName, 522 String rawName, 523 String type, 524 String value, 525 boolean XSLAttribute) 526 { 527 // do nothing, just forget all about the attribute 528 } 529 530 /** 531 * @see org.xml.sax.ext.LexicalHandler#endCDATA() 532 */ 533 public void endCDATA() throws SAXException 534 { 535 // do nothing 536 } 537 538 /** 539 * @see ExtendedContentHandler#endElement(String) 540 */ 541 public void endElement(String elemName) throws SAXException 542 { 543 if (m_tracer != null) 544 super.fireEndElem(elemName); 545 } 546 547 /** 548 * From XSLTC 549 */ 550 public void startElement( 551 String elementNamespaceURI, 552 String elementLocalName, 553 String elementName) 554 throws SAXException 555 { 556 if (m_needToCallStartDocument) 557 startDocumentInternal(); 558 // time to fire off startlement event. 559 if (m_tracer != null) { 560 super.fireStartElem(elementName); 561 this.firePseudoAttributes(); 562 } 563 564 return; 565 } 566 567 568 /** 569 * From XSLTC 570 */ 571 public void characters(String characters) 572 throws SAXException 573 { 574 final int length = characters.length(); 575 if (length > m_charsBuff.length) 576 { 577 m_charsBuff = new char[length*2 + 1]; 578 } 579 characters.getChars(0, length, m_charsBuff, 0); 580 characters(m_charsBuff, 0, length); 581 } 582 583 584 /** 585 * From XSLTC 586 */ 587 public void addAttribute(String name, String value) 588 { 589 // do nothing, forget about the attribute 590 } 591 592 /** 593 * Add a unique attribute 594 */ 595 public void addUniqueAttribute(String qName, String value, int flags) 596 throws SAXException 597 { 598 // do nothing, forget about the attribute 599 } 600 601 public boolean startPrefixMapping( 602 String prefix, 603 String uri, 604 boolean shouldFlush) 605 throws SAXException 606 { 607 // no namespace support for HTML 608 return false; 609 } 610 611 612 public void startPrefixMapping(String prefix, String uri) 613 throws org.xml.sax.SAXException 614 { 615 // no namespace support for HTML 616 } 617 618 619 public void namespaceAfterStartElement( 620 final String prefix, 621 final String uri) 622 throws SAXException 623 { 624 // no namespace support for HTML 625 } 626 627 public void flushPending() throws org.xml.sax.SAXException 628 { 629 if (m_needToCallStartDocument) 630 { 631 startDocumentInternal(); 632 m_needToCallStartDocument = false; 633 } 634 } 635 }