src/com/sun/org/apache/xml/internal/serializer/ToStream.java

Print this page


   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: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid Exp $
  22  */
  23 package com.sun.org.apache.xml.internal.serializer;
  24 
  25 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  26 import java.io.IOException;
  27 import java.io.OutputStream;

  28 import java.io.UnsupportedEncodingException;
  29 import java.io.Writer;


  30 import java.util.Properties;

  31 import java.util.StringTokenizer;
  32 import java.util.Vector;
  33 
  34 import javax.xml.transform.ErrorListener;
  35 import javax.xml.transform.OutputKeys;
  36 import javax.xml.transform.Transformer;
  37 import javax.xml.transform.TransformerException;
  38 
  39 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  40 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  41 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
  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 //import com.sun.media.sound.IESecurity;
  48 
  49 /**
  50  * This abstract class is a base class for other stream
  51  * serializers (xml, html, text ...) that write output to a stream.
  52  *


 169     protected boolean m_spaceBeforeClose = false;
 170 
 171     /**
 172      * Flag to signal that a newline should be added.
 173      *
 174      * Used only in indent() which is called only if m_doIndent is true.
 175      * If m_doIndent is false this flag has no impact.
 176      */
 177     boolean m_startNewLine;
 178 
 179     /**
 180      * Tells if we're in an internal document type subset.
 181      */
 182     protected boolean m_inDoctype = false;
 183 
 184     /**
 185        * Flag to quickly tell if the encoding is UTF8.
 186        */
 187     boolean m_isUTF8 = false;
 188 
 189     /** The xsl:output properties. */
 190     protected Properties m_format;
 191 
 192     /**
 193      * remembers if we are in between the startCDATA() and endCDATA() callbacks
 194      */
 195     protected boolean m_cdataStartCalled = false;
 196 
 197     /**
 198      * If this flag is true DTD entity references are not left as-is,
 199      * which is exiting older behavior.
 200      */
 201     private boolean m_expandDTDEntities = true;
 202 
 203 
 204     /**
 205      * Default constructor
 206      */
 207     public ToStream()
 208     {
 209     }
 210 
 211     /**


 289                 if (writer instanceof WriterToASCI)
 290                 {
 291                     if (m_shouldFlush)
 292                         writer.flush();
 293                 }
 294                 else
 295                 {
 296                     // Flush always.
 297                     // Not a great thing if the writer was created
 298                     // by this class, but don't have a choice.
 299                     writer.flush();
 300                 }
 301             }
 302             catch (IOException ioe)
 303             {
 304                 throw new org.xml.sax.SAXException(ioe);
 305             }
 306         }
 307     }
 308 

 309     /**
 310      * Get the output stream where the events will be serialized to.
 311      *
 312      * @return reference to the result stream, or null of only a writer was
 313      * set.
 314      */
 315     public OutputStream getOutputStream()
 316     {
 317 
 318         if (m_writer instanceof WriterToUTF8Buffered)
 319             return ((WriterToUTF8Buffered) m_writer).getOutputStream();
 320         if (m_writer instanceof WriterToASCI)
 321             return ((WriterToASCI) m_writer).getOutputStream();
 322         else
 323             return null;
 324     }
 325 
 326     // Implement DeclHandler
 327 
 328     /**
 329      *   Report an element type declaration.
 330      *
 331      *   <p>The content model will consist of the string "EMPTY", the
 332      *   string "ANY", or a parenthesised group, optionally followed
 333      *   by an occurrence indicator.  The model will be normalized so
 334      *   that all whitespace is removed,and will include the enclosing
 335      *   parentheses.</p>
 336      *
 337      *   @param name The element type name.
 338      *   @param model The content model as a normalized string.
 339      *   @exception SAXException The application may raise an exception.
 340      */
 341     public void elementDecl(String name, String model) throws SAXException
 342     {
 343         // Do not inline external DTD


 402      * @throws org.xml.sax.SAXException
 403      */
 404     void outputEntityDecl(String name, String value) throws IOException
 405     {
 406         final java.io.Writer writer = m_writer;
 407         writer.write("<!ENTITY ");
 408         writer.write(name);
 409         writer.write(" \"");
 410         writer.write(value);
 411         writer.write("\">");
 412         writer.write(m_lineSep, 0, m_lineSepLen);
 413     }
 414 
 415     /**
 416      * Output a system-dependent line break.
 417      *
 418      * @throws org.xml.sax.SAXException
 419      */
 420     protected final void outputLineSep() throws IOException
 421     {
 422 
 423         m_writer.write(m_lineSep, 0, m_lineSepLen);
 424     }
 425 
 426     /**
 427      * Specifies an output format for this serializer. It the
 428      * serializer has already been associated with an output format,
 429      * it will switch to the new format. This method should not be
 430      * called while the serializer is in the process of serializing
 431      * a document.
 432      *
 433      * @param format The output format to use
 434      */
 435     public void setOutputFormat(Properties format)
 436     {
 437 
 438         boolean shouldFlush = m_shouldFlush;
 439 
 440         init(m_writer, format, false, false);
 441 
 442         m_shouldFlush = shouldFlush;




 443     }



























 444 
 445     /**
 446      * Initialize the serializer with the specified writer and output format.
 447      * Must be called before calling any of the serialize methods.
 448      * This method can be called multiple times and the xsl:output properties
 449      * passed in the 'format' parameter are accumulated across calls.
 450      *
 451      * @param writer The writer to use
 452      * @param format The output format
 453      * @param shouldFlush True if the writer should be flushed at EndDocument.
 454      */
 455     private synchronized void init(
 456         Writer writer,
 457         Properties format,
 458         boolean defaultProperties,
 459         boolean shouldFlush)
 460     {
 461 
 462         m_shouldFlush = shouldFlush;
 463 
 464 
 465         // if we are tracing events we need to trace what
 466         // characters are written to the output writer.
 467         if (m_tracer != null
 468          && !(writer instanceof SerializerTraceWriter)  )
 469             m_writer = new SerializerTraceWriter(writer, m_tracer);
 470         else
 471             m_writer = writer;
 472 




























 473 
 474         m_format = format;
 475         //        m_cdataSectionNames =
 476         //            OutputProperties.getQNameProperties(
 477         //                OutputKeys.CDATA_SECTION_ELEMENTS,
 478         //                format);
 479         setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format);
 480 
 481         setIndentAmount(
 482             OutputPropertyUtils.getIntProperty(
 483                 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,
 484                 format));
 485         setIndent(
 486             OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format));
 487 
 488         {
 489             String sep =
 490                     format.getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR);
 491             if (sep != null) {
 492                 m_lineSep = sep.toCharArray();
 493                 m_lineSepLen = sep.length();
















 494             }
 495         }








 496 
 497         boolean shouldNotWriteXMLHeader =
 498             OutputPropertyUtils.getBooleanProperty(
 499                 OutputKeys.OMIT_XML_DECLARATION,
 500                 format);
 501         setOmitXMLDeclaration(shouldNotWriteXMLHeader);
 502         setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
 503         String doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
 504         setDoctypePublic(doctypePublic);
 505 













 506         // if standalone was explicitly specified
 507         if (format.get(OutputKeys.STANDALONE) != null)
 508         {
 509             String val = format.getProperty(OutputKeys.STANDALONE);
 510             if (defaultProperties)
 511                 setStandaloneInternal(val);
 512             else
 513                 setStandalone(val);


 514         }
 515 
 516         setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));







 517 
 518         if (null != doctypePublic)
 519         {
 520             if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
 521                 m_spaceBeforeClose = true;
 522         }
 523 
 524         /*
 525          * This code is added for XML 1.1 Version output.






 526          */
 527         String version = getVersion();
 528         if (null == version)
 529         {
 530             version = format.getProperty(OutputKeys.VERSION);
 531             setVersion(version);
 532         }
 533 
 534         // initCharsMap();
 535         String encoding = getEncoding();
 536         if (null == encoding)
 537         {
 538             encoding =
 539                 Encodings.getMimeEncoding(
 540                     format.getProperty(OutputKeys.ENCODING));
 541             setEncoding(encoding);


















 542         }
 543 
 544         m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
 545 
 546         // Access this only from the Hashtable level... we don't want to
 547         // get default properties.
 548         String entitiesFileName =
 549             (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
 550 
 551         if (null != entitiesFileName)
 552         {
 553 
 554             String method =
 555                 (String) format.get(OutputKeys.METHOD);
 556 
 557             m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
 558         }
 559 
 560     }
 561 
 562     /**
 563      * Initialize the serializer with the specified writer and output format.
 564      * Must be called before calling any of the serialize methods.
 565      *
 566      * @param writer The writer to use
 567      * @param format The output format
 568      */
 569     private synchronized void init(Writer writer, Properties format)
 570     {
 571         init(writer, format, false, false);
 572     }

 573     /**
 574      * Initialize the serializer with the specified output stream and output
 575      * format. Must be called before calling any of the serialize methods.
 576      *
 577      * @param output The output stream to use
 578      * @param format The output format
 579      * @param defaultProperties true if the properties are the default
 580      * properties
 581      *
 582      * @throws UnsupportedEncodingException The encoding specified   in the
 583      * output format is not supported
 584      */
 585     protected synchronized void init(
 586         OutputStream output,
 587         Properties format,
 588         boolean defaultProperties)
 589         throws UnsupportedEncodingException
 590     {
 591 
 592         String encoding = getEncoding();
 593         if (encoding == null)
 594         {
 595             // if not already set then get it from the properties
 596             encoding =
 597                 Encodings.getMimeEncoding(
 598                     format.getProperty(OutputKeys.ENCODING));
 599             setEncoding(encoding);
 600         }
 601 
 602         if (encoding.equalsIgnoreCase("UTF-8"))
 603         {
 604             m_isUTF8 = true;
 605             //            if (output instanceof java.io.BufferedOutputStream)
 606             //            {
 607             //                init(new WriterToUTF8(output), format, defaultProperties, true);
 608             //            }
 609             //            else if (output instanceof java.io.FileOutputStream)
 610             //            {
 611             //                init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
 612             //            }
 613             //            else
 614             //            {
 615             //                // Not sure what to do in this case.  I'm going to be conservative
 616             //                // and not buffer.
 617             //                init(new WriterToUTF8(output), format, defaultProperties, true);
 618             //            }
 619 
 620 
 621                 init(
 622                     new WriterToUTF8Buffered(output),
 623                     format,
 624                     defaultProperties,
 625                     true);
 626 
 627 
 628         }
 629         else if (
 630             encoding.equals("WINDOWS-1250")
 631                 || encoding.equals("US-ASCII")
 632                 || encoding.equals("ASCII"))
 633         {
 634             init(new WriterToASCI(output), format, defaultProperties, true);
 635         }
 636         else
 637         {
 638             Writer osw;
 639 
 640             try
 641             {
 642                 osw = Encodings.getWriter(output, encoding);
 643             }
 644             catch (UnsupportedEncodingException uee)
 645             {
 646                 System.out.println(
 647                     "Warning: encoding \""
 648                         + encoding
 649                         + "\" not supported"
 650                         + ", using "
 651                         + Encodings.DEFAULT_MIME_ENCODING);
 652 
 653                 encoding = Encodings.DEFAULT_MIME_ENCODING;
 654                 setEncoding(encoding);
 655                 osw = Encodings.getWriter(output, encoding);
 656             }
 657 
 658             init(osw, format, defaultProperties, true);
 659         }
 660 
 661     }
 662 
 663     /**
 664      * Returns the output format for this serializer.
 665      *
 666      * @return The output format in use
 667      */
 668     public Properties getOutputFormat()
 669     {
 670         return m_format;
 671     }
 672 
 673     /**
 674      * Specifies a writer to which the document should be serialized.
 675      * This method should not be called while the serializer is in
 676      * the process of serializing a document.
 677      *
 678      * @param writer The output writer stream
 679      */
 680     public void setWriter(Writer writer)
 681     {







 682         // if we are tracing events we need to trace what
 683         // characters are written to the output writer.
 684         if (m_tracer != null
 685          && !(writer instanceof SerializerTraceWriter)  )
 686             m_writer = new SerializerTraceWriter(writer, m_tracer);
 687         else
 688             m_writer = writer;








 689     }
 690 
 691     /**
 692      * Set if the operating systems end-of-line line separator should
 693      * be used when serializing.  If set false NL character
 694      * (decimal 10) is left alone, otherwise the new-line will be replaced on
 695      * output with the systems line separator. For example on UNIX this is
 696      * NL, while on Windows it is two characters, CR NL, where CR is the
 697      * carriage-return (decimal 13).
 698      *
 699      * @param use_sytem_line_break True if an input NL is replaced with the
 700      * operating systems end-of-line separator.
 701      * @return The previously set value of the serializer.
 702      */
 703     public boolean setLineSepUse(boolean use_sytem_line_break)
 704     {
 705         boolean oldValue = m_lineSepUse;
 706         m_lineSepUse = use_sytem_line_break;
 707         return oldValue;
 708     }
 709 
 710     /**
 711      * Specifies an output stream to which the document should be
 712      * serialized. This method should not be called while the
 713      * serializer is in the process of serializing a document.
 714      * <p>
 715      * The encoding specified in the output properties is used, or
 716      * if no encoding was specified, the default for the selected
 717      * output method.
 718      *
 719      * @param output The output stream
 720      */
 721     public void setOutputStream(OutputStream output)
 722     {


 723 





















 724         try
 725         {
 726             Properties format;
 727             if (null == m_format)
 728                 format =
 729                     OutputPropertiesFactory.getDefaultMethodProperties(
 730                         Method.XML);
 731             else
 732                 format = m_format;
 733             init(output, format, true);
 734         }
 735         catch (UnsupportedEncodingException uee)
 736         {











 737 
 738             // Should have been warned in init, I guess...















 739         }
 740     }
 741 

 742     /**
 743      * @see SerializationHandler#setEscaping(boolean)
 744      */
 745     public boolean setEscaping(boolean escape)
 746     {
 747         final boolean temp = m_escaping;
 748         m_escaping = escape;
 749         return temp;
 750 
 751     }
 752 
 753 
 754     /**
 755      * Might print a newline character and the indentation amount
 756      * of the given depth.
 757      *
 758      * @param depth the indentation depth (element nesting depth)
 759      *
 760      * @throws org.xml.sax.SAXException if an error occurs during writing.
 761      */


2438                 if (m_tracer != null)
2439                     super.fireStartElem(m_elemContext.m_elementName);
2440                 int nAttrs = m_attributes.getLength();
2441                 if (nAttrs > 0)
2442                 {
2443                      processAttributes(m_writer, nAttrs);
2444                     // clear attributes object for re-use with next element
2445                     m_attributes.clear();
2446                 }
2447                 m_writer.write('>');
2448             }
2449             catch (IOException e)
2450             {
2451                 throw new SAXException(e);
2452             }
2453 
2454             /* whether Xalan or XSLTC, we have the prefix mappings now, so
2455              * lets determine if the current element is specified in the cdata-
2456              * section-elements list.
2457              */
2458             if (m_cdataSectionElements != null)
2459                 m_elemContext.m_isCdataSection = isCdataSection();
2460 
2461             if (m_doIndent)
2462             {
2463                 m_isprevtext = false;
2464                 m_preserves.push(m_ispreserve);
2465             }
2466         }
2467 
2468     }
2469 
2470     /**
2471      * Report the start of DTD declarations, if any.
2472      *
2473      * Any declarations are assumed to be in the internal subset unless
2474      * otherwise indicated.
2475      *
2476      * @param name The document type name.
2477      * @param publicId The declared public identifier for the
2478      *        external DTD subset, or null if none was declared.


2515     /**
2516      * Tell if, based on space preservation constraints and the doIndent property,
2517      * if an indent should occur.
2518      *
2519      * @return True if an indent should occur.
2520      */
2521     protected boolean shouldIndent()
2522     {
2523         return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2524     }
2525 
2526     /**
2527      * Searches for the list of qname properties with the specified key in the
2528      * property list. If the key is not found in this property list, the default
2529      * property list, and its defaults, recursively, are then checked. The
2530      * method returns <code>null</code> if the property is not found.
2531      *
2532      * @param   key   the property key.
2533      * @param props the list of properties to search in.
2534      *
2535      * Sets the vector of local-name/URI pairs of the cdata section elements
2536      * specified in the cdata-section-elements property.
2537      *
2538      * This method is essentially a copy of getQNameProperties() from
2539      * OutputProperties. Eventually this method should go away and a call
2540      * to setCdataSectionElements(Vector v) should be made directly.
2541      */
2542     private void setCdataSectionElements(String key, Properties props)
2543     {
2544 
2545         String s = props.getProperty(key);
2546 
2547         if (null != s)
2548         {
2549             // Vector of URI/LocalName pairs
2550             Vector v = new Vector();
2551             int l = s.length();
2552             boolean inCurly = false;
2553             StringBuffer buf = new StringBuffer();
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(), v);
2569                             buf.setLength(0);
2570                         }
2571                         continue;
2572                     }
2573                 }


2580             }
2581 
2582             if (buf.length() > 0)
2583             {
2584                 addCdataSectionElement(buf.toString(), v);
2585                 buf.setLength(0);
2586             }
2587             // call the official, public method to set the collected names
2588             setCdataSectionElements(v);
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, Vector v)
2601     {
2602 
2603         StringTokenizer tokenizer =
2604             new StringTokenizer(URI_and_localName, "{}", false);
2605         String s1 = tokenizer.nextToken();
2606         String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2607 
2608         if (null == s2)
2609         {
2610             // add null URI and the local name
2611             v.addElement(null);
2612             v.addElement(s1);
2613         }
2614         else
2615         {
2616             // add URI, then local name
2617             v.addElement(s1);
2618             v.addElement(s2);
2619         }
2620     }
2621 
2622     /**
2623      * Remembers the cdata sections specified in the cdata-section-elements.
2624      * The "official way to set URI and localName pairs.
2625      * This method should be used by both Xalan and XSLTC.
2626      *
2627      * @param URI_and_localNames a vector of pairs of Strings (URI/local)
2628      */
2629     public void setCdataSectionElements(Vector URI_and_localNames)

















2630     {
2631         m_cdataSectionElements = URI_and_localNames;










2632     }
2633 
2634     /**
2635      * Makes sure that the namespace URI for the given qualified attribute name
2636      * is declared.
2637      * @param ns the namespace URI
2638      * @param rawName the qualified name
2639      * @return returns null if no action is taken, otherwise it returns the
2640      * prefix used in declaring the namespace.
2641      * @throws SAXException
2642      */
2643     protected String ensureAttributesNamespaceIsDeclared(
2644         String ns,
2645         String localName,
2646         String rawName)
2647         throws org.xml.sax.SAXException
2648     {
2649 
2650         if (ns != null && ns.length() > 0)
2651         {


3067          this.m_ispreserve = false;
3068          this.m_isprevtext = false;
3069          this.m_isUTF8 = false; //  ?? used anywhere ??
3070          this.m_preserves.clear();
3071          this.m_shouldFlush = true;
3072          this.m_spaceBeforeClose = false;
3073          this.m_startNewLine = false;
3074          this.m_lineSepUse = true;
3075          // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3076          // this.m_writer = null;
3077          this.m_expandDTDEntities = true;
3078 
3079     }
3080 
3081     /**
3082       * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3083       * @param encoding the character encoding
3084       */
3085      public void setEncoding(String encoding)
3086      {
3087          String old = getEncoding();
3088          super.setEncoding(encoding);
3089          if (old == null || !old.equals(encoding)) {
3090             // If we have changed the setting of the
3091             m_encodingInfo = Encodings.getEncodingInfo(encoding);
3092 
3093             if (encoding != null && m_encodingInfo.name == null) {
3094                 // We tried to get an EncodingInfo for Object for the given
3095                 // encoding, but it came back with an internall null name
3096                 // so the encoding is not supported by the JDK, issue a message.
3097                 String msg = Utils.messages.createMessage(
3098                                 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ encoding });
3099                 try
3100                 {
3101                         // Prepare to issue the warning message
3102                         Transformer tran = super.getTransformer();
3103                         if (tran != null) {
3104                                 ErrorListener errHandler = tran.getErrorListener();
3105                                 // Issue the warning message
3106                                 if (null != errHandler && m_sourceLocator != null)
3107                                         errHandler.warning(new TransformerException(msg, m_sourceLocator));
3108                                 else
3109                                         System.out.println(msg);
3110                     }
3111                         else
3112                                 System.out.println(msg);
3113                 }
3114                 catch (Exception e){}
3115             }
3116          }
3117          return;
3118      }
3119 
3120     /**
3121      * Simple stack for boolean values.
3122      *
3123      * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
3124      * It exists to cut the serializers dependancy on that package.
3125      * A minor changes from that package are:
3126      * doesn't implement Clonable
3127      *
3128      * @xsl.usage internal
3129      */
3130     static final class BoolStack
3131     {
3132 
3133       /** Array of boolean values          */
3134       private boolean m_values[];
3135 
3136       /** Array size allocated           */
3137       private int m_allocatedSize;


3368         final java.io.Writer writer = m_writer;
3369         if (m_needToOutputDocTypeDecl)
3370         {
3371             outputDocTypeDecl(m_elemContext.m_elementName, false);
3372             m_needToOutputDocTypeDecl = false;
3373         }
3374         if (m_inDoctype)
3375         {
3376             writer.write(" [");
3377             writer.write(m_lineSep, 0, m_lineSepLen);
3378             m_inDoctype = false;
3379         }
3380     }
3381 
3382     /**
3383      * If set to false the serializer does not expand DTD entities,
3384      * but leaves them as is, the default value is true;
3385      */
3386     public void setDTDEntityExpansion(boolean expand) {
3387         m_expandDTDEntities = expand;




















3388     }
3389 }
   1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one
   7  * or more contributor license agreements. See the NOTICE file
   8  * distributed with this work for additional information
   9  * regarding copyright ownership. The ASF licenses this file
  10  * to you under the Apache License, Version 2.0 (the  "License");
  11  * you may not use this file except in compliance with the License.
  12  * You may obtain a copy of the License at
  13  *
  14  *     http://www.apache.org/licenses/LICENSE-2.0
  15  *
  16  * Unless required by applicable law or agreed to in writing, software
  17  * distributed under the License is distributed on an "AS IS" BASIS,
  18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19  * See the License for the specific language governing permissions and
  20  * limitations under the License.
  21  */
  22 /*
  23  * $Id: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid Exp $
  24  */
  25 package com.sun.org.apache.xml.internal.serializer;
  26 
  27 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  28 import java.io.IOException;
  29 import java.io.OutputStream;
  30 import java.io.OutputStreamWriter;
  31 import java.io.UnsupportedEncodingException;
  32 import java.io.Writer;
  33 import java.util.Enumeration;
  34 import java.util.Iterator;
  35 import java.util.Properties;
  36 import java.util.Set;
  37 import java.util.StringTokenizer;
  38 import java.util.ArrayList;
  39 
  40 import javax.xml.transform.ErrorListener;
  41 import javax.xml.transform.OutputKeys;
  42 import javax.xml.transform.Transformer;
  43 import javax.xml.transform.TransformerException;
  44 
  45 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  46 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  47 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
  48 import org.w3c.dom.Node;
  49 import org.xml.sax.Attributes;
  50 import org.xml.sax.ContentHandler;
  51 import org.xml.sax.SAXException;
  52 
  53 //import com.sun.media.sound.IESecurity;
  54 
  55 /**
  56  * This abstract class is a base class for other stream
  57  * serializers (xml, html, text ...) that write output to a stream.
  58  *


 175     protected boolean m_spaceBeforeClose = false;
 176 
 177     /**
 178      * Flag to signal that a newline should be added.
 179      *
 180      * Used only in indent() which is called only if m_doIndent is true.
 181      * If m_doIndent is false this flag has no impact.
 182      */
 183     boolean m_startNewLine;
 184 
 185     /**
 186      * Tells if we're in an internal document type subset.
 187      */
 188     protected boolean m_inDoctype = false;
 189 
 190     /**
 191        * Flag to quickly tell if the encoding is UTF8.
 192        */
 193     boolean m_isUTF8 = false;
 194 



 195     /**
 196      * remembers if we are in between the startCDATA() and endCDATA() callbacks
 197      */
 198     protected boolean m_cdataStartCalled = false;
 199 
 200     /**
 201      * If this flag is true DTD entity references are not left as-is,
 202      * which is exiting older behavior.
 203      */
 204     private boolean m_expandDTDEntities = true;
 205 
 206 
 207     /**
 208      * Default constructor
 209      */
 210     public ToStream()
 211     {
 212     }
 213 
 214     /**


 292                 if (writer instanceof WriterToASCI)
 293                 {
 294                     if (m_shouldFlush)
 295                         writer.flush();
 296                 }
 297                 else
 298                 {
 299                     // Flush always.
 300                     // Not a great thing if the writer was created
 301                     // by this class, but don't have a choice.
 302                     writer.flush();
 303                 }
 304             }
 305             catch (IOException ioe)
 306             {
 307                 throw new org.xml.sax.SAXException(ioe);
 308             }
 309         }
 310     }
 311 
 312     OutputStream m_outputStream;
 313     /**
 314      * Get the output stream where the events will be serialized to.
 315      *
 316      * @return reference to the result stream, or null of only a writer was
 317      * set.
 318      */
 319     public OutputStream getOutputStream()
 320     {
 321         return m_outputStream;






 322     }
 323 
 324     // Implement DeclHandler
 325 
 326     /**
 327      *   Report an element type declaration.
 328      *
 329      *   <p>The content model will consist of the string "EMPTY", the
 330      *   string "ANY", or a parenthesised group, optionally followed
 331      *   by an occurrence indicator.  The model will be normalized so
 332      *   that all whitespace is removed,and will include the enclosing
 333      *   parentheses.</p>
 334      *
 335      *   @param name The element type name.
 336      *   @param model The content model as a normalized string.
 337      *   @exception SAXException The application may raise an exception.
 338      */
 339     public void elementDecl(String name, String model) throws SAXException
 340     {
 341         // Do not inline external DTD


 400      * @throws org.xml.sax.SAXException
 401      */
 402     void outputEntityDecl(String name, String value) throws IOException
 403     {
 404         final java.io.Writer writer = m_writer;
 405         writer.write("<!ENTITY ");
 406         writer.write(name);
 407         writer.write(" \"");
 408         writer.write(value);
 409         writer.write("\">");
 410         writer.write(m_lineSep, 0, m_lineSepLen);
 411     }
 412 
 413     /**
 414      * Output a system-dependent line break.
 415      *
 416      * @throws org.xml.sax.SAXException
 417      */
 418     protected final void outputLineSep() throws IOException
 419     {

 420         m_writer.write(m_lineSep, 0, m_lineSepLen);
 421     }
 422 
 423     void setProp(String name, String val, boolean defaultVal) {
 424         if (val != null) {













 425 
 426             char first = getFirstCharLocName(name);
 427             switch (first) {
 428             case 'c':
 429                 if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name)) {
 430                     addCdataSectionElements(val); // val is cdataSectionNames
 431                 }
 432                 break;
 433             case 'd':
 434                 if (OutputKeys.DOCTYPE_SYSTEM.equals(name)) {
 435                     this.m_doctypeSystem = val;
 436                 } else if (OutputKeys.DOCTYPE_PUBLIC.equals(name)) {
 437                     this.m_doctypePublic = val;
 438                     if (val.startsWith("-//W3C//DTD XHTML"))
 439                         m_spaceBeforeClose = true;
 440                 }
 441                 break;
 442             case 'e':
 443                 String newEncoding = val;
 444                 if (OutputKeys.ENCODING.equals(name)) {
 445                     String possible_encoding = Encodings.getMimeEncoding(val);
 446                     if (possible_encoding != null) {
 447                         // if the encoding is being set, try to get the
 448                         // preferred
 449                         // mime-name and set it too.
 450                         super.setProp("mime-name", possible_encoding,
 451                                 defaultVal);
 452                     }
 453                     final String oldExplicitEncoding = getOutputPropertyNonDefault(OutputKeys.ENCODING);
 454                     final String oldDefaultEncoding  = getOutputPropertyDefault(OutputKeys.ENCODING);
 455                     if ( (defaultVal && ( oldDefaultEncoding == null || !oldDefaultEncoding.equalsIgnoreCase(newEncoding)))
 456                             || ( !defaultVal && (oldExplicitEncoding == null || !oldExplicitEncoding.equalsIgnoreCase(newEncoding) ))) {
 457                        // We are trying to change the default or the non-default setting of the encoding to a different value
 458                        // from what it was
 459                        
 460                        EncodingInfo encodingInfo = Encodings.getEncodingInfo(newEncoding);
 461                        if (newEncoding != null && encodingInfo.name == null) {
 462                         // We tried to get an EncodingInfo for Object for the given
 463                         // encoding, but it came back with an internall null name
 464                         // so the encoding is not supported by the JDK, issue a message.
 465                         final String msg = Utils.messages.createMessage(
 466                                 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ newEncoding });




















 467                         
 468                         final String msg2 = 
 469                             "Warning: encoding \"" + newEncoding + "\" not supported, using "
 470                                    + Encodings.DEFAULT_MIME_ENCODING;
 471                         try {
 472                                 // Prepare to issue the warning message
 473                                 final Transformer tran = super.getTransformer();
 474                                 if (tran != null) {
 475                                     final ErrorListener errHandler = tran
 476                                             .getErrorListener();
 477                                     // Issue the warning message
 478                                     if (null != errHandler
 479                                             && m_sourceLocator != null) {
 480                                         errHandler
 481                                                 .warning(new TransformerException(
 482                                                         msg, m_sourceLocator));
 483                                         errHandler
 484                                                 .warning(new TransformerException(
 485                                                         msg2, m_sourceLocator));
 486                                     } else {
 487                                         System.out.println(msg);
 488                                         System.out.println(msg2);
 489                                     }
 490                                 } else {
 491                                     System.out.println(msg);
 492                                     System.out.println(msg2);
 493                                 }
 494                             } catch (Exception e) {
 495                             }
 496 
 497                             // We said we are using UTF-8, so use it
 498                             newEncoding = Encodings.DEFAULT_MIME_ENCODING;
 499                             val = Encodings.DEFAULT_MIME_ENCODING; // to store the modified value into the properties a little later
 500                             encodingInfo = Encodings.getEncodingInfo(newEncoding);
 501                         } 
 502                        // The encoding was good, or was forced to UTF-8 above
 503                        
 504                        
 505                        // If there is already a non-default set encoding and we 
 506                        // are trying to set the default encoding, skip the this block
 507                        // as the non-default value is already the one to use.
 508                        if (defaultVal == false || oldExplicitEncoding == null) {
 509                            m_encodingInfo = encodingInfo;
 510                            if (newEncoding != null)
 511                                m_isUTF8 = newEncoding.equals(Encodings.DEFAULT_MIME_ENCODING);
 512                            
 513                            // if there was a previously set OutputStream
 514                            OutputStream os = getOutputStream();
 515                            if (os != null) {
 516                                Writer w = getWriter();
 517                                
 518                                // If the writer was previously set, but
 519                                // set by the user, or if the new encoding is the same
 520                                // as the old encoding, skip this block
 521                                String oldEncoding = getOutputProperty(OutputKeys.ENCODING);
 522                                if ((w == null || !m_writer_set_by_user) 
 523                                        && !newEncoding.equalsIgnoreCase(oldEncoding)) {
 524                                    // Make the change of encoding in our internal
 525                                    // table, then call setOutputStreamInternal
 526                                    // which will stomp on the old Writer (if any)
 527                                    // with a new Writer with the new encoding.
 528                                    super.setProp(name, val, defaultVal);
 529                                    setOutputStreamInternal(os,false);
 530                                }
 531                            }
 532                        }
 533                     }
 534                 }
 535                 break;
 536             case 'i':
 537                 if (OutputPropertiesFactory.S_KEY_INDENT_AMOUNT.equals(name)) {
 538                     setIndentAmount(Integer.parseInt(val));
 539                 } else if (OutputKeys.INDENT.equals(name)) {
 540                     boolean b = "yes".equals(val) ? true : false;
 541                     m_doIndent = b;
 542                 }
 543 
 544                 break;
 545             case 'l':
 546                 if (OutputPropertiesFactory.S_KEY_LINE_SEPARATOR.equals(name)) {
 547                     m_lineSep = val.toCharArray();
 548                     m_lineSepLen = m_lineSep.length;
 549                 }


 550 
 551                 break;
 552             case 'm':
 553                 if (OutputKeys.MEDIA_TYPE.equals(name)) {
 554                     m_mediatype = val;
 555                 }
 556                 break;
 557             case 'o':
 558                 if (OutputKeys.OMIT_XML_DECLARATION.equals(name)) {
 559                     boolean b = "yes".equals(val) ? true : false;
 560                     this.m_shouldNotWriteXMLHeader = b;
 561                 }
 562                 break;
 563             case 's':
 564                 // if standalone was explicitly specified
 565                 if (OutputKeys.STANDALONE.equals(name)) {
 566                     if (defaultVal) {


 567                         setStandaloneInternal(val);
 568                     } else {
 569                         m_standaloneWasSpecified = true;
 570                         setStandaloneInternal(val);
 571                     }
 572                 }
 573 
 574                 break;
 575             case 'v':
 576                 if (OutputKeys.VERSION.equals(name)) {
 577                     m_version = val;
 578                 }
 579                 break;
 580             default:
 581                 break;
 582 
 583             } 
 584             super.setProp(name, val, defaultVal);
 585         }

 586     }
 587     
 588     /**
 589      * Specifies an output format for this serializer. It the
 590      * serializer has already been associated with an output format,
 591      * it will switch to the new format. This method should not be
 592      * called while the serializer is in the process of serializing
 593      * a document.
 594      *
 595      * @param format The output format to use
 596      */
 597     public void setOutputFormat(Properties format)

 598     {
 599         boolean shouldFlush = m_shouldFlush;


 600         
 601         if (format != null)


 602         {
 603             // Set the default values first,
 604             // and the non-default values after that, 
 605             // just in case there is some unexpected
 606             // residual values left over from over-ridden default values
 607             Enumeration propNames;
 608             propNames = format.propertyNames();
 609             while (propNames.hasMoreElements())
 610             {
 611                 String key = (String) propNames.nextElement();
 612                 // Get the value, possibly a default value
 613                 String value = format.getProperty(key);
 614                 // Get the non-default value (if any).
 615                 String explicitValue = (String) format.get(key);
 616                 if (explicitValue == null && value != null) {
 617                     // This is a default value
 618                     this.setOutputPropertyDefault(key,value);
 619                 }
 620                 if (explicitValue != null) {
 621                     // This is an explicit non-default value
 622                     this.setOutputProperty(key,explicitValue);
 623                 }
 624             } 
 625         }   


 626 
 627         // Access this only from the Hashtable level... we don't want to 
 628         // get default properties.
 629         String entitiesFileName =
 630             (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
 631 
 632         if (null != entitiesFileName)
 633         {
 634 
 635             String method = 
 636                 (String) format.get(OutputKeys.METHOD);
 637             
 638             m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
 639         }
 640 

 641 
 642          
 643 
 644         m_shouldFlush = shouldFlush;







 645     }
 646 
 647     /**
 648      * Returns the output format for this serializer.






 649      *
 650      * @return The output format in use

 651      */
 652     public Properties getOutputFormat() {
 653         Properties def = new Properties();
















 654         {
 655             Set<String> s = getOutputPropDefaultKeys();
 656             for (String key : s) {
 657                 String val = getOutputPropertyDefault(key);
 658                 def.put(key, val);




















 659             }






 660         }



 661         
 662         Properties props = new Properties(def);




 663         {
 664             Set<String> s = getOutputPropKeys();
 665             for (String key : s) {
 666                 String val = getOutputPropertyNonDefault(key);
 667                 if (val != null)
 668                     props.put(key, val);





 669             }




 670         }
 671         return props;








 672     }
 673     
 674     /**
 675      * Specifies a writer to which the document should be serialized.
 676      * This method should not be called while the serializer is in
 677      * the process of serializing a document.
 678      *
 679      * @param writer The output writer stream
 680      */
 681     public void setWriter(Writer writer)
 682     {
 683         setWriterInternal(writer, true);
 684     }
 685     
 686     private boolean m_writer_set_by_user;
 687     private void setWriterInternal(Writer writer, boolean setByUser) {
 688         m_writer_set_by_user = setByUser;
 689         m_writer = writer;
 690         // if we are tracing events we need to trace what
 691         // characters are written to the output writer.
 692         if (m_tracer != null) {
 693             boolean noTracerYet = true;
 694             Writer w2 = m_writer;
 695             while (w2 instanceof WriterChain) {
 696                 if (w2 instanceof SerializerTraceWriter) {
 697                     noTracerYet = false;
 698                     break;
 699                 }
 700                 w2 = ((WriterChain)w2).getWriter();
 701             }
 702             if (noTracerYet)
 703                 m_writer = new SerializerTraceWriter(m_writer, m_tracer);
 704         }
 705     }
 706 
 707     /**
 708      * Set if the operating systems end-of-line line separator should
 709      * be used when serializing.  If set false NL character
 710      * (decimal 10) is left alone, otherwise the new-line will be replaced on
 711      * output with the systems line separator. For example on UNIX this is
 712      * NL, while on Windows it is two characters, CR NL, where CR is the
 713      * carriage-return (decimal 13).
 714      *
 715      * @param use_sytem_line_break True if an input NL is replaced with the
 716      * operating systems end-of-line separator.
 717      * @return The previously set value of the serializer.
 718      */
 719     public boolean setLineSepUse(boolean use_sytem_line_break)
 720     {
 721         boolean oldValue = m_lineSepUse;
 722         m_lineSepUse = use_sytem_line_break;
 723         return oldValue;
 724     }
 725 
 726     /**
 727      * Specifies an output stream to which the document should be
 728      * serialized. This method should not be called while the
 729      * serializer is in the process of serializing a document.
 730      * <p>
 731      * The encoding specified in the output properties is used, or
 732      * if no encoding was specified, the default for the selected
 733      * output method.
 734      *
 735      * @param output The output stream
 736      */
 737     public void setOutputStream(OutputStream output)
 738     {
 739         setOutputStreamInternal(output, true);
 740     }
 741     
 742     private void setOutputStreamInternal(OutputStream output, boolean setByUser)
 743     {
 744         m_outputStream = output;
 745         String encoding = getOutputProperty(OutputKeys.ENCODING);        
 746         if (Encodings.DEFAULT_MIME_ENCODING.equalsIgnoreCase(encoding))
 747         {
 748             // We wrap the OutputStream with a writer, but
 749             // not one set by the user
 750             try {
 751                 setWriterInternal(new WriterToUTF8Buffered(output), false);
 752             } catch (UnsupportedEncodingException e) {
 753                 e.printStackTrace();
 754             }
 755         } else if (
 756                 "WINDOWS-1250".equals(encoding)
 757                 || "US-ASCII".equals(encoding)
 758                 || "ASCII".equals(encoding))
 759         {
 760             setWriterInternal(new WriterToASCI(output), false);
 761         } else if (encoding != null) {
 762             Writer osw = null;
 763                 try
 764                 {
 765                     osw = Encodings.getWriter(output, encoding);







 766                 }
 767                 catch (UnsupportedEncodingException uee)
 768                 {
 769                     osw = null;
 770                 }
 771 
 772             
 773             if (osw == null) {
 774                 System.out.println(
 775                     "Warning: encoding \""
 776                         + encoding
 777                         + "\" not supported"
 778                         + ", using "
 779                         + Encodings.DEFAULT_MIME_ENCODING);
 780 
 781                 encoding = Encodings.DEFAULT_MIME_ENCODING;
 782                 setEncoding(encoding);
 783                 try {
 784                     osw = Encodings.getWriter(output, encoding);
 785                 } catch (UnsupportedEncodingException e) {
 786                     // We can't really get here, UTF-8 is always supported
 787                     // This try-catch exists to make the compiler happy
 788                     e.printStackTrace();
 789                 }
 790             }
 791             setWriterInternal(osw,false);
 792         }
 793         else {
 794             // don't have any encoding, but we have an OutputStream
 795             Writer osw = new OutputStreamWriter(output);
 796             setWriterInternal(osw,false);
 797         }
 798     }
 799 
 800 
 801     /**
 802      * @see SerializationHandler#setEscaping(boolean)
 803      */
 804     public boolean setEscaping(boolean escape)
 805     {
 806         final boolean temp = m_escaping;
 807         m_escaping = escape;
 808         return temp;
 809 
 810     }
 811 
 812 
 813     /**
 814      * Might print a newline character and the indentation amount
 815      * of the given depth.
 816      *
 817      * @param depth the indentation depth (element nesting depth)
 818      *
 819      * @throws org.xml.sax.SAXException if an error occurs during writing.
 820      */


2497                 if (m_tracer != null)
2498                     super.fireStartElem(m_elemContext.m_elementName);
2499                 int nAttrs = m_attributes.getLength();
2500                 if (nAttrs > 0)
2501                 {
2502                      processAttributes(m_writer, nAttrs);
2503                     // clear attributes object for re-use with next element
2504                     m_attributes.clear();
2505                 }
2506                 m_writer.write('>');
2507             }
2508             catch (IOException e)
2509             {
2510                 throw new SAXException(e);
2511             }
2512 
2513             /* whether Xalan or XSLTC, we have the prefix mappings now, so
2514              * lets determine if the current element is specified in the cdata-
2515              * section-elements list.
2516              */
2517             if (m_StringOfCDATASections != null)
2518                 m_elemContext.m_isCdataSection = isCdataSection();
2519             
2520             if (m_doIndent)
2521             {
2522                 m_isprevtext = false;
2523                 m_preserves.push(m_ispreserve);
2524             }
2525         }
2526 
2527     }
2528 
2529     /**
2530      * Report the start of DTD declarations, if any.
2531      *
2532      * Any declarations are assumed to be in the internal subset unless
2533      * otherwise indicated.
2534      *
2535      * @param name The document type name.
2536      * @param publicId The declared public identifier for the
2537      *        external DTD subset, or null if none was declared.


2574     /**
2575      * Tell if, based on space preservation constraints and the doIndent property,
2576      * if an indent should occur.
2577      *
2578      * @return True if an indent should occur.
2579      */
2580     protected boolean shouldIndent()
2581     {
2582         return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2583     }
2584 
2585     /**
2586      * Searches for the list of qname properties with the specified key in the
2587      * property list. If the key is not found in this property list, the default
2588      * property list, and its defaults, recursively, are then checked. The
2589      * method returns <code>null</code> if the property is not found.
2590      *
2591      * @param   key   the property key.
2592      * @param props the list of properties to search in.
2593      *
2594      * Sets the ArrayList of local-name/URI pairs of the cdata section elements
2595      * specified in the cdata-section-elements property.
2596      *
2597      * This method is essentially a copy of getQNameProperties() from
2598      * OutputProperties. Eventually this method should go away and a call
2599      * to setCdataSectionElements(ArrayList<String> v) should be made directly.
2600      */
2601     private void setCdataSectionElements(String key, Properties props)
2602     {
2603 
2604         String s = props.getProperty(key);
2605 
2606         if (null != s)
2607         {
2608             // ArrayList<String> of URI/LocalName pairs
2609             ArrayList<String> v = new ArrayList<>();
2610             int l = s.length();
2611             boolean inCurly = false;
2612             StringBuilder buf = new StringBuilder();
2613 
2614             // parse through string, breaking on whitespaces.  I do this instead
2615             // of a tokenizer so I can track whitespace inside of curly brackets,
2616             // which theoretically shouldn't happen if they contain legal URLs.
2617             for (int i = 0; i < l; i++)
2618             {
2619                 char c = s.charAt(i);
2620 
2621                 if (Character.isWhitespace(c))
2622                 {
2623                     if (!inCurly)
2624                     {
2625                         if (buf.length() > 0)
2626                         {
2627                             addCdataSectionElement(buf.toString(), v);
2628                             buf.setLength(0);
2629                         }
2630                         continue;
2631                     }
2632                 }


2639             }
2640 
2641             if (buf.length() > 0)
2642             {
2643                 addCdataSectionElement(buf.toString(), v);
2644                 buf.setLength(0);
2645             }
2646             // call the official, public method to set the collected names
2647             setCdataSectionElements(v);
2648         }
2649 
2650     }
2651 
2652     /**
2653      * Adds a URI/LocalName pair of strings to the list.
2654      *
2655      * @param URI_and_localName String of the form "{uri}local" or "local"
2656      *
2657      * @return a QName object
2658      */
2659     private void addCdataSectionElement(String URI_and_localName, ArrayList<String> v)
2660     {
2661 
2662         StringTokenizer tokenizer =
2663             new StringTokenizer(URI_and_localName, "{}", false);
2664         String s1 = tokenizer.nextToken();
2665         String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2666 
2667         if (null == s2)
2668         {
2669             // add null URI and the local name
2670             v.add(null);
2671             v.add(s1);
2672         }
2673         else
2674         {
2675             // add URI, then local name
2676             v.add(s1);
2677             v.add(s2);
2678         }
2679     }
2680 
2681     /**
2682      * Remembers the cdata sections specified in the cdata-section-elements.
2683      * The "official way to set URI and localName pairs. 
2684      * This method should be used by both Xalan and XSLTC.
2685      * 
2686      * @param URI_and_localNames an ArrayList of pairs of Strings (URI/local)
2687      */
2688     public void setCdataSectionElements(ArrayList<String> URI_and_localNames)
2689     {
2690         // convert to the new way.
2691         if (URI_and_localNames != null)
2692         {
2693             final int len = URI_and_localNames.size() - 1;
2694             if (len > 0)
2695             {
2696                 final StringBuilder sb = new StringBuilder();
2697                 for (int i = 0; i < len; i += 2)
2698                 {
2699                     // whitspace separated "{uri1}local1 {uri2}local2 ..."
2700                     if (i != 0)
2701                         sb.append(' ');
2702                     final String uri = (String) URI_and_localNames.get(i);
2703                     final String localName =
2704                         (String) URI_and_localNames.get(i + 1);
2705                     if (uri != null)
2706                     {
2707                         // If there is no URI don't put this in, just the localName then.
2708                         sb.append('{');
2709                         sb.append(uri);
2710                         sb.append('}');
2711                     }
2712                     sb.append(localName);
2713                 }
2714                 m_StringOfCDATASections = sb.toString();
2715             }
2716         }
2717         initCdataElems(m_StringOfCDATASections);
2718     }
2719       
2720     /**
2721      * Makes sure that the namespace URI for the given qualified attribute name
2722      * is declared.
2723      * @param ns the namespace URI
2724      * @param rawName the qualified name
2725      * @return returns null if no action is taken, otherwise it returns the
2726      * prefix used in declaring the namespace.
2727      * @throws SAXException
2728      */
2729     protected String ensureAttributesNamespaceIsDeclared(
2730         String ns,
2731         String localName,
2732         String rawName)
2733         throws org.xml.sax.SAXException
2734     {
2735 
2736         if (ns != null && ns.length() > 0)
2737         {


3153          this.m_ispreserve = false;
3154          this.m_isprevtext = false;
3155          this.m_isUTF8 = false; //  ?? used anywhere ??
3156          this.m_preserves.clear();
3157          this.m_shouldFlush = true;
3158          this.m_spaceBeforeClose = false;
3159          this.m_startNewLine = false;
3160          this.m_lineSepUse = true;
3161          // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3162          // this.m_writer = null;
3163          this.m_expandDTDEntities = true;
3164 
3165     }
3166 
3167     /**
3168       * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3169       * @param encoding the character encoding
3170       */
3171      public void setEncoding(String encoding)
3172      {
3173          setOutputProperty(OutputKeys.ENCODING,encoding);






























3174      }
3175 
3176     /**
3177      * Simple stack for boolean values.
3178      *
3179      * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
3180      * It exists to cut the serializers dependancy on that package.
3181      * A minor changes from that package are:
3182      * doesn't implement Clonable
3183      *
3184      * @xsl.usage internal
3185      */
3186     static final class BoolStack
3187     {
3188 
3189       /** Array of boolean values          */
3190       private boolean m_values[];
3191 
3192       /** Array size allocated           */
3193       private int m_allocatedSize;


3424         final java.io.Writer writer = m_writer;
3425         if (m_needToOutputDocTypeDecl)
3426         {
3427             outputDocTypeDecl(m_elemContext.m_elementName, false);
3428             m_needToOutputDocTypeDecl = false;
3429         }
3430         if (m_inDoctype)
3431         {
3432             writer.write(" [");
3433             writer.write(m_lineSep, 0, m_lineSepLen);
3434             m_inDoctype = false;
3435         }
3436     }
3437 
3438     /**
3439      * If set to false the serializer does not expand DTD entities,
3440      * but leaves them as is, the default value is true;
3441      */
3442     public void setDTDEntityExpansion(boolean expand) {
3443         m_expandDTDEntities = expand;
3444     }
3445     
3446     /**
3447      * Remembers the cdata sections specified in the cdata-section-elements by appending the given
3448      * cdata section elements to the list. This method can be called multiple times, but once an
3449      * element is put in the list of cdata section elements it can not be removed.
3450      * This method should be used by both Xalan and XSLTC.
3451      * 
3452      * @param URI_and_localNames a whitespace separated list of element names, each element
3453      * is a URI in curly braces (optional) and a local name. An example of such a parameter is:
3454      * "{http://company.com}price {myURI2}book chapter"
3455      */   
3456     public void addCdataSectionElements(String URI_and_localNames)
3457     {
3458         if (URI_and_localNames != null)
3459             initCdataElems(URI_and_localNames);
3460         if (m_StringOfCDATASections == null)
3461             m_StringOfCDATASections = URI_and_localNames;
3462         else
3463             m_StringOfCDATASections += (" " + URI_and_localNames);
3464     }
3465 }