1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at

  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 /*
  21  * $Id: ToXMLStream.java,v 1.2.4.2 2005/09/15 12:01:25 suresh_emailid Exp $
  22  */
  23  package com.sun.org.apache.xml.internal.serializer;
  24 
  25 import java.io.IOException;
  26 
  27 import javax.xml.transform.ErrorListener;
  28 import javax.xml.transform.Result;
  29 import javax.xml.transform.Transformer;
  30 import javax.xml.transform.TransformerException;
  31 
  32 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  33 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  34 import org.xml.sax.SAXException;
  35 
  36 /**
  37  * This class converts SAX or SAX-like calls to a
  38  * serialized xml document.  The xsl:output method is "xml".
  39  *
  40  * This class is used explicitly in code generated by XSLTC,
  41  * so it is "public", but it should
  42  * be viewed as internal or package private, this is not an API.
  43  *
  44  * @xsl.usage internal
  45  */
  46 public final class ToXMLStream extends ToStream
  47 {
  48 
  49     /**
  50      * remembers if we need to write out "]]>" to close the CDATA
  51      */
  52     boolean m_cdataTagOpen = false;
  53 
  54 
  55     /**
  56      * Map that tells which XML characters should have special treatment, and it
  57      *  provides character to entity name lookup.
  58      */
  59     private static CharInfo m_xmlcharInfo =
  60 //      new CharInfo(CharInfo.XML_ENTITIES_RESOURCE);
  61         CharInfo.getCharInfoInternal(CharInfo.XML_ENTITIES_RESOURCE, Method.XML);
  62 
  63     /**
  64      * Default constructor.
  65      */
  66     public ToXMLStream()
  67     {
  68         m_charInfo = m_xmlcharInfo;
  69 
  70         initCDATA();
  71         // initialize namespaces
  72         m_prefixMap = new NamespaceMappings();
  73 
  74     }
  75 
  76     /**
  77      * Copy properties from another SerializerToXML.
  78      *
  79      * @param xmlListener non-null reference to a SerializerToXML object.
  80      */
  81     public void CopyFrom(ToXMLStream xmlListener)
  82     {
  83 
  84         m_writer = xmlListener.m_writer;
  85 
  86 
  87         // m_outputStream = xmlListener.m_outputStream;
  88         String encoding = xmlListener.getEncoding();
  89         setEncoding(encoding);
  90 
  91         setOmitXMLDeclaration(xmlListener.getOmitXMLDeclaration());
  92 
  93         m_ispreserve = xmlListener.m_ispreserve;
  94         m_preserves = xmlListener.m_preserves;






  95         m_isprevtext = xmlListener.m_isprevtext;
  96         m_doIndent = xmlListener.m_doIndent;
  97         setIndentAmount(xmlListener.getIndentAmount());
  98         m_startNewLine = xmlListener.m_startNewLine;
  99         m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl;
 100         setDoctypeSystem(xmlListener.getDoctypeSystem());
 101         setDoctypePublic(xmlListener.getDoctypePublic());
 102         setStandalone(xmlListener.getStandalone());
 103         setMediaType(xmlListener.getMediaType());
 104         m_maxCharacter = xmlListener.m_maxCharacter;
 105         m_encodingInfo = xmlListener.m_encodingInfo;
 106         m_spaceBeforeClose = xmlListener.m_spaceBeforeClose;
 107         m_cdataStartCalled = xmlListener.m_cdataStartCalled;
 108 
 109     }
 110 
 111     /**
 112      * Receive notification of the beginning of a document.
 113      *
 114      * @throws org.xml.sax.SAXException Any SAX exception, possibly
 115      *            wrapping another exception.
 116      *
 117      * @throws org.xml.sax.SAXException
 118      */
 119     public void startDocumentInternal() throws org.xml.sax.SAXException
 120     {
 121 
 122         if (m_needToCallStartDocument)
 123         {
 124             super.startDocumentInternal();
 125             m_needToCallStartDocument = false;
 126 
 127             if (m_inEntityRef)
 128                 return;
 129 
 130             m_needToOutputDocTypeDecl = true;
 131             m_startNewLine = false;
 132             /* The call to getXMLVersion() might emit an error message
 133              * and we should emit this message regardless of if we are
 134              * writing out an XML header or not.
 135              */
 136             if (getOmitXMLDeclaration() == false)
 137             {
 138                 String encoding = Encodings.getMimeEncoding(getEncoding());
 139                 String version = getVersion();
 140                 if (version == null)
 141                     version = "1.0";
 142                 String standalone;
 143 
 144                 if (m_standaloneWasSpecified)
 145                 {
 146                     standalone = " standalone=\"" + getStandalone() + "\"";
 147                 }
 148                 else
 149                 {
 150                     standalone = "";
 151                 }
 152 
 153                 try
 154                 {
 155                     final java.io.Writer writer = m_writer;
 156                     writer.write("<?xml version=\"");
 157                     writer.write(version);
 158                     writer.write("\" encoding=\"");
 159                     writer.write(encoding);
 160                     writer.write('\"');
 161                     writer.write(standalone);
 162                     writer.write("?>");
 163                     if (m_doIndent) {
 164                         if (m_standaloneWasSpecified
 165                                 || getDoctypePublic() != null
 166                                 || getDoctypeSystem() != null
 167                                 || m_isStandalone) {
 168                             // We almost never put a newline after the XML
 169                             // header because this XML could be used as
 170                             // an extenal general parsed entity
 171                             // and we don't know the context into which it
 172                             // will be used in the future.  Only when
 173                             // standalone, or a doctype system or public is
 174                             // specified are we free to insert a new line
 175                             // after the header.  Is it even worth bothering
 176                             // in these rare cases?
 177                             writer.write(m_lineSep, 0, m_lineSepLen);
 178                         }
 179                     }
 180                 }
 181                 catch(IOException e)
 182                 {
 183                     throw new SAXException(e);
 184                 }
 185 
 186             }
 187         }
 188     }
 189 
 190     /**
 191      * Receive notification of the end of a document.
 192      *
 193      * @throws org.xml.sax.SAXException Any SAX exception, possibly
 194      *            wrapping another exception.
 195      *
 196      * @throws org.xml.sax.SAXException
 197      */
 198     public void endDocument() throws org.xml.sax.SAXException
 199     {

 200         flushPending();
 201         if (m_doIndent && !m_isprevtext)
 202         {
 203             try
 204             {
 205             outputLineSep();
 206             }
 207             catch(IOException e)
 208             {
 209                 throw new SAXException(e);
 210             }
 211         }
 212 
 213         flushWriter();
 214 
 215         if (m_tracer != null)
 216             super.fireEndDoc();
 217     }
 218 
 219     /**
 220      * Starts a whitespace preserving section. All characters printed
 221      * within a preserving section are printed without indentation and
 222      * without consolidating multiple spaces. This is equivalent to
 223      * the <tt>xml:space=&quot;preserve&quot;</tt> attribute. Only XML
 224      * and HTML serializers need to support this method.
 225      * <p>
 226      * The contents of the whitespace preserving section will be delivered
 227      * through the regular <tt>characters</tt> event.
 228      *
 229      * @throws org.xml.sax.SAXException
 230      */
 231     public void startPreserving() throws org.xml.sax.SAXException
 232     {
 233 
 234         // Not sure this is really what we want.  -sb
 235         m_preserves.push(true);
 236 
 237         m_ispreserve = true;
 238     }
 239 
 240     /**
 241      * Ends a whitespace preserving section.
 242      *
 243      * @see #startPreserving
 244      *
 245      * @throws org.xml.sax.SAXException
 246      */
 247     public void endPreserving() throws org.xml.sax.SAXException
 248     {
 249 
 250         // Not sure this is really what we want.  -sb
 251         m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
 252     }
 253 
 254     /**
 255      * Receive notification of a processing instruction.
 256      *
 257      * @param target The processing instruction target.
 258      * @param data The processing instruction data, or null if
 259      *        none was supplied.
 260      * @throws org.xml.sax.SAXException Any SAX exception, possibly
 261      *            wrapping another exception.
 262      *
 263      * @throws org.xml.sax.SAXException
 264      */
 265     public void processingInstruction(String target, String data)
 266         throws org.xml.sax.SAXException
 267     {
 268         if (m_inEntityRef)
 269             return;
 270 


 271         flushPending();
 272 
 273         if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
 274         {
 275             startNonEscaping();
 276         }
 277         else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
 278         {
 279             endNonEscaping();
 280         }
 281         else
 282         {
 283             try
 284             {
 285                 if (m_elemContext.m_startTagOpen)
 286                 {
 287                     closeStartTag();
 288                     m_elemContext.m_startTagOpen = false;
 289                 }
 290                 else if (m_needToCallStartDocument)
 291                     startDocumentInternal();
 292 
 293                 if (shouldIndent())
 294                     indent();
 295 
 296                 final java.io.Writer writer = m_writer;
 297                 writer.write("<?");
 298                 writer.write(target);
 299 
 300                 if (data.length() > 0
 301                     && !Character.isSpaceChar(data.charAt(0)))
 302                     writer.write(' ');
 303 
 304                 int indexOfQLT = data.indexOf("?>");
 305 
 306                 if (indexOfQLT >= 0)
 307                 {
 308 
 309                     // See XSLT spec on error recovery of "?>" in PIs.
 310                     if (indexOfQLT > 0)
 311                     {
 312                         writer.write(data.substring(0, indexOfQLT));
 313                     }
 314 
 315                     writer.write("? >"); // add space between.
 316 
 317                     if ((indexOfQLT + 2) < data.length())
 318                     {
 319                         writer.write(data.substring(indexOfQLT + 2));
 320                     }
 321                 }
 322                 else
 323                 {
 324                     writer.write(data);
 325                 }
 326 
 327                 writer.write('?');
 328                 writer.write('>');
 329 
 330                 /**
 331                  * Before Xalan 1497, a newline char was printed out if not inside of an
 332                  * element. The whitespace is not significant is the output is standalone
 333                 */
 334                 if (m_elemContext.m_currentElemDepth <= 0 && m_isStandalone)
 335                     writer.write(m_lineSep, 0, m_lineSepLen);
 336 
 337 
 338                 /*
 339                  * Don't write out any indentation whitespace now,
 340                  * because there may be non-whitespace text after this.
 341                  *
 342                  * Simply mark that at this point if we do decide
 343                  * to indent that we should
 344                  * add a newline on the end of the current line before
 345                  * the indentation at the start of the next line.
 346                  */
 347                 m_startNewLine = true;
 348             }
 349             catch(IOException e)
 350             {
 351                 throw new SAXException(e);
 352             }
 353         }
 354 
 355         if (m_tracer != null)
 356             super.fireEscapingEvent(target, data);
 357     }
 358 
 359     /**
 360      * Receive notivication of a entityReference.
 361      *
 362      * @param name The name of the entity.
 363      *
 364      * @throws org.xml.sax.SAXException
 365      */
 366     public void entityReference(String name) throws org.xml.sax.SAXException
 367     {
 368         if (m_elemContext.m_startTagOpen)
 369         {
 370             closeStartTag();
 371             m_elemContext.m_startTagOpen = false;
 372         }
 373 
 374         try
 375         {
 376             if (shouldIndent())
 377                 indent();
 378 
 379             final java.io.Writer writer = m_writer;
 380             writer.write('&');
 381             writer.write(name);
 382             writer.write(';');
 383         }
 384         catch(IOException e)
 385         {
 386             throw new SAXException(e);
 387         }
 388 
 389         if (m_tracer != null)
 390             super.fireEntityReference(name);
 391     }
 392 
 393     /**
 394      * This method is used to add an attribute to the currently open element.
 395      * The caller has guaranted that this attribute is unique, which means that it
 396      * not been seen before and will not be seen again.
 397      *
 398      * @param name the qualified name of the attribute
 399      * @param value the value of the attribute which can contain only
 400      * ASCII printable characters characters in the range 32 to 127 inclusive.
 401      * @param flags the bit values of this integer give optimization information.
 402      */
 403     public void addUniqueAttribute(String name, String value, int flags)
 404         throws SAXException
 405     {
 406         if (m_elemContext.m_startTagOpen)
 407         {
 408 
 409             try
 410             {
 411                 final String patchedName = patchName(name);
 412                 final java.io.Writer writer = m_writer;
 413                 if ((flags & NO_BAD_CHARS) > 0 && m_xmlcharInfo.onlyQuotAmpLtGt)
 414                 {
 415                     // "flags" has indicated that the characters
 416                     // '>'  '<'   '&'  and '"' are not in the value and
 417                     // m_htmlcharInfo has recorded that there are no other
 418                     // entities in the range 32 to 127 so we write out the
 419                     // value directly
 420 
 421                     writer.write(' ');
 422                     writer.write(patchedName);
 423                     writer.write("=\"");
 424                     writer.write(value);
 425                     writer.write('"');
 426                 }
 427                 else
 428                 {
 429                     writer.write(' ');
 430                     writer.write(patchedName);
 431                     writer.write("=\"");
 432                     writeAttrString(writer, value, this.getEncoding());
 433                     writer.write('"');
 434                 }
 435             } catch (IOException e) {
 436                 throw new SAXException(e);
 437             }
 438         }
 439     }
 440 
 441     /**
 442      * Add an attribute to the current element.
 443      * @param uri the URI associated with the element name
 444      * @param localName local part of the attribute name
 445      * @param rawName   prefix:localName
 446      * @param type
 447      * @param value the value of the attribute
 448      * @param xslAttribute true if this attribute is from an xsl:attribute,
 449      * false if declared within the elements opening tag.
 450      * @throws SAXException
 451      */
 452     public void addAttribute(
 453         String uri,
 454         String localName,
 455         String rawName,
 456         String type,
 457         String value,
 458         boolean xslAttribute)
 459         throws SAXException
 460     {
 461         if (m_elemContext.m_startTagOpen)
 462         {
 463             boolean was_added = addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
 464 
 465 
 466             /*
 467              * We don't run this block of code if:
 468              * 1. The attribute value was only replaced (was_added is false).
 469              * 2. The attribute is from an xsl:attribute element (that is handled
 470              *    in the addAttributeAlways() call just above.
 471              * 3. The name starts with "xmlns", i.e. it is a namespace declaration.
 472              */
 473             if (was_added && !xslAttribute && !rawName.startsWith("xmlns"))
 474             {
 475                 String prefixUsed =
 476                     ensureAttributesNamespaceIsDeclared(
 477                         uri,
 478                         localName,
 479                         rawName);
 480                 if (prefixUsed != null
 481                     && rawName != null
 482                     && !rawName.startsWith(prefixUsed))
 483                 {
 484                     // use a different raw name, with the prefix used in the
 485                     // generated namespace declaration
 486                     rawName = prefixUsed + ":" + localName;
 487 
 488                 }
 489             }
 490             addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
 491         }
 492         else
 493         {
 494             /*
 495              * The startTag is closed, yet we are adding an attribute?
 496              *
 497              * Section: 7.1.3 Creating Attributes Adding an attribute to an
 498              * element after a PI (for example) has been added to it is an
 499              * error. The attributes can be ignored. The spec doesn't explicitly
 500              * say this is disallowed, as it does for child elements, but it
 501              * makes sense to have the same treatment.
 502              *
 503              * We choose to ignore the attribute which is added too late.
 504              */
 505             // Generate a warning of the ignored attributes
 506 
 507             // Create the warning message
 508             String msg = Utils.messages.createMessage(
 509                     MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,new Object[]{ localName });
 510 
 511             try {
 512                 // Prepare to issue the warning message
 513                 Transformer tran = super.getTransformer();
 514                 ErrorListener errHandler = tran.getErrorListener();
 515 
 516 
 517                 // Issue the warning message
 518                 if (null != errHandler && m_sourceLocator != null)
 519                   errHandler.warning(new TransformerException(msg, m_sourceLocator));
 520                 else
 521                   System.out.println(msg);
 522                 }
 523             catch (Exception e){}
 524         }
 525     }
 526 
 527     /**
 528      * @see ExtendedContentHandler#endElement(String)
 529      */
 530     public void endElement(String elemName) throws SAXException
 531     {
 532         endElement(null, null, elemName);
 533     }
 534 
 535     /**
 536      * This method is used to notify the serializer of a namespace mapping (or node)
 537      * that applies to the current element whose startElement() call has already been seen.
 538      * The official SAX startPrefixMapping(prefix,uri) is to define a mapping for a child
 539      * element that is soon to be seen with a startElement() call. The official SAX call
 540      * does not apply to the current element, hence the reason for this method.
 541      */
 542     public void namespaceAfterStartElement(
 543         final String prefix,
 544         final String uri)
 545         throws SAXException
 546     {
 547 
 548         // hack for XSLTC with finding URI for default namespace
 549         if (m_elemContext.m_elementURI == null)
 550         {
 551             String prefix1 = getPrefixPart(m_elemContext.m_elementName);
 552             if (prefix1 == null && EMPTYSTRING.equals(prefix))
 553             {
 554                 // the elements URI is not known yet, and it
 555                 // doesn't have a prefix, and we are currently
 556                 // setting the uri for prefix "", so we have
 557                 // the uri for the element... lets remember it
 558                 m_elemContext.m_elementURI = uri;
 559             }
 560         }
 561         startPrefixMapping(prefix,uri,false);
 562         return;
 563 
 564     }
 565 
 566     /**
 567      * From XSLTC
 568      * Declare a prefix to point to a namespace URI. Inform SAX handler
 569      * if this is a new prefix mapping.
 570      */
 571     protected boolean pushNamespace(String prefix, String uri)
 572     {
 573         try
 574         {
 575             if (m_prefixMap.pushNamespace(
 576                 prefix, uri, m_elemContext.m_currentElemDepth))
 577             {
 578                 startPrefixMapping(prefix, uri);
 579                 return true;
 580             }
 581         }
 582         catch (SAXException e)
 583         {
 584             // falls through
 585         }
 586         return false;
 587     }
 588     /**
 589      * Try's to reset the super class and reset this class for
 590      * re-use, so that you don't need to create a new serializer
 591      * (mostly for performance reasons).
 592      *
 593      * @return true if the class was successfuly reset.
 594      */
 595     public boolean reset()
 596     {
 597         boolean wasReset = false;
 598         if (super.reset())
 599         {
 600             resetToXMLStream();
 601             wasReset = true;
 602         }
 603         return wasReset;
 604     }
 605 
 606     /**
 607      * Reset all of the fields owned by ToStream class
 608      *
 609      */
 610     private void resetToXMLStream()
 611     {
 612         this.m_cdataTagOpen = false;
 613 
 614     }
 615 
 616     /**
 617      * This method checks for the XML version of output document.
 618      * If XML version of output document is not specified, then output
 619      * document is of version XML 1.0.
 620      * If XML version of output doucment is specified, but it is not either
 621      * XML 1.0 or XML 1.1, a warning message is generated, the XML Version of
 622      * output document is set to XML 1.0 and processing continues.
 623      * @return string (XML version)
 624      */
 625     private String getXMLVersion()
 626     {
 627         String xmlVersion = getVersion();
 628         if(xmlVersion == null || xmlVersion.equals(XMLVERSION10))
 629         {
 630             xmlVersion = XMLVERSION10;
 631         }
 632         else if(xmlVersion.equals(XMLVERSION11))
 633         {
 634             xmlVersion = XMLVERSION11;
 635         }
 636         else
 637         {
 638             String msg = Utils.messages.createMessage(
 639                                MsgKey.ER_XML_VERSION_NOT_SUPPORTED,new Object[]{ xmlVersion });
 640             try
 641             {
 642                 // Prepare to issue the warning message
 643                 Transformer tran = super.getTransformer();
 644                 ErrorListener errHandler = tran.getErrorListener();
 645                 // Issue the warning message
 646                 if (null != errHandler && m_sourceLocator != null)
 647                     errHandler.warning(new TransformerException(msg, m_sourceLocator));
 648                 else
 649                     System.out.println(msg);
 650             }
 651             catch (Exception e){}
 652             xmlVersion = XMLVERSION10;
 653         }
 654         return xmlVersion;
 655     }
 656 }
--- EOF ---