< prev index next >

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

Print this page




  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xml.internal.serializer;
  22 
  23 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  24 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  25 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  26 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
  27 import java.io.IOException;
  28 import java.io.OutputStream;
  29 import java.io.OutputStreamWriter;
  30 import java.io.UnsupportedEncodingException;
  31 import java.io.Writer;





  32 import java.util.Enumeration;
  33 import java.util.Iterator;
  34 import java.util.Properties;

  35 import java.util.Set;
  36 import java.util.StringTokenizer;
  37 import java.util.ArrayList;
  38 import javax.xml.transform.ErrorListener;
  39 import javax.xml.transform.OutputKeys;
  40 import javax.xml.transform.Transformer;
  41 import javax.xml.transform.TransformerException;
  42 import org.w3c.dom.Node;
  43 import org.xml.sax.Attributes;
  44 import org.xml.sax.ContentHandler;
  45 import org.xml.sax.SAXException;
  46 
  47 /**
  48  * This abstract class is a base class for other stream
  49  * serializers (xml, html, text ...) that write output to a stream.
  50  *
  51  * @xsl.usage internal
  52  */
  53 abstract public class ToStream extends SerializerBase {
  54 
  55     private static final String COMMENT_BEGIN = "<!--";
  56     private static final String COMMENT_END = "-->";
  57 


  72     EncodingInfo m_encodingInfo = new EncodingInfo(null,null);
  73 
  74     /**
  75      * Method reference to the sun.io.CharToByteConverter#canConvert method
  76      * for this encoding.  Invalid if m_charToByteConverter is null.
  77      */
  78     java.lang.reflect.Method m_canConvertMeth;
  79 
  80     /**
  81      * Boolean that tells if we already tried to get the converter.
  82      */
  83     boolean m_triedToGetConverter = false;
  84 
  85     /**
  86      * Opaque reference to the sun.io.CharToByteConverter for this
  87      * encoding.
  88      */
  89     Object m_charToByteConverter = null;
  90 
  91     /**
























  92      * Stack to keep track of whether or not we need to
  93      * preserve whitespace.
  94      *
  95      * Used to push/pop values used for the field m_ispreserve, but
  96      * m_ispreserve is only relevant if m_doIndent is true.
  97      * If m_doIndent is false this field has no impact.
  98      *
  99      */
 100     protected BoolStack m_preserves = new BoolStack();
 101 
 102     /**
 103      * State flag to tell if preservation of whitespace
 104      * is important.
 105      *
 106      * Used only in shouldIndent() but only if m_doIndent is true.
 107      * If m_doIndent is false this flag has no impact.
 108      *
 109      */
 110     protected boolean m_ispreserve = false;
 111 


 750         final boolean temp = m_escaping;
 751         m_escaping = escape;
 752         return temp;
 753 
 754     }
 755 
 756 
 757     /**
 758      * Might print a newline character and the indentation amount
 759      * of the given depth.
 760      *
 761      * @param depth the indentation depth (element nesting depth)
 762      *
 763      * @throws org.xml.sax.SAXException if an error occurs during writing.
 764      */
 765     protected void indent(int depth) throws IOException
 766     {
 767 
 768         if (m_startNewLine)
 769             outputLineSep();
 770         /* For m_indentAmount > 0 this extra test might be slower
 771          * but Xalan's default value is 0, so this extra test
 772          * will run faster in that situation.
 773          */
 774         if (m_indentAmount > 0)
 775             printSpace(depth * m_indentAmount);
 776 
 777     }
 778 
 779     /**
 780      * Indent at the current element nesting depth.
 781      * @throws IOException
 782      */
 783     protected void indent() throws IOException
 784     {
 785         indent(m_elemContext.m_currentElemDepth);
 786     }
 787     /**
 788      * Prints <var>n</var> spaces.
 789      * @param n         Number of spaces to print.
 790      *
 791      * @throws org.xml.sax.SAXException if an error occurs when writing.
 792      */
 793     private void printSpace(int n) throws IOException
 794     {


1217      * <p>The application must not attempt to read from the array
1218      * outside of the specified range.</p>
1219      *
1220      * <p>Note that some parsers will report whitespace using the
1221      * ignorableWhitespace() method rather than this one (validating
1222      * parsers must do so).</p>
1223      *
1224      * @param ch The characters from the XML document.
1225      * @param start The start position in the array.
1226      * @param length The number of characters to read from the array.
1227      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1228      *            wrapping another exception.
1229      * @see #ignorableWhitespace
1230      * @see org.xml.sax.Locator
1231      *
1232      * @throws org.xml.sax.SAXException
1233      */
1234     protected void cdata(char ch[], int start, final int length)
1235         throws org.xml.sax.SAXException
1236     {
1237 
1238         try
1239         {
1240             final int old_start = start;
1241             if (m_elemContext.m_startTagOpen)
1242             {
1243                 closeStartTag();
1244                 m_elemContext.m_startTagOpen = false;
1245             }
1246             m_ispreserve = true;
1247 
1248             if (shouldIndent())
1249                 indent();
1250 
1251             boolean writeCDataBrackets =
1252                 (((length >= 1) && escapingNotNeeded(ch[start])));
1253 
1254             /* Write out the CDATA opening delimiter only if
1255              * we are supposed to, and if we are not already in
1256              * the middle of a CDATA section
1257              */


1306      */
1307     private boolean isEscapingDisabled()
1308     {
1309         return m_disableOutputEscapingStates.peekOrFalse();
1310     }
1311 
1312     /**
1313      * If available, when the disable-output-escaping attribute is used,
1314      * output raw text without escaping.
1315      *
1316      * @param ch The characters from the XML document.
1317      * @param start The start position in the array.
1318      * @param length The number of characters to read from the array.
1319      *
1320      * @throws org.xml.sax.SAXException
1321      */
1322     protected void charactersRaw(char ch[], int start, int length)
1323         throws org.xml.sax.SAXException
1324     {
1325 
1326         if (m_inEntityRef)
1327             return;
1328         try
1329         {
1330             if (m_elemContext.m_startTagOpen)
1331             {
1332                 closeStartTag();
1333                 m_elemContext.m_startTagOpen = false;
1334             }
1335 
1336             m_ispreserve = true;
1337 
1338             m_writer.write(ch, start, length);
1339         }
1340         catch (IOException e)
1341         {
1342             throw new SAXException(e);
1343         }
1344 
1345     }
1346 


1361      * ignorableWhitespace() method rather than this one (validating
1362      * parsers must do so).</p>
1363      *
1364      * @param chars The characters from the XML document.
1365      * @param start The start position in the array.
1366      * @param length The number of characters to read from the array.
1367      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1368      *            wrapping another exception.
1369      * @see #ignorableWhitespace
1370      * @see org.xml.sax.Locator
1371      *
1372      * @throws org.xml.sax.SAXException
1373      */
1374     public void characters(final char chars[], final int start, final int length)
1375         throws org.xml.sax.SAXException
1376     {
1377         // It does not make sense to continue with rest of the method if the number of
1378         // characters to read from array is 0.
1379         // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
1380         // is created if string is empty.
1381         if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
1382             return;
1383         if (m_elemContext.m_startTagOpen)


1384         {
1385             closeStartTag();
1386             m_elemContext.m_startTagOpen = false;
1387         }
1388         else if (m_needToCallStartDocument)
1389         {
1390             startDocumentInternal();
1391         }
1392 
1393         if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
1394         {
1395             /* either due to startCDATA() being called or due to
1396              * cdata-section-elements atribute, we need this as cdata
1397              */
1398             cdata(chars, start, length);
1399 
1400             return;
1401         }
1402 
1403         if (m_cdataTagOpen)
1404             closeCDATA();
1405         // the check with _escaping is a bit of a hack for XLSTC
1406 
1407         if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
1408         {
1409             charactersRaw(chars, start, length);
1410 
1411             // time to fire off characters generation event
1412             if (m_tracer != null)
1413                 super.fireCharEvent(chars, start, length);
1414 
1415             return;
1416         }
1417 
1418         if (m_elemContext.m_startTagOpen)
1419         {
1420             closeStartTag();
1421             m_elemContext.m_startTagOpen = false;
1422         }
1423 




















1424 








1425         try
1426         {
1427             int i;
1428             char ch1;
1429             int startClean;
1430 
1431             // skip any leading whitspace
1432             // don't go off the end and use a hand inlined version
1433             // of isWhitespace(ch)
1434             final int end = start + length;
1435             int lastDirty = start - 1; // last character that needed processing
1436             for (i = start;
1437                 ((i < end)
1438                     && ((ch1 = chars[i]) == 0x20
1439                         || (ch1 == 0xA && m_lineSepUse)
1440                         || ch1 == 0xD
1441                         || ch1 == 0x09));
1442                 i++)
1443             {
1444                 /*


1497                     i = lastDirty;
1498                 }
1499             }
1500 
1501             // we've reached the end. Any clean characters at the
1502             // end of the array than need to be written out?
1503             startClean = lastDirty + 1;
1504             if (i > startClean)
1505             {
1506                 int lengthClean = i - startClean;
1507                 m_writer.write(chars, startClean, lengthClean);
1508             }
1509 
1510             // For indentation purposes, mark that we've just writen text out
1511             m_isprevtext = true;
1512         }
1513         catch (IOException e)
1514         {
1515             throw new SAXException(e);
1516         }

1517 
1518         // time to fire off characters generation event
1519         if (m_tracer != null)
1520             super.fireCharEvent(chars, start, length);
























1521     }


















1522     /**
1523      * This method checks if a given character is between C0 or C1 range
1524      * of Control characters.
1525      * This method is added to support Control Characters for XML 1.1
1526      * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
1527      * return false. Since they are whitespace characters, no special processing is needed.
1528      *
1529      * @param ch
1530      * @return boolean
1531      */
1532     private static boolean isCharacterInC0orC1Range(char ch)
1533     {
1534         if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
1535                 return false;
1536         else
1537                 return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
1538     }
1539     /**
1540      * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
1541      * These are new end of line charcters added in XML 1.1.  These characters must be


1593                     chars,
1594                     end,
1595                     fromTextNode,
1596                     false);
1597             i = startClean - 1;
1598         }
1599         // Return the index of the last character that we just processed
1600         // which is a dirty character.
1601         return i;
1602     }
1603 
1604     /**
1605      * Receive notification of character data.
1606      *
1607      * @param s The string of characters to process.
1608      *
1609      * @throws org.xml.sax.SAXException
1610      */
1611     public void characters(String s) throws org.xml.sax.SAXException
1612     {
1613         if (m_inEntityRef && !m_expandDTDEntities)
1614             return;
1615         final int length = s.length();
1616         if (length > m_charsBuff.length)
1617         {
1618             m_charsBuff = new char[length * 2 + 1];
1619         }
1620         s.getChars(0, length, m_charsBuff, 0);
1621         characters(m_charsBuff, 0, length);
1622     }
1623 
1624     /**
1625      * Escape and writer.write a character.
1626      *
1627      * @param ch character to be escaped.
1628      * @param i index into character array.
1629      * @param chars non-null reference to character array.
1630      * @param len length of chars.
1631      * @param fromTextNode true if the characters being processed are
1632      * from a text node, false if the characters being processed are from
1633      * an attribute value.


1741      * @param localName The local name (without prefix), or the
1742      *        empty string if Namespace processing is not being
1743      *        performed.
1744      * @param name The element type name.
1745      * @param atts The attributes attached to the element, if any.
1746      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1747      *            wrapping another exception.
1748      * @see org.xml.sax.ContentHandler#startElement
1749      * @see org.xml.sax.ContentHandler#endElement
1750      * @see org.xml.sax.AttributeList
1751      *
1752      * @throws org.xml.sax.SAXException
1753      */
1754     public void startElement(
1755         String namespaceURI,
1756         String localName,
1757         String name,
1758         Attributes atts)
1759         throws org.xml.sax.SAXException
1760     {
1761         if (m_inEntityRef)
1762             return;
1763 



1764         if (m_needToCallStartDocument)
1765         {
1766             startDocumentInternal();
1767             m_needToCallStartDocument = false;
1768         }
1769         else if (m_cdataTagOpen)
1770             closeCDATA();
1771         try
1772         {
1773             if ((true == m_needToOutputDocTypeDecl)
1774                 && (null != getDoctypeSystem()))
1775             {
1776                 outputDocTypeDecl(name, true);
1777             }
1778 
1779             m_needToOutputDocTypeDecl = false;
1780 
1781             /* before we over-write the current elementLocalName etc.
1782              * lets close out the old one (if we still need to)
1783              */
1784             if (m_elemContext.m_startTagOpen)
1785             {
1786                 closeStartTag();
1787                 m_elemContext.m_startTagOpen = false;
1788             }
1789 
1790             if (namespaceURI != null)
1791                 ensurePrefixIsDeclared(namespaceURI, name);
1792 
1793             m_ispreserve = false;
1794 


1795             if (shouldIndent() && m_startNewLine)
1796             {
1797                 indent();
1798             }
1799 
1800             m_startNewLine = true;
1801 
1802             final Writer writer = m_writer;
1803             writer.write('<');
1804             writer.write(name);
1805         }
1806         catch (IOException e)
1807         {
1808             throw new SAXException(e);
1809         }
1810 
1811         // process the attributes now, because after this SAX call they might be gone
1812         if (atts != null)
1813             addAttributes(atts);
1814 






1815         m_elemContext = m_elemContext.push(namespaceURI,localName,name);
1816         m_isprevtext = false;
1817 
1818         if (m_tracer != null){
1819             firePseudoAttributes();
1820         }
1821 
1822     }
1823 
1824     /**
1825       * Receive notification of the beginning of an element, additional
1826       * namespace or attribute information can occur before or after this call,
1827       * that is associated with this element.
1828       *
1829       *
1830       * @param elementNamespaceURI The Namespace URI, or the empty string if the
1831       *        element has no Namespace URI or if Namespace
1832       *        processing is not being performed.
1833       * @param elementLocalName The local name (without prefix), or the
1834       *        empty string if Namespace processing is not being


2002     /**
2003      * Receive notification of the end of an element.
2004      *
2005      *
2006      * @param namespaceURI The Namespace URI, or the empty string if the
2007      *        element has no Namespace URI or if Namespace
2008      *        processing is not being performed.
2009      * @param localName The local name (without prefix), or the
2010      *        empty string if Namespace processing is not being
2011      *        performed.
2012      * @param name The element type name
2013      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2014      *            wrapping another exception.
2015      *
2016      * @throws org.xml.sax.SAXException
2017      */
2018     public void endElement(String namespaceURI, String localName, String name)
2019         throws org.xml.sax.SAXException
2020     {
2021 
2022         if (m_inEntityRef)
2023             return;
2024 

2025         // namespaces declared at the current depth are no longer valid
2026         // so get rid of them
2027         m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2028 
2029         try
2030         {
2031             final Writer writer = m_writer;
2032             if (m_elemContext.m_startTagOpen)
2033             {
2034                 if (m_tracer != null)
2035                     super.fireStartElem(m_elemContext.m_elementName);
2036                 int nAttrs = m_attributes.getLength();
2037                 if (nAttrs > 0)
2038                 {
2039                     processAttributes(m_writer, nAttrs);
2040                     // clear attributes object for re-use with next element
2041                     m_attributes.clear();
2042                 }
2043                 if (m_spaceBeforeClose)
2044                     writer.write(" />");
2045                 else
2046                     writer.write("/>");
2047                 /* don't need to pop cdataSectionState because
2048                  * this element ended so quickly that we didn't get
2049                  * to push the state.
2050                  */
2051 
2052             }
2053             else
2054             {
2055                 if (m_cdataTagOpen)
2056                     closeCDATA();
2057 
2058                 if (shouldIndent())
2059                     indent(m_elemContext.m_currentElemDepth - 1);
2060                 writer.write('<');
2061                 writer.write('/');
2062                 writer.write(name);
2063                 writer.write('>');
2064             }
2065         }
2066         catch (IOException e)
2067         {
2068             throw new SAXException(e);
2069         }
2070 
2071         if (!m_elemContext.m_startTagOpen && m_doIndent)
2072         {
2073             m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
2074         }
2075 



2076         m_isprevtext = false;
2077 
2078         // fire off the end element event
2079         if (m_tracer != null)
2080             super.fireEndElem(name);
2081         m_elemContext = m_elemContext.m_prev;
2082     }
2083 
2084     /**
2085      * Receive notification of the end of an element.
2086      * @param name The element type name
2087      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2088      *     wrapping another exception.
2089      */
2090     public void endElement(String name) throws org.xml.sax.SAXException
2091     {
2092         endElement(null, null, name);
2093     }
2094 
2095     /**


2191                 }
2192             }
2193         }
2194         return pushed;
2195     }
2196 
2197     /**
2198      * Receive notification of an XML comment anywhere in the document. This
2199      * callback will be used for comments inside or outside the document
2200      * element, including comments in the external DTD subset (if read).
2201      * @param ch An array holding the characters in the comment.
2202      * @param start The starting position in the array.
2203      * @param length The number of characters to use from the array.
2204      * @throws org.xml.sax.SAXException The application may raise an exception.
2205      */
2206     public void comment(char ch[], int start, int length)
2207         throws org.xml.sax.SAXException
2208     {
2209 
2210         int start_old = start;
2211         if (m_inEntityRef)
2212             return;


2213         if (m_elemContext.m_startTagOpen)
2214         {
2215             closeStartTag();
2216             m_elemContext.m_startTagOpen = false;
2217         }
2218         else if (m_needToCallStartDocument)
2219         {
2220             startDocumentInternal();
2221             m_needToCallStartDocument = false;
2222         }
2223 
2224         try
2225         {
2226             if (shouldIndent() && m_isStandalone)
2227                 indent();
2228 
2229             final int limit = start + length;
2230             boolean wasDash = false;
2231             if (m_cdataTagOpen)
2232                 closeCDATA();


2372      *
2373      * @param name The name of the skipped entity.  If it is a
2374      *       parameter                   entity, the name will begin with '%',
2375      * and if it is the external DTD subset, it will be the string
2376      * "[dtd]".
2377      * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2378      * another exception.
2379      */
2380     public void skippedEntity(String name) throws org.xml.sax.SAXException
2381     { // TODO: Should handle
2382     }
2383 
2384     /**
2385      * Report the start of a CDATA section.
2386      *
2387      * @throws org.xml.sax.SAXException The application may raise an exception.
2388      * @see #endCDATA
2389      */
2390     public void startCDATA() throws org.xml.sax.SAXException
2391     {



2392         m_cdataStartCalled = true;
2393     }
2394 
2395     /**
2396      * Report the beginning of an entity.
2397      *
2398      * The start and end of the document entity are not reported.
2399      * The start and end of the external DTD subset are reported
2400      * using the pseudo-name "[dtd]".  All other events must be
2401      * properly nested within start/end entity events.
2402      *
2403      * @param name The name of the entity.  If it is a parameter
2404      *        entity, the name will begin with '%'.
2405      * @throws org.xml.sax.SAXException The application may raise an exception.
2406      * @see #endEntity
2407      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2408      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2409      */
2410     public void startEntity(String name) throws org.xml.sax.SAXException
2411     {
2412         if (name.equals("[dtd]"))
2413             m_inExternalDTD = true;
2414 
2415         if (!m_expandDTDEntities && !m_inExternalDTD) {
2416             /* Only leave the entity as-is if
2417              * we've been told not to expand them
2418              * and this is not the magic [dtd] name.














2419              */

2420             startNonEscaping();
2421             characters("&" + name + ';');
2422             endNonEscaping();
2423         }
2424 
2425         m_inEntityRef = true;
2426     }
2427 
2428     /**
2429      * For the enclosing elements starting tag write out
2430      * out any attributes followed by ">"
2431      *
2432      * @throws org.xml.sax.SAXException
2433      */
2434     protected void closeStartTag() throws SAXException
2435     {
2436         if (m_elemContext.m_startTagOpen)
2437         {
2438 
2439             try
2440             {
2441                 if (m_tracer != null)
2442                     super.fireStartElem(m_elemContext.m_elementName);
2443                 int nAttrs = m_attributes.getLength();
2444                 if (nAttrs > 0)
2445                 {


2506     }
2507 
2508     /**
2509      * Sets the m_indentAmount.
2510      *
2511      * @param m_indentAmount The m_indentAmount to set
2512      */
2513     public void setIndentAmount(int m_indentAmount)
2514     {
2515         this.m_indentAmount = m_indentAmount;
2516     }
2517 
2518     /**
2519      * Tell if, based on space preservation constraints and the doIndent property,
2520      * if an indent should occur.
2521      *
2522      * @return True if an indent should occur.
2523      */
2524     protected boolean shouldIndent()
2525     {
2526         return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2527     }
2528 
2529     /**
2530      * Searches for the list of qname properties with the specified key in the
2531      * property list. If the key is not found in this property list, the default
2532      * property list, and its defaults, recursively, are then checked. The
2533      * method returns <code>null</code> if the property is not found.
2534      *
2535      * @param   key   the property key.
2536      * @param props the list of properties to search in.
2537      *
2538      * Sets the ArrayList of local-name/URI pairs of the cdata section elements
2539      * specified in the cdata-section-elements property.
2540      *
2541      * This method is essentially a copy of getQNameProperties() from
2542      * OutputProperties. Eventually this method should go away and a call
2543      * to setCdataSectionElements(ArrayList<String> v) should be made directly.
2544      */
2545     private void setCdataSectionElements(String key, Properties props) {
2546         String s = props.getProperty(key);


2798      * stream serializers, not for SAX ones.
2799      *
2800      * @param uri the URI of the attribute
2801      * @param localName the local name of the attribute
2802      * @param rawName   the qualified name of the attribute
2803      * @param type the type of the attribute (probably CDATA)
2804      * @param value the value of the attribute
2805      * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
2806      * @return true if the attribute value was added,
2807      * false if the attribute already existed and the value was
2808      * replaced with the new value.
2809      */
2810     public boolean addAttributeAlways(
2811         String uri,
2812         String localName,
2813         String rawName,
2814         String type,
2815         String value,
2816         boolean xslAttribute)
2817     {



























2818         boolean was_added;
2819         int index;
2820         //if (uri == null || localName == null || uri.length() == 0)
2821             index = m_attributes.getIndex(rawName);
2822         // Don't use 'localName' as it gives incorrect value, rely only on 'rawName'
2823         /*else {
2824             index = m_attributes.getIndex(uri, localName);
2825         }*/
2826         if (index >= 0)
2827         {
2828             String old_value = null;
2829             if (m_tracer != null)
2830             {
2831                 old_value = m_attributes.getValue(index);
2832                 if (value.equals(old_value))
2833                     old_value = null;
2834             }
2835 
2836             /* We've seen the attribute before.
2837              * We may have a null uri or localName, but all we really


2906 
2907                 try
2908                 {
2909                     /* This is our last chance to make sure the namespace for this
2910                      * attribute is declared, especially if we just generated an alternate
2911                      * prefix to avoid a collision (the new prefix/rawName will go out of scope
2912                      * soon and be lost ...  last chance here.
2913                      */
2914                     String prefixUsed =
2915                         ensureAttributesNamespaceIsDeclared(
2916                             uri,
2917                             localName,
2918                             rawName);
2919                 }
2920                 catch (SAXException e)
2921                 {
2922                     // TODO Auto-generated catch block
2923                     e.printStackTrace();
2924                 }
2925             }

2926             m_attributes.addAttribute(uri, localName, rawName, type, value);
2927             was_added = true;
2928             if (m_tracer != null){
2929                 firePseudoAttributes();
2930             }
2931         }













2932         return was_added;
2933     }
2934 
2935     /**
2936      * To fire off the pseudo characters of attributes, as they currently
2937      * exist. This method should be called everytime an attribute is added,
2938      * or when an attribute value is changed, or an element is created.
2939      */
2940     protected void firePseudoAttributes() {
2941         if (m_tracer != null) {
2942             try {
2943                 // flush out the "<elemName" if not already flushed
2944                 m_writer.flush();
2945 
2946                 // make a StringBuffer to write the name="value" pairs to.
2947                 StringBuffer sb = new StringBuffer();
2948                 int nAttrs = m_attributes.getLength();
2949                 if (nAttrs > 0) {
2950                     // make a writer that internally appends to the same
2951                     // StringBuffer


3042     /**
3043      * Reset all of the fields owned by ToStream class
3044      *
3045      */
3046     private void resetToStream() {
3047          this.m_cdataStartCalled = false;
3048          /* The stream is being reset. It is one of
3049           * ToXMLStream, ToHTMLStream ... and this type can't be changed
3050           * so neither should m_charInfo which is associated with the
3051           * type of Stream. Just leave m_charInfo as-is for the next re-use.
3052           */
3053          // this.m_charInfo = null; // don't set to null
3054 
3055          this.m_disableOutputEscapingStates.clear();
3056 
3057          this.m_escaping = true;
3058          // Leave m_format alone for now - Brian M.
3059          // this.m_format = null;
3060          this.m_inDoctype = false;
3061          this.m_ispreserve = false;
3062          this.m_ispreserve = false;





3063          this.m_isprevtext = false;
3064          this.m_isUTF8 = false; //  ?? used anywhere ??
3065          this.m_preserves.clear();
3066          this.m_shouldFlush = true;
3067          this.m_spaceBeforeClose = false;
3068          this.m_startNewLine = false;
3069          this.m_lineSepUse = true;
3070          // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3071          // this.m_writer = null;
3072          this.m_expandDTDEntities = true;
3073 
3074     }
3075 
3076     /**
3077       * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3078       * @param encoding the character encoding
3079       */
3080      public void setEncoding(String encoding)
3081      {
3082          setOutputProperty(OutputKeys.ENCODING,encoding);
3083      }
3084 
3085     /**


3221          *
3222          * @return  <code>true</code> if this stack is empty;
3223          *          <code>false</code> otherwise.
3224          */
3225         public boolean isEmpty() {
3226             return (m_index == -1);
3227         }
3228 
3229         /**
3230          * Grows the size of the stack
3231          *
3232          */
3233         private void grow() {
3234             m_allocatedSize *= 2;
3235             boolean newVector[] = new boolean[m_allocatedSize];
3236             System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
3237             m_values = newVector;
3238         }
3239     }
3240 



























































































































3241     // Implement DTDHandler
3242     /**
3243      * If this method is called, the serializer is used as a
3244      * DTDHandler, which changes behavior how the serializer
3245      * handles document entities.
3246      * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
3247      */
3248     public void notationDecl(String name, String pubID, String sysID) throws SAXException {
3249         // TODO Auto-generated method stub
3250         try {
3251             DTDprolog();
3252 
3253             m_writer.write("<!NOTATION ");
3254             m_writer.write(name);
3255             if (pubID != null) {
3256                 m_writer.write(" PUBLIC \"");
3257                 m_writer.write(pubID);
3258 
3259             }
3260             else {




  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xml.internal.serializer;
  22 
  23 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
  24 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
  25 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
  26 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
  27 import java.io.IOException;
  28 import java.io.OutputStream;
  29 import java.io.OutputStreamWriter;
  30 import java.io.UnsupportedEncodingException;
  31 import java.io.Writer;
  32 import java.util.ArrayDeque;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Deque;
  36 import java.util.EmptyStackException;
  37 import java.util.Enumeration;

  38 import java.util.Properties;
  39 import java.util.Queue;
  40 import java.util.Set;
  41 import java.util.StringTokenizer;

  42 import javax.xml.transform.ErrorListener;
  43 import javax.xml.transform.OutputKeys;
  44 import javax.xml.transform.Transformer;
  45 import javax.xml.transform.TransformerException;
  46 import org.w3c.dom.Node;
  47 import org.xml.sax.Attributes;
  48 import org.xml.sax.ContentHandler;
  49 import org.xml.sax.SAXException;
  50 
  51 /**
  52  * This abstract class is a base class for other stream
  53  * serializers (xml, html, text ...) that write output to a stream.
  54  *
  55  * @xsl.usage internal
  56  */
  57 abstract public class ToStream extends SerializerBase {
  58 
  59     private static final String COMMENT_BEGIN = "<!--";
  60     private static final String COMMENT_END = "-->";
  61 


  76     EncodingInfo m_encodingInfo = new EncodingInfo(null,null);
  77 
  78     /**
  79      * Method reference to the sun.io.CharToByteConverter#canConvert method
  80      * for this encoding.  Invalid if m_charToByteConverter is null.
  81      */
  82     java.lang.reflect.Method m_canConvertMeth;
  83 
  84     /**
  85      * Boolean that tells if we already tried to get the converter.
  86      */
  87     boolean m_triedToGetConverter = false;
  88 
  89     /**
  90      * Opaque reference to the sun.io.CharToByteConverter for this
  91      * encoding.
  92      */
  93     Object m_charToByteConverter = null;
  94 
  95     /**
  96      * Used to buffer the text nodes and the entity reference nodes if
  97      * indentation is on.
  98      */
  99     protected CharacterBuffer m_charactersBuffer = new CharacterBuffer();
 100 
 101     /**
 102      * Used to decide if a text node is pretty-printed with indentation.
 103      * If m_childNodeNum > 1, the text node will be indented.
 104      *
 105      */
 106     protected Deque<Integer> m_childNodeNumStack = new ArrayDeque<>();
 107 
 108     protected int m_childNodeNum = 0;
 109 
 110     /**
 111      * Used to handle xml:space attribute
 112      *
 113      */
 114     protected BoolStack m_preserveSpaces = new BoolStack();
 115 
 116     protected boolean m_ispreserveSpace = false;
 117 
 118 
 119     /**
 120      * Stack to keep track of whether or not we need to
 121      * preserve whitespace.
 122      *
 123      * Used to push/pop values used for the field m_ispreserve, but
 124      * m_ispreserve is only relevant if m_doIndent is true.
 125      * If m_doIndent is false this field has no impact.
 126      *
 127      */
 128     protected BoolStack m_preserves = new BoolStack();
 129 
 130     /**
 131      * State flag to tell if preservation of whitespace
 132      * is important.
 133      *
 134      * Used only in shouldIndent() but only if m_doIndent is true.
 135      * If m_doIndent is false this flag has no impact.
 136      *
 137      */
 138     protected boolean m_ispreserve = false;
 139 


 778         final boolean temp = m_escaping;
 779         m_escaping = escape;
 780         return temp;
 781 
 782     }
 783 
 784 
 785     /**
 786      * Might print a newline character and the indentation amount
 787      * of the given depth.
 788      *
 789      * @param depth the indentation depth (element nesting depth)
 790      *
 791      * @throws org.xml.sax.SAXException if an error occurs during writing.
 792      */
 793     protected void indent(int depth) throws IOException
 794     {
 795 
 796         if (m_startNewLine)
 797             outputLineSep();
 798         /*
 799          * Default value is 4, so printSpace directly.

 800          */

 801         printSpace(depth * m_indentAmount);
 802 
 803     }
 804 
 805     /**
 806      * Indent at the current element nesting depth.
 807      * @throws IOException
 808      */
 809     protected void indent() throws IOException
 810     {
 811         indent(m_elemContext.m_currentElemDepth);
 812     }
 813     /**
 814      * Prints <var>n</var> spaces.
 815      * @param n         Number of spaces to print.
 816      *
 817      * @throws org.xml.sax.SAXException if an error occurs when writing.
 818      */
 819     private void printSpace(int n) throws IOException
 820     {


1243      * <p>The application must not attempt to read from the array
1244      * outside of the specified range.</p>
1245      *
1246      * <p>Note that some parsers will report whitespace using the
1247      * ignorableWhitespace() method rather than this one (validating
1248      * parsers must do so).</p>
1249      *
1250      * @param ch The characters from the XML document.
1251      * @param start The start position in the array.
1252      * @param length The number of characters to read from the array.
1253      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1254      *            wrapping another exception.
1255      * @see #ignorableWhitespace
1256      * @see org.xml.sax.Locator
1257      *
1258      * @throws org.xml.sax.SAXException
1259      */
1260     protected void cdata(char ch[], int start, final int length)
1261         throws org.xml.sax.SAXException
1262     {

1263         try
1264         {
1265             final int old_start = start;
1266             if (m_elemContext.m_startTagOpen)
1267             {
1268                 closeStartTag();
1269                 m_elemContext.m_startTagOpen = false;
1270             }
1271             m_ispreserve = true;
1272 
1273             if (shouldIndent())
1274                 indent();
1275 
1276             boolean writeCDataBrackets =
1277                 (((length >= 1) && escapingNotNeeded(ch[start])));
1278 
1279             /* Write out the CDATA opening delimiter only if
1280              * we are supposed to, and if we are not already in
1281              * the middle of a CDATA section
1282              */


1331      */
1332     private boolean isEscapingDisabled()
1333     {
1334         return m_disableOutputEscapingStates.peekOrFalse();
1335     }
1336 
1337     /**
1338      * If available, when the disable-output-escaping attribute is used,
1339      * output raw text without escaping.
1340      *
1341      * @param ch The characters from the XML document.
1342      * @param start The start position in the array.
1343      * @param length The number of characters to read from the array.
1344      *
1345      * @throws org.xml.sax.SAXException
1346      */
1347     protected void charactersRaw(char ch[], int start, int length)
1348         throws org.xml.sax.SAXException
1349     {
1350 
1351         if (isInEntityRef())
1352             return;
1353         try
1354         {
1355             if (m_elemContext.m_startTagOpen)
1356             {
1357                 closeStartTag();
1358                 m_elemContext.m_startTagOpen = false;
1359             }
1360 
1361             m_ispreserve = true;
1362 
1363             m_writer.write(ch, start, length);
1364         }
1365         catch (IOException e)
1366         {
1367             throw new SAXException(e);
1368         }
1369 
1370     }
1371 


1386      * ignorableWhitespace() method rather than this one (validating
1387      * parsers must do so).</p>
1388      *
1389      * @param chars The characters from the XML document.
1390      * @param start The start position in the array.
1391      * @param length The number of characters to read from the array.
1392      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1393      *            wrapping another exception.
1394      * @see #ignorableWhitespace
1395      * @see org.xml.sax.Locator
1396      *
1397      * @throws org.xml.sax.SAXException
1398      */
1399     public void characters(final char chars[], final int start, final int length)
1400         throws org.xml.sax.SAXException
1401     {
1402         // It does not make sense to continue with rest of the method if the number of
1403         // characters to read from array is 0.
1404         // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
1405         // is created if string is empty.
1406         if (length == 0 || (isInEntityRef()))
1407             return;
1408 
1409         final boolean shouldFormat = shouldFormatOutput();
1410         if (m_elemContext.m_startTagOpen && !shouldFormat)
1411         {
1412             closeStartTag();
1413             m_elemContext.m_startTagOpen = false;
1414         }
1415         else if (m_needToCallStartDocument)
1416         {
1417             startDocumentInternal();
1418         }
1419 
1420         if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
1421         {
1422             /* either due to startCDATA() being called or due to
1423              * cdata-section-elements atribute, we need this as cdata
1424              */
1425             cdata(chars, start, length);
1426 
1427             return;
1428         }
1429 
1430         if (m_cdataTagOpen)
1431             closeCDATA();
1432         // the check with _escaping is a bit of a hack for XLSTC
1433 
1434         if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
1435         {
1436             charactersRaw(chars, start, length);
1437             m_isprevtext = true;
1438             // time to fire off characters generation event
1439             if (m_tracer != null)
1440                 super.fireCharEvent(chars, start, length);
1441 
1442             return;
1443         }
1444 
1445         if (m_elemContext.m_startTagOpen && !shouldFormat)
1446         {
1447             closeStartTag();
1448             m_elemContext.m_startTagOpen = false;
1449         }
1450 
1451         if (shouldFormat) {
1452             m_charactersBuffer.addText(chars, start, length);
1453         } else {
1454             outputCharacters(chars, start, length);
1455         }
1456 
1457         // time to fire off characters generation event
1458         if (m_tracer != null)
1459             super.fireCharEvent(chars, start, length);
1460     }
1461 
1462 
1463     /**
1464      * This method checks if the content in current element should be formatted.
1465      *
1466      * @return True if the content should be formatted.
1467      */
1468     protected boolean shouldFormatOutput() {
1469         return !m_ispreserveSpace && m_doIndent;
1470     }
1471 
1472     /**
1473      * Write out the characters.
1474      *
1475      * @param chars The characters of the text.
1476      * @param start The start position in the char array.
1477      * @param length The number of characters from the char array.
1478      */
1479     private void outputCharacters(final char chars[], final int start, final int length) throws SAXException {
1480         try
1481         {
1482             int i;
1483             char ch1;
1484             int startClean;
1485 
1486             // skip any leading whitspace
1487             // don't go off the end and use a hand inlined version
1488             // of isWhitespace(ch)
1489             final int end = start + length;
1490             int lastDirty = start - 1; // last character that needed processing
1491             for (i = start;
1492                 ((i < end)
1493                     && ((ch1 = chars[i]) == 0x20
1494                         || (ch1 == 0xA && m_lineSepUse)
1495                         || ch1 == 0xD
1496                         || ch1 == 0x09));
1497                 i++)
1498             {
1499                 /*


1552                     i = lastDirty;
1553                 }
1554             }
1555 
1556             // we've reached the end. Any clean characters at the
1557             // end of the array than need to be written out?
1558             startClean = lastDirty + 1;
1559             if (i > startClean)
1560             {
1561                 int lengthClean = i - startClean;
1562                 m_writer.write(chars, startClean, lengthClean);
1563             }
1564 
1565             // For indentation purposes, mark that we've just writen text out
1566             m_isprevtext = true;
1567         }
1568         catch (IOException e)
1569         {
1570             throw new SAXException(e);
1571         }
1572     }
1573 
1574     /**
1575      * Used to flush the buffered characters when indentation is on, this method
1576      * will be called when the next node is traversed.
1577      *
1578      */
1579     final protected void flushCharactersBuffer() throws SAXException {
1580         try {
1581             if (shouldFormatOutput() && m_charactersBuffer.hasContent()) {
1582                 if (m_elemContext.m_startTagOpen) {
1583                     closeStartTag();
1584                     m_elemContext.m_startTagOpen = false;
1585                 }
1586 
1587                 if (m_elemContext.m_isCdataSection) {
1588                     /*
1589                      * due to cdata-section-elements atribute, we need this as
1590                      * cdata
1591                      */
1592                     char[] chars = m_charactersBuffer.toChars();
1593                     cdata(chars, 0, chars.length);
1594                     return;
1595                 }
1596 
1597                 m_childNodeNum++;
1598                 if (shouldIndentForText()) {
1599                     indent();
1600                     m_startNewLine = true;
1601                 }
1602                 m_charactersBuffer.flush();
1603             }
1604         } catch (IOException e) {
1605             throw new SAXException(e);
1606         } finally {
1607             m_charactersBuffer.clear();
1608         }
1609     }
1610 
1611     /**
1612      * True if should indent in flushCharactersBuffer method.
1613      * This method may be overridden in sub-class.
1614      *
1615      */
1616     protected boolean shouldIndentForText() {
1617         return (shouldIndent() && m_childNodeNum > 1);
1618     }
1619 
1620     /**
1621      * This method checks if a given character is between C0 or C1 range
1622      * of Control characters.
1623      * This method is added to support Control Characters for XML 1.1
1624      * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
1625      * return false. Since they are whitespace characters, no special processing is needed.
1626      *
1627      * @param ch
1628      * @return boolean
1629      */
1630     private static boolean isCharacterInC0orC1Range(char ch)
1631     {
1632         if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
1633                 return false;
1634         else
1635                 return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
1636     }
1637     /**
1638      * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
1639      * These are new end of line charcters added in XML 1.1.  These characters must be


1691                     chars,
1692                     end,
1693                     fromTextNode,
1694                     false);
1695             i = startClean - 1;
1696         }
1697         // Return the index of the last character that we just processed
1698         // which is a dirty character.
1699         return i;
1700     }
1701 
1702     /**
1703      * Receive notification of character data.
1704      *
1705      * @param s The string of characters to process.
1706      *
1707      * @throws org.xml.sax.SAXException
1708      */
1709     public void characters(String s) throws org.xml.sax.SAXException
1710     {
1711         if (isInEntityRef())
1712             return;
1713         final int length = s.length();
1714         if (length > m_charsBuff.length)
1715         {
1716             m_charsBuff = new char[length * 2 + 1];
1717         }
1718         s.getChars(0, length, m_charsBuff, 0);
1719         characters(m_charsBuff, 0, length);
1720     }
1721 
1722     /**
1723      * Escape and writer.write a character.
1724      *
1725      * @param ch character to be escaped.
1726      * @param i index into character array.
1727      * @param chars non-null reference to character array.
1728      * @param len length of chars.
1729      * @param fromTextNode true if the characters being processed are
1730      * from a text node, false if the characters being processed are from
1731      * an attribute value.


1839      * @param localName The local name (without prefix), or the
1840      *        empty string if Namespace processing is not being
1841      *        performed.
1842      * @param name The element type name.
1843      * @param atts The attributes attached to the element, if any.
1844      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1845      *            wrapping another exception.
1846      * @see org.xml.sax.ContentHandler#startElement
1847      * @see org.xml.sax.ContentHandler#endElement
1848      * @see org.xml.sax.AttributeList
1849      *
1850      * @throws org.xml.sax.SAXException
1851      */
1852     public void startElement(
1853         String namespaceURI,
1854         String localName,
1855         String name,
1856         Attributes atts)
1857         throws org.xml.sax.SAXException
1858     {
1859         if (isInEntityRef())
1860             return;
1861 
1862         m_childNodeNum++;
1863         flushCharactersBuffer();
1864 
1865         if (m_needToCallStartDocument)
1866         {
1867             startDocumentInternal();
1868             m_needToCallStartDocument = false;
1869         }
1870         else if (m_cdataTagOpen)
1871             closeCDATA();
1872         try
1873         {
1874             if ((true == m_needToOutputDocTypeDecl)
1875                 && (null != getDoctypeSystem()))
1876             {
1877                 outputDocTypeDecl(name, true);
1878             }
1879 
1880             m_needToOutputDocTypeDecl = false;
1881 
1882             /* before we over-write the current elementLocalName etc.
1883              * lets close out the old one (if we still need to)
1884              */
1885             if (m_elemContext.m_startTagOpen)
1886             {
1887                 closeStartTag();
1888                 m_elemContext.m_startTagOpen = false;
1889             }
1890 
1891             if (namespaceURI != null)
1892                 ensurePrefixIsDeclared(namespaceURI, name);
1893 
1894             m_ispreserve = false;
1895 
1896 
1897 
1898             if (shouldIndent() && m_startNewLine)
1899             {
1900                 indent();
1901             }
1902 
1903             m_startNewLine = true;
1904 
1905             final Writer writer = m_writer;
1906             writer.write('<');
1907             writer.write(name);
1908         }
1909         catch (IOException e)
1910         {
1911             throw new SAXException(e);
1912         }
1913 
1914         // process the attributes now, because after this SAX call they might be gone
1915         if (atts != null)
1916             addAttributes(atts);
1917 
1918         m_ispreserveSpace = m_preserveSpaces.peekOrFalse();
1919         m_preserveSpaces.push(m_ispreserveSpace);
1920 
1921         m_childNodeNumStack.push(m_childNodeNum);
1922         m_childNodeNum = 0;
1923 
1924         m_elemContext = m_elemContext.push(namespaceURI,localName,name);
1925         m_isprevtext = false;
1926 
1927         if (m_tracer != null){
1928             firePseudoAttributes();
1929         }
1930 
1931     }
1932 
1933     /**
1934       * Receive notification of the beginning of an element, additional
1935       * namespace or attribute information can occur before or after this call,
1936       * that is associated with this element.
1937       *
1938       *
1939       * @param elementNamespaceURI The Namespace URI, or the empty string if the
1940       *        element has no Namespace URI or if Namespace
1941       *        processing is not being performed.
1942       * @param elementLocalName The local name (without prefix), or the
1943       *        empty string if Namespace processing is not being


2111     /**
2112      * Receive notification of the end of an element.
2113      *
2114      *
2115      * @param namespaceURI The Namespace URI, or the empty string if the
2116      *        element has no Namespace URI or if Namespace
2117      *        processing is not being performed.
2118      * @param localName The local name (without prefix), or the
2119      *        empty string if Namespace processing is not being
2120      *        performed.
2121      * @param name The element type name
2122      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2123      *            wrapping another exception.
2124      *
2125      * @throws org.xml.sax.SAXException
2126      */
2127     public void endElement(String namespaceURI, String localName, String name)
2128         throws org.xml.sax.SAXException
2129     {
2130 
2131         if (isInEntityRef())
2132             return;
2133 
2134         flushCharactersBuffer();
2135         // namespaces declared at the current depth are no longer valid
2136         // so get rid of them
2137         m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2138 
2139         try
2140         {
2141             final Writer writer = m_writer;
2142             if (m_elemContext.m_startTagOpen)
2143             {
2144                 if (m_tracer != null)
2145                     super.fireStartElem(m_elemContext.m_elementName);
2146                 int nAttrs = m_attributes.getLength();
2147                 if (nAttrs > 0)
2148                 {
2149                     processAttributes(m_writer, nAttrs);
2150                     // clear attributes object for re-use with next element
2151                     m_attributes.clear();
2152                 }
2153                 if (m_spaceBeforeClose)
2154                     writer.write(" />");
2155                 else
2156                     writer.write("/>");
2157                 /* don't need to pop cdataSectionState because
2158                  * this element ended so quickly that we didn't get
2159                  * to push the state.
2160                  */
2161 
2162             }
2163             else
2164             {
2165                 if (m_cdataTagOpen)
2166                     closeCDATA();
2167 
2168                 if (shouldIndent() && (m_childNodeNum > 1 || !m_isprevtext))
2169                     indent(m_elemContext.m_currentElemDepth - 1);
2170                 writer.write('<');
2171                 writer.write('/');
2172                 writer.write(name);
2173                 writer.write('>');
2174             }
2175         }
2176         catch (IOException e)
2177         {
2178             throw new SAXException(e);
2179         }
2180 
2181         if (!m_elemContext.m_startTagOpen && m_doIndent)
2182         {
2183             m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
2184         }
2185 
2186         m_ispreserveSpace = m_preserveSpaces.popAndTop();
2187         m_childNodeNum = m_childNodeNumStack.pop();
2188 
2189         m_isprevtext = false;
2190 
2191         // fire off the end element event
2192         if (m_tracer != null)
2193             super.fireEndElem(name);
2194         m_elemContext = m_elemContext.m_prev;
2195     }
2196 
2197     /**
2198      * Receive notification of the end of an element.
2199      * @param name The element type name
2200      * @throws org.xml.sax.SAXException Any SAX exception, possibly
2201      *     wrapping another exception.
2202      */
2203     public void endElement(String name) throws org.xml.sax.SAXException
2204     {
2205         endElement(null, null, name);
2206     }
2207 
2208     /**


2304                 }
2305             }
2306         }
2307         return pushed;
2308     }
2309 
2310     /**
2311      * Receive notification of an XML comment anywhere in the document. This
2312      * callback will be used for comments inside or outside the document
2313      * element, including comments in the external DTD subset (if read).
2314      * @param ch An array holding the characters in the comment.
2315      * @param start The starting position in the array.
2316      * @param length The number of characters to use from the array.
2317      * @throws org.xml.sax.SAXException The application may raise an exception.
2318      */
2319     public void comment(char ch[], int start, int length)
2320         throws org.xml.sax.SAXException
2321     {
2322 
2323         int start_old = start;
2324         if (isInEntityRef())
2325             return;
2326         m_childNodeNum++;
2327         flushCharactersBuffer();
2328         if (m_elemContext.m_startTagOpen)
2329         {
2330             closeStartTag();
2331             m_elemContext.m_startTagOpen = false;
2332         }
2333         else if (m_needToCallStartDocument)
2334         {
2335             startDocumentInternal();
2336             m_needToCallStartDocument = false;
2337         }
2338 
2339         try
2340         {
2341             if (shouldIndent() && m_isStandalone)
2342                 indent();
2343 
2344             final int limit = start + length;
2345             boolean wasDash = false;
2346             if (m_cdataTagOpen)
2347                 closeCDATA();


2487      *
2488      * @param name The name of the skipped entity.  If it is a
2489      *       parameter                   entity, the name will begin with '%',
2490      * and if it is the external DTD subset, it will be the string
2491      * "[dtd]".
2492      * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2493      * another exception.
2494      */
2495     public void skippedEntity(String name) throws org.xml.sax.SAXException
2496     { // TODO: Should handle
2497     }
2498 
2499     /**
2500      * Report the start of a CDATA section.
2501      *
2502      * @throws org.xml.sax.SAXException The application may raise an exception.
2503      * @see #endCDATA
2504      */
2505     public void startCDATA() throws org.xml.sax.SAXException
2506     {
2507         m_childNodeNum++;
2508         flushCharactersBuffer();
2509 
2510         m_cdataStartCalled = true;
2511     }
2512 
2513     /**
2514      * Report the beginning of an entity.
2515      *
2516      * The start and end of the document entity are not reported.
2517      * The start and end of the external DTD subset are reported
2518      * using the pseudo-name "[dtd]".  All other events must be
2519      * properly nested within start/end entity events.
2520      *
2521      * @param name The name of the entity.  If it is a parameter
2522      *        entity, the name will begin with '%'.
2523      * @throws org.xml.sax.SAXException The application may raise an exception.
2524      * @see #endEntity
2525      * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2526      * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2527      */
2528     public void startEntity(String name) throws org.xml.sax.SAXException
2529     {
2530         if (name.equals("[dtd]"))
2531             m_inExternalDTD = true;
2532 
2533         // if this is not the magic [dtd] name
2534         if (!m_inExternalDTD) {
2535             // if it's not in nested entity reference
2536             if (!isInEntityRef()) {
2537                 if (shouldFormatOutput()) {
2538                     m_charactersBuffer.addEntityReference(name);
2539                 } else {
2540                     outputEntityReference(name);
2541                 }
2542             }
2543             m_inEntityRef++;
2544         }
2545     }
2546 
2547     /**
2548      * Write out the entity reference with the form as "&amp;entityName;".
2549      *
2550      * @param name The name of the entity.
2551      */
2552     private void outputEntityReference(String name) throws SAXException {
2553         startNonEscaping();
2554         characters("&" + name + ';');
2555         endNonEscaping();
2556         m_isprevtext = true;


2557     }
2558 
2559     /**
2560      * For the enclosing elements starting tag write out
2561      * out any attributes followed by ">"
2562      *
2563      * @throws org.xml.sax.SAXException
2564      */
2565     protected void closeStartTag() throws SAXException
2566     {
2567         if (m_elemContext.m_startTagOpen)
2568         {
2569 
2570             try
2571             {
2572                 if (m_tracer != null)
2573                     super.fireStartElem(m_elemContext.m_elementName);
2574                 int nAttrs = m_attributes.getLength();
2575                 if (nAttrs > 0)
2576                 {


2637     }
2638 
2639     /**
2640      * Sets the m_indentAmount.
2641      *
2642      * @param m_indentAmount The m_indentAmount to set
2643      */
2644     public void setIndentAmount(int m_indentAmount)
2645     {
2646         this.m_indentAmount = m_indentAmount;
2647     }
2648 
2649     /**
2650      * Tell if, based on space preservation constraints and the doIndent property,
2651      * if an indent should occur.
2652      *
2653      * @return True if an indent should occur.
2654      */
2655     protected boolean shouldIndent()
2656     {
2657         return shouldFormatOutput() && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2658     }
2659 
2660     /**
2661      * Searches for the list of qname properties with the specified key in the
2662      * property list. If the key is not found in this property list, the default
2663      * property list, and its defaults, recursively, are then checked. The
2664      * method returns <code>null</code> if the property is not found.
2665      *
2666      * @param   key   the property key.
2667      * @param props the list of properties to search in.
2668      *
2669      * Sets the ArrayList of local-name/URI pairs of the cdata section elements
2670      * specified in the cdata-section-elements property.
2671      *
2672      * This method is essentially a copy of getQNameProperties() from
2673      * OutputProperties. Eventually this method should go away and a call
2674      * to setCdataSectionElements(ArrayList<String> v) should be made directly.
2675      */
2676     private void setCdataSectionElements(String key, Properties props) {
2677         String s = props.getProperty(key);


2929      * stream serializers, not for SAX ones.
2930      *
2931      * @param uri the URI of the attribute
2932      * @param localName the local name of the attribute
2933      * @param rawName   the qualified name of the attribute
2934      * @param type the type of the attribute (probably CDATA)
2935      * @param value the value of the attribute
2936      * @param xslAttribute true if this attribute is coming from an xsl:attribute element.
2937      * @return true if the attribute value was added,
2938      * false if the attribute already existed and the value was
2939      * replaced with the new value.
2940      */
2941     public boolean addAttributeAlways(
2942         String uri,
2943         String localName,
2944         String rawName,
2945         String type,
2946         String value,
2947         boolean xslAttribute)
2948     {
2949         if (m_charactersBuffer.isAnyCharactersBuffered()) {
2950             /*
2951              * If stylesheet includes xsl:copy-of an attribute node, XSLTC will
2952              * fire an addAttribute event. When a text node is handling in
2953              * ToStream, addAttribute has no effect. But closeStartTag call is
2954              * delayed to flushCharactersBuffer() method if the text node is
2955              * buffered, so here we ignore the attribute to avoid corrupting the
2956              * start tag content.
2957              *
2958              */
2959             return m_attributes.getIndex(rawName) < 0;
2960         } else {
2961             return doAddAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
2962         }
2963     }
2964 
2965     /**
2966      * Does really add the attribute to the set of attributes.
2967      */
2968     private boolean doAddAttributeAlways(
2969         String uri,
2970         String localName,
2971         String rawName,
2972         String type,
2973         String value,
2974         boolean xslAttribute)
2975     {
2976         boolean was_added;
2977         int index;
2978         //if (uri == null || localName == null || uri.length() == 0)
2979         index = m_attributes.getIndex(rawName);
2980         // Don't use 'localName' as it gives incorrect value, rely only on 'rawName'
2981         /*else {
2982             index = m_attributes.getIndex(uri, localName);
2983         }*/
2984         if (index >= 0)
2985         {
2986             String old_value = null;
2987             if (m_tracer != null)
2988             {
2989                 old_value = m_attributes.getValue(index);
2990                 if (value.equals(old_value))
2991                     old_value = null;
2992             }
2993 
2994             /* We've seen the attribute before.
2995              * We may have a null uri or localName, but all we really


3064 
3065                 try
3066                 {
3067                     /* This is our last chance to make sure the namespace for this
3068                      * attribute is declared, especially if we just generated an alternate
3069                      * prefix to avoid a collision (the new prefix/rawName will go out of scope
3070                      * soon and be lost ...  last chance here.
3071                      */
3072                     String prefixUsed =
3073                         ensureAttributesNamespaceIsDeclared(
3074                             uri,
3075                             localName,
3076                             rawName);
3077                 }
3078                 catch (SAXException e)
3079                 {
3080                     // TODO Auto-generated catch block
3081                     e.printStackTrace();
3082                 }
3083             }
3084 
3085             m_attributes.addAttribute(uri, localName, rawName, type, value);
3086             was_added = true;
3087             if (m_tracer != null){
3088                 firePseudoAttributes();
3089             }
3090         }
3091 
3092         if (rawName.equals("xml:space")) {
3093             if (value.equals("preserve")) {
3094                 m_ispreserveSpace = true;
3095                 if (m_preserveSpaces.size() > 0)
3096                     m_preserveSpaces.setTop(m_ispreserveSpace);
3097             } else if (value.equals("default")) {
3098                 m_ispreserveSpace = false;
3099                 if (m_preserveSpaces.size() > 0)
3100                     m_preserveSpaces.setTop(m_ispreserveSpace);
3101             }
3102         }
3103 
3104         return was_added;
3105     }
3106 
3107     /**
3108      * To fire off the pseudo characters of attributes, as they currently
3109      * exist. This method should be called everytime an attribute is added,
3110      * or when an attribute value is changed, or an element is created.
3111      */
3112     protected void firePseudoAttributes() {
3113         if (m_tracer != null) {
3114             try {
3115                 // flush out the "<elemName" if not already flushed
3116                 m_writer.flush();
3117 
3118                 // make a StringBuffer to write the name="value" pairs to.
3119                 StringBuffer sb = new StringBuffer();
3120                 int nAttrs = m_attributes.getLength();
3121                 if (nAttrs > 0) {
3122                     // make a writer that internally appends to the same
3123                     // StringBuffer


3214     /**
3215      * Reset all of the fields owned by ToStream class
3216      *
3217      */
3218     private void resetToStream() {
3219          this.m_cdataStartCalled = false;
3220          /* The stream is being reset. It is one of
3221           * ToXMLStream, ToHTMLStream ... and this type can't be changed
3222           * so neither should m_charInfo which is associated with the
3223           * type of Stream. Just leave m_charInfo as-is for the next re-use.
3224           */
3225          // this.m_charInfo = null; // don't set to null
3226 
3227          this.m_disableOutputEscapingStates.clear();
3228 
3229          this.m_escaping = true;
3230          // Leave m_format alone for now - Brian M.
3231          // this.m_format = null;
3232          this.m_inDoctype = false;
3233          this.m_ispreserve = false;
3234          this.m_preserves.clear();
3235          this.m_ispreserveSpace = false;
3236          this.m_preserveSpaces.clear();
3237          this.m_childNodeNum = 0;
3238          this.m_childNodeNumStack.clear();
3239          this.m_charactersBuffer.clear();
3240          this.m_isprevtext = false;
3241          this.m_isUTF8 = false; //  ?? used anywhere ??

3242          this.m_shouldFlush = true;
3243          this.m_spaceBeforeClose = false;
3244          this.m_startNewLine = false;
3245          this.m_lineSepUse = true;
3246          // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3247          // this.m_writer = null;
3248          this.m_expandDTDEntities = true;
3249 
3250     }
3251 
3252     /**
3253       * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3254       * @param encoding the character encoding
3255       */
3256      public void setEncoding(String encoding)
3257      {
3258          setOutputProperty(OutputKeys.ENCODING,encoding);
3259      }
3260 
3261     /**


3397          *
3398          * @return  <code>true</code> if this stack is empty;
3399          *          <code>false</code> otherwise.
3400          */
3401         public boolean isEmpty() {
3402             return (m_index == -1);
3403         }
3404 
3405         /**
3406          * Grows the size of the stack
3407          *
3408          */
3409         private void grow() {
3410             m_allocatedSize *= 2;
3411             boolean newVector[] = new boolean[m_allocatedSize];
3412             System.arraycopy(m_values, 0, newVector, 0, m_index + 1);
3413             m_values = newVector;
3414         }
3415     }
3416 
3417     /**
3418      * This inner class is used to buffer the text nodes and the entity
3419      * reference nodes if indentation is on. There is only one CharacterBuffer
3420      * instance in ToStream, it contains a queue of GenericCharacters,
3421      * GenericCharacters can be a text node or an entity reference node. The
3422      * text nodes and entity reference nodes are joined together and then are
3423      * flushed.
3424      */
3425     private class CharacterBuffer {
3426         /**
3427          * GenericCharacters is immutable.
3428          */
3429         private abstract class GenericCharacters {
3430             /**
3431              * @return True if having any character other than whitespace or
3432              *         line feed.
3433              */
3434             abstract boolean hasContent();
3435 
3436             abstract void flush() throws SAXException;
3437 
3438             /**
3439              * Converts this GenericCharacters to a new character array.
3440              */
3441             abstract char[] toChars();
3442         }
3443 
3444         private Queue<GenericCharacters> bufferedCharacters = new ArrayDeque<>();
3445 
3446         /**
3447          * Append a text node to the buffer.
3448          */
3449         public void addText(final char chars[], final int start, final int length) {
3450             bufferedCharacters.add(new GenericCharacters() {
3451                 char[] text;
3452 
3453                 {
3454                     text = Arrays.copyOfRange(chars, start, start + length);
3455                 }
3456 
3457                 boolean hasContent() {
3458                     for (int i = 0; i < text.length; i++) {
3459                         if (!isWhiteSpace(text[i])) {
3460                             return true;
3461                         }
3462                     }
3463                     return false;
3464                 }
3465 
3466                 void flush() throws SAXException {
3467                     outputCharacters(text, 0, text.length);
3468                 }
3469 
3470                 char[] toChars() {
3471                     return text;
3472                 }
3473                 
3474                 boolean isWhiteSpace(char ch) {
3475                     return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
3476                 }
3477 
3478             });
3479         }
3480 
3481         /**
3482          * Append an entity reference to the buffer.
3483          */
3484         public void addEntityReference(String entityName) {
3485             bufferedCharacters.add(new GenericCharacters() {
3486                 boolean hasContent() {
3487                     return true;
3488                 }
3489 
3490                 void flush() throws SAXException {
3491                     outputEntityReference(entityName);
3492                 }
3493 
3494                 char[] toChars() {
3495                     return ("&" + entityName + ";").toCharArray();
3496                 }
3497             });
3498         }
3499 
3500         /**
3501          * @return True if any GenericCharacters is already buffered.
3502          */
3503         public boolean isAnyCharactersBuffered() {
3504             return !bufferedCharacters.isEmpty();
3505         }
3506 
3507         /**
3508          * @return True if any buffered GenericCharacters has content.
3509          */
3510         public boolean hasContent() {
3511             return bufferedCharacters.stream().anyMatch(GenericCharacters::hasContent);
3512         }
3513 
3514         /**
3515          * Flush all buffered GenericCharacters.
3516          */
3517         public void flush() throws SAXException {
3518             GenericCharacters element;
3519             while ((element = bufferedCharacters.poll()) != null)
3520                 element.flush();
3521         }
3522 
3523         /**
3524          * Converts all buffered GenericCharacters to a new character array.
3525          */
3526         public char[] toChars() {
3527             return bufferedCharacters.stream().map(GenericCharacters::toChars)
3528                     .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString()
3529                     .toCharArray();
3530         }
3531 
3532         /**
3533          * Clear the buffer.
3534          */
3535         public void clear() {
3536             bufferedCharacters.clear();
3537         }
3538     }
3539 
3540     // Implement DTDHandler
3541     /**
3542      * If this method is called, the serializer is used as a
3543      * DTDHandler, which changes behavior how the serializer
3544      * handles document entities.
3545      * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
3546      */
3547     public void notationDecl(String name, String pubID, String sysID) throws SAXException {
3548         // TODO Auto-generated method stub
3549         try {
3550             DTDprolog();
3551 
3552             m_writer.write("<!NOTATION ");
3553             m_writer.write(name);
3554             if (pubID != null) {
3555                 m_writer.write(" PUBLIC \"");
3556                 m_writer.write(pubID);
3557 
3558             }
3559             else {


< prev index next >