1 /* 2 * Copyright (c) 2014, 2019, 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 java.io.IOException; 24 25 import javax.xml.transform.ErrorListener; 26 import javax.xml.transform.Result; 27 import javax.xml.transform.Transformer; 28 import javax.xml.transform.TransformerException; 29 30 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey; 31 import com.sun.org.apache.xml.internal.serializer.utils.Utils; 32 import org.xml.sax.SAXException; 33 34 /** 35 * This class converts SAX or SAX-like calls to a 36 * serialized xml document. The xsl:output method is "xml". 37 * 38 * This class is used explicitly in code generated by XSLTC, 39 * so it is "public", but it should 40 * be viewed as internal or package private, this is not an API. 41 * 42 * @xsl.usage internal 43 * @LastModified: July 2019 44 */ 45 public final class ToXMLStream extends ToStream 46 { 47 48 /** 49 * remembers if we need to write out "]]>" to close the CDATA 50 */ 51 boolean m_cdataTagOpen = false; 52 53 54 /** 55 * Map that tells which XML characters should have special treatment, and it 56 * provides character to entity name lookup. 57 */ 58 private static CharInfo m_xmlcharInfo = 59 // new CharInfo(CharInfo.XML_ENTITIES_RESOURCE); 60 CharInfo.getCharInfoInternal(CharInfo.XML_ENTITIES_RESOURCE, Method.XML); 61 62 /** 63 * Default constructor. 64 */ 65 public ToXMLStream() 66 { 67 m_charInfo = m_xmlcharInfo; 68 69 initCDATA(); 70 // initialize namespaces 71 m_prefixMap = new NamespaceMappings(); 72 73 } 74 75 /** 76 * Copy properties from another SerializerToXML. 77 * 78 * @param xmlListener non-null reference to a SerializerToXML object. 79 */ 80 public void CopyFrom(ToXMLStream xmlListener) 81 { 82 83 m_writer = xmlListener.m_writer; 84 85 86 // m_outputStream = xmlListener.m_outputStream; 87 String encoding = xmlListener.getEncoding(); 88 setEncoding(encoding); 89 90 setOmitXMLDeclaration(xmlListener.getOmitXMLDeclaration()); 91 92 m_ispreserveSpace = xmlListener.m_ispreserveSpace; 93 m_preserveSpaces = xmlListener.m_preserveSpaces; 94 m_childNodeNum = xmlListener.m_childNodeNum; 95 m_childNodeNumStack = xmlListener.m_childNodeNumStack; 96 m_charactersBuffer = xmlListener.m_charactersBuffer; 97 m_inEntityRef = xmlListener.m_inEntityRef; 98 m_isprevtext = xmlListener.m_isprevtext; 99 m_doIndent = xmlListener.m_doIndent; 100 setIndentAmount(xmlListener.getIndentAmount()); 101 m_startNewLine = xmlListener.m_startNewLine; 102 m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl; 103 setDoctypeSystem(xmlListener.getDoctypeSystem()); 104 setDoctypePublic(xmlListener.getDoctypePublic()); 105 setStandalone(xmlListener.getStandalone()); 106 setMediaType(xmlListener.getMediaType()); 107 m_maxCharacter = xmlListener.m_maxCharacter; 108 m_encodingInfo = xmlListener.m_encodingInfo; 109 m_spaceBeforeClose = xmlListener.m_spaceBeforeClose; 110 m_cdataStartCalled = xmlListener.m_cdataStartCalled; 111 112 } 113 114 /** 115 * Receive notification of the beginning of a document. 116 * 117 * @throws org.xml.sax.SAXException Any SAX exception, possibly 118 * wrapping another exception. 119 * 120 * @throws org.xml.sax.SAXException 121 */ 122 public void startDocumentInternal() throws org.xml.sax.SAXException 123 { 124 125 if (m_needToCallStartDocument) 126 { 127 super.startDocumentInternal(); 128 m_needToCallStartDocument = false; 129 130 if (isInEntityRef()) 131 return; 132 133 m_needToOutputDocTypeDecl = true; 134 m_startNewLine = false; 135 /* The call to getXMLVersion() might emit an error message 136 * and we should emit this message regardless of if we are 137 * writing out an XML header or not. 138 */ 139 if (getOmitXMLDeclaration() == false) 140 { 141 String encoding = Encodings.getMimeEncoding(getEncoding()); 142 String version = getVersion(); 143 if (version == null) 144 version = "1.0"; 145 String standalone; 146 147 if (m_standaloneWasSpecified) 148 { 149 standalone = " standalone=\"" + getStandalone() + "\""; 150 } 151 else 152 { 153 standalone = ""; 154 } 155 156 try 157 { 158 final java.io.Writer writer = m_writer; 159 writer.write("<?xml version=\""); 160 writer.write(version); 161 writer.write("\" encoding=\""); 162 writer.write(encoding); 163 writer.write('\"'); 164 writer.write(standalone); 165 writer.write("?>"); 166 if (m_doIndent) { 167 if (m_standaloneWasSpecified 168 || getDoctypePublic() != null 169 || getDoctypeSystem() != null 170 || m_isStandalone) { 171 // We almost never put a newline after the XML 172 // header because this XML could be used as 173 // an extenal general parsed entity 174 // and we don't know the context into which it 175 // will be used in the future. Only when 176 // standalone, or a doctype system or public is 177 // specified are we free to insert a new line 178 // after the header. Is it even worth bothering 179 // in these rare cases? 180 writer.write(m_lineSep, 0, m_lineSepLen); 181 } 182 } 183 } 184 catch(IOException e) 185 { 186 throw new SAXException(e); 187 } 188 189 } 190 } 191 } 192 193 /** 194 * Receive notification of the end of a document. 195 * 196 * @throws org.xml.sax.SAXException Any SAX exception, possibly 197 * wrapping another exception. 198 * 199 * @throws org.xml.sax.SAXException 200 */ 201 public void endDocument() throws org.xml.sax.SAXException 202 { 203 if (m_doIndent) { 204 flushCharactersBuffer(false); 205 } 206 flushPending(); 207 if (m_doIndent && !m_isprevtext) 208 { 209 try 210 { 211 outputLineSep(); 212 } 213 catch(IOException e) 214 { 215 throw new SAXException(e); 216 } 217 } 218 219 flushWriter(); 220 221 if (m_tracer != null) 222 super.fireEndDoc(); 223 } 224 225 /** 226 * Starts a whitespace preserving section. All characters printed 227 * within a preserving section are printed without indentation and 228 * without consolidating multiple spaces. This is equivalent to 229 * the <tt>xml:space="preserve"</tt> attribute. Only XML 230 * and HTML serializers need to support this method. 231 * <p> 232 * The contents of the whitespace preserving section will be delivered 233 * through the regular <tt>characters</tt> event. 234 * 235 * @throws org.xml.sax.SAXException 236 */ 237 public void startPreserving() throws org.xml.sax.SAXException 238 { 239 } 240 241 /** 242 * Ends a whitespace preserving section. 243 * 244 * @see #startPreserving 245 * 246 * @throws org.xml.sax.SAXException 247 */ 248 public void endPreserving() throws org.xml.sax.SAXException 249 { 250 } 251 252 /** 253 * Receive notification of a processing instruction. 254 * 255 * @param target The processing instruction target. 256 * @param data The processing instruction data, or null if 257 * none was supplied. 258 * @throws org.xml.sax.SAXException Any SAX exception, possibly 259 * wrapping another exception. 260 * 261 * @throws org.xml.sax.SAXException 262 */ 263 public void processingInstruction(String target, String data) 264 throws org.xml.sax.SAXException 265 { 266 if (isInEntityRef()) 267 return; 268 269 if (m_doIndent) { 270 m_childNodeNum++; 271 flushCharactersBuffer(false); 272 } 273 flushPending(); 274 275 if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING)) 276 { 277 startNonEscaping(); 278 } 279 else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING)) 280 { 281 endNonEscaping(); 282 } 283 else 284 { 285 try 286 { 287 if (m_elemContext.m_startTagOpen) 288 { 289 closeStartTag(); 290 m_elemContext.m_startTagOpen = false; 291 } 292 else if (m_needToCallStartDocument) 293 startDocumentInternal(); 294 295 if (shouldIndent()) 296 indent(); 297 298 final java.io.Writer writer = m_writer; 299 writer.write("<?"); 300 writer.write(target); 301 302 if (data.length() > 0 303 && !Character.isSpaceChar(data.charAt(0))) 304 writer.write(' '); 305 306 int indexOfQLT = data.indexOf("?>"); 307 308 if (indexOfQLT >= 0) 309 { 310 311 // See XSLT spec on error recovery of "?>" in PIs. 312 if (indexOfQLT > 0) 313 { 314 writer.write(data.substring(0, indexOfQLT)); 315 } 316 317 writer.write("? >"); // add space between. 318 319 if ((indexOfQLT + 2) < data.length()) 320 { 321 writer.write(data.substring(indexOfQLT + 2)); 322 } 323 } 324 else 325 { 326 writer.write(data); 327 } 328 329 writer.write('?'); 330 writer.write('>'); 331 332 /** 333 * Before Xalan 1497, a newline char was printed out if not inside of an 334 * element. The whitespace is not significant is the output is standalone 335 */ 336 if (m_elemContext.m_currentElemDepth <= 0 && m_isStandalone) 337 writer.write(m_lineSep, 0, m_lineSepLen); 338 339 340 /* 341 * Don't write out any indentation whitespace now, 342 * because there may be non-whitespace text after this. 343 * 344 * Simply mark that at this point if we do decide 345 * to indent that we should 346 * add a newline on the end of the current line before 347 * the indentation at the start of the next line. 348 */ 349 m_startNewLine = true; 350 } 351 catch(IOException e) 352 { 353 throw new SAXException(e); 354 } 355 } 356 357 if (m_tracer != null) 358 super.fireEscapingEvent(target, data); 359 } 360 361 /** 362 * Receive notivication of a entityReference. 363 * 364 * @param name The name of the entity. 365 * 366 * @throws org.xml.sax.SAXException 367 */ 368 public void entityReference(String name) throws org.xml.sax.SAXException 369 { 370 if (m_elemContext.m_startTagOpen) 371 { 372 closeStartTag(); 373 m_elemContext.m_startTagOpen = false; 374 } 375 376 try 377 { 378 if (shouldIndent()) 379 indent(); 380 381 final java.io.Writer writer = m_writer; 382 writer.write('&'); 383 writer.write(name); 384 writer.write(';'); 385 } 386 catch(IOException e) 387 { 388 throw new SAXException(e); 389 } 390 391 if (m_tracer != null) 392 super.fireEntityReference(name); 393 } 394 395 /** 396 * This method is used to add an attribute to the currently open element. 397 * The caller has guaranted that this attribute is unique, which means that it 398 * not been seen before and will not be seen again. 399 * 400 * @param name the qualified name of the attribute 401 * @param value the value of the attribute which can contain only 402 * ASCII printable characters characters in the range 32 to 127 inclusive. 403 * @param flags the bit values of this integer give optimization information. 404 */ 405 public void addUniqueAttribute(String name, String value, int flags) 406 throws SAXException 407 { 408 if (m_elemContext.m_startTagOpen) 409 { 410 411 try 412 { 413 final String patchedName = patchName(name); 414 final java.io.Writer writer = m_writer; 415 if ((flags & NO_BAD_CHARS) > 0 && m_xmlcharInfo.onlyQuotAmpLtGt) 416 { 417 // "flags" has indicated that the characters 418 // '>' '<' '&' and '"' are not in the value and 419 // m_htmlcharInfo has recorded that there are no other 420 // entities in the range 32 to 127 so we write out the 421 // value directly 422 423 writer.write(' '); 424 writer.write(patchedName); 425 writer.write("=\""); 426 writer.write(value); 427 writer.write('"'); 428 } 429 else 430 { 431 writer.write(' '); 432 writer.write(patchedName); 433 writer.write("=\""); 434 writeAttrString(writer, value, this.getEncoding()); 435 writer.write('"'); 436 } 437 } catch (IOException e) { 438 throw new SAXException(e); 439 } 440 } 441 } 442 443 /** 444 * Add an attribute to the current element. 445 * @param uri the URI associated with the element name 446 * @param localName local part of the attribute name 447 * @param rawName prefix:localName 448 * @param type 449 * @param value the value of the attribute 450 * @param xslAttribute true if this attribute is from an xsl:attribute, 451 * false if declared within the elements opening tag. 452 * @throws SAXException 453 */ 454 public void addAttribute( 455 String uri, 456 String localName, 457 String rawName, 458 String type, 459 String value, 460 boolean xslAttribute) 461 throws SAXException 462 { 463 if (m_elemContext.m_startTagOpen) 464 { 465 boolean was_added = addAttributeAlways(uri, localName, rawName, type, value, xslAttribute); 466 467 468 /* 469 * We don't run this block of code if: 470 * 1. The attribute value was only replaced (was_added is false). 471 * 2. The attribute is from an xsl:attribute element (that is handled 472 * in the addAttributeAlways() call just above. 473 * 3. The name starts with "xmlns", i.e. it is a namespace declaration. 474 */ 475 if (was_added && !xslAttribute && !rawName.startsWith("xmlns")) 476 { 477 String prefixUsed = 478 ensureAttributesNamespaceIsDeclared( 479 uri, 480 localName, 481 rawName); 482 if (prefixUsed != null 483 && rawName != null 484 && !rawName.startsWith(prefixUsed)) 485 { 486 // use a different raw name, with the prefix used in the 487 // generated namespace declaration 488 rawName = prefixUsed + ":" + localName; 489 490 } 491 } 492 addAttributeAlways(uri, localName, rawName, type, value, xslAttribute); 493 } 494 else 495 { 496 /* 497 * The startTag is closed, yet we are adding an attribute? 498 * 499 * Section: 7.1.3 Creating Attributes Adding an attribute to an 500 * element after a PI (for example) has been added to it is an 501 * error. The attributes can be ignored. The spec doesn't explicitly 502 * say this is disallowed, as it does for child elements, but it 503 * makes sense to have the same treatment. 504 * 505 * We choose to ignore the attribute which is added too late. 506 */ 507 // Generate a warning of the ignored attributes 508 509 // Create the warning message 510 String msg = Utils.messages.createMessage( 511 MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,new Object[]{ localName }); 512 513 try { 514 // Prepare to issue the warning message 515 Transformer tran = super.getTransformer(); 516 ErrorListener errHandler = tran.getErrorListener(); 517 518 519 // Issue the warning message 520 if (null != errHandler && m_sourceLocator != null) 521 errHandler.warning(new TransformerException(msg, m_sourceLocator)); 522 else 523 System.out.println(msg); 524 } 525 catch (Exception e){} 526 } 527 } 528 529 /** 530 * @see ExtendedContentHandler#endElement(String) 531 */ 532 public void endElement(String elemName) throws SAXException 533 { 534 endElement(null, null, elemName); 535 } 536 537 /** 538 * This method is used to notify the serializer of a namespace mapping (or node) 539 * that applies to the current element whose startElement() call has already been seen. 540 * The official SAX startPrefixMapping(prefix,uri) is to define a mapping for a child 541 * element that is soon to be seen with a startElement() call. The official SAX call 542 * does not apply to the current element, hence the reason for this method. 543 */ 544 public void namespaceAfterStartElement( 545 final String prefix, 546 final String uri) 547 throws SAXException 548 { 549 550 // hack for XSLTC with finding URI for default namespace 551 if (m_elemContext.m_elementURI == null) 552 { 553 String prefix1 = getPrefixPart(m_elemContext.m_elementName); 554 if (prefix1 == null && EMPTYSTRING.equals(prefix)) 555 { 556 // the elements URI is not known yet, and it 557 // doesn't have a prefix, and we are currently 558 // setting the uri for prefix "", so we have 559 // the uri for the element... lets remember it 560 m_elemContext.m_elementURI = uri; 561 } 562 } 563 startPrefixMapping(prefix,uri,false); 564 return; 565 566 } 567 568 /** 569 * From XSLTC 570 * Declare a prefix to point to a namespace URI. Inform SAX handler 571 * if this is a new prefix mapping. 572 */ 573 protected boolean pushNamespace(String prefix, String uri) 574 { 575 try 576 { 577 if (m_prefixMap.pushNamespace( 578 prefix, uri, m_elemContext.m_currentElemDepth)) 579 { 580 startPrefixMapping(prefix, uri); 581 return true; 582 } 583 } 584 catch (SAXException e) 585 { 586 // falls through 587 } 588 return false; 589 } 590 /** 591 * Try's to reset the super class and reset this class for 592 * re-use, so that you don't need to create a new serializer 593 * (mostly for performance reasons). 594 * 595 * @return true if the class was successfuly reset. 596 */ 597 public boolean reset() 598 { 599 boolean wasReset = false; 600 if (super.reset()) 601 { 602 resetToXMLStream(); 603 wasReset = true; 604 } 605 return wasReset; 606 } 607 608 /** 609 * Reset all of the fields owned by ToStream class 610 * 611 */ 612 private void resetToXMLStream() 613 { 614 this.m_cdataTagOpen = false; 615 616 } 617 618 /** 619 * This method checks for the XML version of output document. 620 * If XML version of output document is not specified, then output 621 * document is of version XML 1.0. 622 * If XML version of output doucment is specified, but it is not either 623 * XML 1.0 or XML 1.1, a warning message is generated, the XML Version of 624 * output document is set to XML 1.0 and processing continues. 625 * @return string (XML version) 626 */ 627 private String getXMLVersion() 628 { 629 String xmlVersion = getVersion(); 630 if(xmlVersion == null || xmlVersion.equals(XMLVERSION10)) 631 { 632 xmlVersion = XMLVERSION10; 633 } 634 else if(xmlVersion.equals(XMLVERSION11)) 635 { 636 xmlVersion = XMLVERSION11; 637 } 638 else 639 { 640 String msg = Utils.messages.createMessage( 641 MsgKey.ER_XML_VERSION_NOT_SUPPORTED,new Object[]{ xmlVersion }); 642 try 643 { 644 // Prepare to issue the warning message 645 Transformer tran = super.getTransformer(); 646 ErrorListener errHandler = tran.getErrorListener(); 647 // Issue the warning message 648 if (null != errHandler && m_sourceLocator != null) 649 errHandler.warning(new TransformerException(msg, m_sourceLocator)); 650 else 651 System.out.println(msg); 652 } 653 catch (Exception e){} 654 xmlVersion = XMLVERSION10; 655 } 656 return xmlVersion; 657 } 658 }