1 /*
2 * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements. See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 package com.sun.org.apache.xml.internal.serializer;
22
34 import java.util.Enumeration;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.Set;
39 import java.util.StringTokenizer;
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 import org.w3c.dom.Node;
45 import org.xml.sax.Attributes;
46 import org.xml.sax.ContentHandler;
47 import org.xml.sax.SAXException;
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 *
53 * @xsl.usage internal
54 * @LastModified: Sept 2018
55 */
56 abstract public class ToStream extends SerializerBase {
57
58 private static final String COMMENT_BEGIN = "<!--";
59 private static final String COMMENT_END = "-->";
60
61 /** Stack to keep track of disabling output escaping. */
62 protected BoolStack m_disableOutputEscapingStates = new BoolStack();
63
64 /**
65 * The encoding information associated with this serializer.
66 * Although initially there is no encoding,
67 * there is a dummy EncodingInfo object that will say
68 * that every character is in the encoding. This is useful
69 * for a serializer that is in temporary output state and has
70 * no associated encoding. A serializer in final output state
71 * will have an encoding, and will worry about whether
72 * single chars or surrogate pairs of high/low chars form
73 * characters in the output encoding.
74 */
1214 * @param length The number of characters to read from the array.
1215 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1216 * wrapping another exception.
1217 * @see #ignorableWhitespace
1218 * @see org.xml.sax.Locator
1219 *
1220 * @throws org.xml.sax.SAXException
1221 */
1222 protected void cdata(char ch[], int start, final int length)
1223 throws org.xml.sax.SAXException
1224 {
1225 try
1226 {
1227 final int old_start = start;
1228 if (m_elemContext.m_startTagOpen)
1229 {
1230 closeStartTag();
1231 m_elemContext.m_startTagOpen = false;
1232 }
1233
1234 if (!m_cdataTagOpen && shouldIndent())
1235 indent();
1236
1237 boolean writeCDataBrackets =
1238 (((length >= 1) && escapingNotNeeded(ch[start])));
1239
1240 /* Write out the CDATA opening delimiter only if
1241 * we are supposed to, and if we are not already in
1242 * the middle of a CDATA section
1243 */
1244 if (writeCDataBrackets && !m_cdataTagOpen)
1245 {
1246 m_writer.write(CDATA_DELIMITER_OPEN);
1247 m_cdataTagOpen = true;
1248 }
1249
1250 // writer.write(ch, start, length);
1251 if (isEscapingDisabled())
1252 {
1253 charactersRaw(ch, start, length);
1254 }
1255 else
1256 writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1257
1258 /* used to always write out CDATA closing delimiter here,
1259 * but now we delay, so that we can merge CDATA sections on output.
1260 * need to write closing delimiter later
1261 */
1262 if (writeCDataBrackets)
1263 {
1264 /* if the CDATA section ends with ] don't leave it open
1265 * as there is a chance that an adjacent CDATA sections
1266 * starts with ]>.
1267 * We don't want to merge ]] with > , or ] with ]>
1268 */
1269 if (ch[start + length - 1] == ']')
1270 closeCDATA();
1271 }
1272
1273 // time to fire off CDATA event
1274 if (m_tracer != null)
1275 super.fireCDATAEvent(ch, old_start, length);
1276 }
1277 catch (IOException ioe)
1278 {
1279 throw new org.xml.sax.SAXException(
1280 Utils.messages.createMessage(
1281 MsgKey.ER_OIERROR,
1282 null),
1283 ioe);
1284 //"IO error", ioe);
1285 }
1286 }
1287
1288 /**
1289 * Tell if the character escaping should be disabled for the current state.
1290 *
1291 * @return true if the character escaping should be disabled.
1292 */
1519
1520 // we've reached the end. Any clean characters at the
1521 // end of the array than need to be written out?
1522 startClean = lastDirty + 1;
1523 if (i > startClean)
1524 {
1525 int lengthClean = i - startClean;
1526 m_writer.write(chars, startClean, lengthClean);
1527 }
1528
1529 // For indentation purposes, mark that we've just writen text out
1530 m_isprevtext = true;
1531 }
1532 catch (IOException e)
1533 {
1534 throw new SAXException(e);
1535 }
1536 }
1537
1538 /**
1539 * Used to flush the buffered characters when indentation is on, this method
1540 * will be called when the next node is traversed.
1541 *
1542 */
1543 final protected void flushCharactersBuffer() throws SAXException {
1544 try {
1545 if (shouldFormatOutput() && m_charactersBuffer.isAnyCharactersBuffered()) {
1546 if (m_elemContext.m_isCdataSection) {
1547 /*
1548 * due to cdata-section-elements atribute, we need this as
1549 * cdata
1550 */
1551 char[] chars = m_charactersBuffer.toChars();
1552 cdata(chars, 0, chars.length);
1553 return;
1554 }
1555
1556 m_childNodeNum++;
1557 boolean skipBeginningNewlines = false;
1558 if (shouldIndentForText()) {
1559 indent();
1560 m_startNewLine = true;
1561 // newline has always been added here because if this is the
1562 // text before the first element, shouldIndent() won't
1563 // return true.
1564 skipBeginningNewlines = true;
1565 }
1566 m_charactersBuffer.flush(skipBeginningNewlines);
1567 }
1568 } catch (IOException e) {
1569 throw new SAXException(e);
1570 } finally {
1571 m_charactersBuffer.clear();
1572 }
1573 }
1574
1575 /**
1576 * True if should indent in flushCharactersBuffer method.
1829 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1830 * wrapping another exception.
1831 * @see org.xml.sax.ContentHandler#startElement
1832 * @see org.xml.sax.ContentHandler#endElement
1833 * @see org.xml.sax.AttributeList
1834 *
1835 * @throws org.xml.sax.SAXException
1836 */
1837 public void startElement(
1838 String namespaceURI,
1839 String localName,
1840 String name,
1841 Attributes atts)
1842 throws org.xml.sax.SAXException
1843 {
1844 if (isInEntityRef())
1845 return;
1846
1847 if (m_doIndent) {
1848 m_childNodeNum++;
1849 flushCharactersBuffer();
1850 }
1851
1852 if (m_needToCallStartDocument)
1853 {
1854 startDocumentInternal();
1855 m_needToCallStartDocument = false;
1856 }
1857 else if (m_cdataTagOpen)
1858 closeCDATA();
1859 try
1860 {
1861 if ((true == m_needToOutputDocTypeDecl)
1862 && (null != getDoctypeSystem()))
1863 {
1864 outputDocTypeDecl(name, true);
1865 }
1866
1867 m_needToOutputDocTypeDecl = false;
1868
1869 /* before we over-write the current elementLocalName etc.
2100 * @param namespaceURI The Namespace URI, or the empty string if the
2101 * element has no Namespace URI or if Namespace
2102 * processing is not being performed.
2103 * @param localName The local name (without prefix), or the
2104 * empty string if Namespace processing is not being
2105 * performed.
2106 * @param name The element type name
2107 * @throws org.xml.sax.SAXException Any SAX exception, possibly
2108 * wrapping another exception.
2109 *
2110 * @throws org.xml.sax.SAXException
2111 */
2112 public void endElement(String namespaceURI, String localName, String name)
2113 throws org.xml.sax.SAXException
2114 {
2115
2116 if (isInEntityRef())
2117 return;
2118
2119 if (m_doIndent) {
2120 flushCharactersBuffer();
2121 }
2122 // namespaces declared at the current depth are no longer valid
2123 // so get rid of them
2124 m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2125
2126 try
2127 {
2128 final Writer writer = m_writer;
2129 if (m_elemContext.m_startTagOpen)
2130 {
2131 if (m_tracer != null)
2132 super.fireStartElem(m_elemContext.m_elementName);
2133 int nAttrs = m_attributes.getLength();
2134 if (nAttrs > 0)
2135 {
2136 processAttributes(m_writer, nAttrs);
2137 // clear attributes object for re-use with next element
2138 m_attributes.clear();
2139 }
2140 if (m_spaceBeforeClose)
2292 }
2293
2294 /**
2295 * Receive notification of an XML comment anywhere in the document. This
2296 * callback will be used for comments inside or outside the document
2297 * element, including comments in the external DTD subset (if read).
2298 * @param ch An array holding the characters in the comment.
2299 * @param start The starting position in the array.
2300 * @param length The number of characters to use from the array.
2301 * @throws org.xml.sax.SAXException The application may raise an exception.
2302 */
2303 public void comment(char ch[], int start, int length)
2304 throws org.xml.sax.SAXException
2305 {
2306
2307 int start_old = start;
2308 if (isInEntityRef())
2309 return;
2310 if (m_doIndent) {
2311 m_childNodeNum++;
2312 flushCharactersBuffer();
2313 }
2314 if (m_elemContext.m_startTagOpen)
2315 {
2316 closeStartTag();
2317 m_elemContext.m_startTagOpen = false;
2318 }
2319 else if (m_needToCallStartDocument)
2320 {
2321 startDocumentInternal();
2322 m_needToCallStartDocument = false;
2323 }
2324
2325 try
2326 {
2327 if (shouldIndent() && m_isStandalone)
2328 indent();
2329
2330 final int limit = start + length;
2331 boolean wasDash = false;
2332 if (m_cdataTagOpen)
2474 * @param name The name of the skipped entity. If it is a
2475 * parameter entity, the name will begin with '%',
2476 * and if it is the external DTD subset, it will be the string
2477 * "[dtd]".
2478 * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2479 * another exception.
2480 */
2481 public void skippedEntity(String name) throws org.xml.sax.SAXException
2482 { // TODO: Should handle
2483 }
2484
2485 /**
2486 * Report the start of a CDATA section.
2487 *
2488 * @throws org.xml.sax.SAXException The application may raise an exception.
2489 * @see #endCDATA
2490 */
2491 public void startCDATA() throws org.xml.sax.SAXException
2492 {
2493 if (m_doIndent) {
2494 m_childNodeNum++;
2495 flushCharactersBuffer();
2496 }
2497
2498 m_cdataStartCalled = true;
2499 }
2500
2501 /**
2502 * Report the beginning of an entity.
2503 *
2504 * The start and end of the document entity are not reported.
2505 * The start and end of the external DTD subset are reported
2506 * using the pseudo-name "[dtd]". All other events must be
2507 * properly nested within start/end entity events.
2508 *
2509 * @param name The name of the entity. If it is a parameter
2510 * entity, the name will begin with '%'.
2511 * @throws org.xml.sax.SAXException The application may raise an exception.
2512 * @see #endEntity
2513 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2514 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2515 */
|
1 /*
2 * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements. See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 package com.sun.org.apache.xml.internal.serializer;
22
34 import java.util.Enumeration;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Properties;
38 import java.util.Set;
39 import java.util.StringTokenizer;
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 import org.w3c.dom.Node;
45 import org.xml.sax.Attributes;
46 import org.xml.sax.ContentHandler;
47 import org.xml.sax.SAXException;
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 *
53 * @xsl.usage internal
54 * @LastModified: July 2019
55 */
56 abstract public class ToStream extends SerializerBase {
57
58 private static final String COMMENT_BEGIN = "<!--";
59 private static final String COMMENT_END = "-->";
60
61 /** Stack to keep track of disabling output escaping. */
62 protected BoolStack m_disableOutputEscapingStates = new BoolStack();
63
64 /**
65 * The encoding information associated with this serializer.
66 * Although initially there is no encoding,
67 * there is a dummy EncodingInfo object that will say
68 * that every character is in the encoding. This is useful
69 * for a serializer that is in temporary output state and has
70 * no associated encoding. A serializer in final output state
71 * will have an encoding, and will worry about whether
72 * single chars or surrogate pairs of high/low chars form
73 * characters in the output encoding.
74 */
1214 * @param length The number of characters to read from the array.
1215 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1216 * wrapping another exception.
1217 * @see #ignorableWhitespace
1218 * @see org.xml.sax.Locator
1219 *
1220 * @throws org.xml.sax.SAXException
1221 */
1222 protected void cdata(char ch[], int start, final int length)
1223 throws org.xml.sax.SAXException
1224 {
1225 try
1226 {
1227 final int old_start = start;
1228 if (m_elemContext.m_startTagOpen)
1229 {
1230 closeStartTag();
1231 m_elemContext.m_startTagOpen = false;
1232 }
1233
1234 if (!m_cdataTagOpen && shouldIndentForText())
1235 indent();
1236
1237 boolean writeCDataBrackets =
1238 (((length >= 1) && escapingNotNeeded(ch[start])));
1239
1240 /* Write out the CDATA opening delimiter only if
1241 * we are supposed to, and if we are not already in
1242 * the middle of a CDATA section
1243 */
1244 if (writeCDataBrackets && !m_cdataTagOpen)
1245 {
1246 m_writer.write(CDATA_DELIMITER_OPEN);
1247 m_cdataTagOpen = true;
1248 }
1249
1250 // writer.write(ch, start, length);
1251 if (isEscapingDisabled())
1252 {
1253 charactersRaw(ch, start, length);
1254 }
1255 else
1256 writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1257
1258 /* used to always write out CDATA closing delimiter here,
1259 * but now we delay, so that we can merge CDATA sections on output.
1260 * need to write closing delimiter later
1261 */
1262 if (writeCDataBrackets)
1263 {
1264 /* if the CDATA section ends with ] don't leave it open
1265 * as there is a chance that an adjacent CDATA sections
1266 * starts with ]>.
1267 * We don't want to merge ]] with > , or ] with ]>
1268 */
1269 if (ch[start + length - 1] == ']')
1270 closeCDATA();
1271 }
1272
1273 m_isprevtext = true;
1274 // time to fire off CDATA event
1275 if (m_tracer != null)
1276 super.fireCDATAEvent(ch, old_start, length);
1277 }
1278 catch (IOException ioe)
1279 {
1280 throw new org.xml.sax.SAXException(
1281 Utils.messages.createMessage(
1282 MsgKey.ER_OIERROR,
1283 null),
1284 ioe);
1285 //"IO error", ioe);
1286 }
1287 }
1288
1289 /**
1290 * Tell if the character escaping should be disabled for the current state.
1291 *
1292 * @return true if the character escaping should be disabled.
1293 */
1520
1521 // we've reached the end. Any clean characters at the
1522 // end of the array than need to be written out?
1523 startClean = lastDirty + 1;
1524 if (i > startClean)
1525 {
1526 int lengthClean = i - startClean;
1527 m_writer.write(chars, startClean, lengthClean);
1528 }
1529
1530 // For indentation purposes, mark that we've just writen text out
1531 m_isprevtext = true;
1532 }
1533 catch (IOException e)
1534 {
1535 throw new SAXException(e);
1536 }
1537 }
1538
1539 /**
1540 * Flushes the buffered characters when indentation is on. This method
1541 * is called before the next node is traversed.
1542 *
1543 * @param isText indicates whether the node to be traversed is text
1544 * @throws org.xml.sax.SAXException
1545 */
1546 final protected void flushCharactersBuffer(boolean isText) throws SAXException {
1547 try {
1548 if (shouldFormatOutput() && m_charactersBuffer.isAnyCharactersBuffered()) {
1549 if (m_elemContext.m_isCdataSection) {
1550 /*
1551 * due to cdata-section-elements atribute, we need this as
1552 * cdata
1553 */
1554 char[] chars = m_charactersBuffer.toChars();
1555 cdata(chars, 0, chars.length);
1556 return;
1557 }
1558
1559 if (!isText) {
1560 m_childNodeNum++;
1561 }
1562 boolean skipBeginningNewlines = false;
1563 if (shouldIndentForText()) {
1564 indent();
1565 m_startNewLine = true;
1566 // newline has always been added here because if this is the
1567 // text before the first element, shouldIndent() won't
1568 // return true.
1569 skipBeginningNewlines = true;
1570 }
1571 m_charactersBuffer.flush(skipBeginningNewlines);
1572 }
1573 } catch (IOException e) {
1574 throw new SAXException(e);
1575 } finally {
1576 m_charactersBuffer.clear();
1577 }
1578 }
1579
1580 /**
1581 * True if should indent in flushCharactersBuffer method.
1834 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1835 * wrapping another exception.
1836 * @see org.xml.sax.ContentHandler#startElement
1837 * @see org.xml.sax.ContentHandler#endElement
1838 * @see org.xml.sax.AttributeList
1839 *
1840 * @throws org.xml.sax.SAXException
1841 */
1842 public void startElement(
1843 String namespaceURI,
1844 String localName,
1845 String name,
1846 Attributes atts)
1847 throws org.xml.sax.SAXException
1848 {
1849 if (isInEntityRef())
1850 return;
1851
1852 if (m_doIndent) {
1853 m_childNodeNum++;
1854 flushCharactersBuffer(false);
1855 }
1856
1857 if (m_needToCallStartDocument)
1858 {
1859 startDocumentInternal();
1860 m_needToCallStartDocument = false;
1861 }
1862 else if (m_cdataTagOpen)
1863 closeCDATA();
1864 try
1865 {
1866 if ((true == m_needToOutputDocTypeDecl)
1867 && (null != getDoctypeSystem()))
1868 {
1869 outputDocTypeDecl(name, true);
1870 }
1871
1872 m_needToOutputDocTypeDecl = false;
1873
1874 /* before we over-write the current elementLocalName etc.
2105 * @param namespaceURI The Namespace URI, or the empty string if the
2106 * element has no Namespace URI or if Namespace
2107 * processing is not being performed.
2108 * @param localName The local name (without prefix), or the
2109 * empty string if Namespace processing is not being
2110 * performed.
2111 * @param name The element type name
2112 * @throws org.xml.sax.SAXException Any SAX exception, possibly
2113 * wrapping another exception.
2114 *
2115 * @throws org.xml.sax.SAXException
2116 */
2117 public void endElement(String namespaceURI, String localName, String name)
2118 throws org.xml.sax.SAXException
2119 {
2120
2121 if (isInEntityRef())
2122 return;
2123
2124 if (m_doIndent) {
2125 flushCharactersBuffer(false);
2126 }
2127 // namespaces declared at the current depth are no longer valid
2128 // so get rid of them
2129 m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2130
2131 try
2132 {
2133 final Writer writer = m_writer;
2134 if (m_elemContext.m_startTagOpen)
2135 {
2136 if (m_tracer != null)
2137 super.fireStartElem(m_elemContext.m_elementName);
2138 int nAttrs = m_attributes.getLength();
2139 if (nAttrs > 0)
2140 {
2141 processAttributes(m_writer, nAttrs);
2142 // clear attributes object for re-use with next element
2143 m_attributes.clear();
2144 }
2145 if (m_spaceBeforeClose)
2297 }
2298
2299 /**
2300 * Receive notification of an XML comment anywhere in the document. This
2301 * callback will be used for comments inside or outside the document
2302 * element, including comments in the external DTD subset (if read).
2303 * @param ch An array holding the characters in the comment.
2304 * @param start The starting position in the array.
2305 * @param length The number of characters to use from the array.
2306 * @throws org.xml.sax.SAXException The application may raise an exception.
2307 */
2308 public void comment(char ch[], int start, int length)
2309 throws org.xml.sax.SAXException
2310 {
2311
2312 int start_old = start;
2313 if (isInEntityRef())
2314 return;
2315 if (m_doIndent) {
2316 m_childNodeNum++;
2317 flushCharactersBuffer(false);
2318 }
2319 if (m_elemContext.m_startTagOpen)
2320 {
2321 closeStartTag();
2322 m_elemContext.m_startTagOpen = false;
2323 }
2324 else if (m_needToCallStartDocument)
2325 {
2326 startDocumentInternal();
2327 m_needToCallStartDocument = false;
2328 }
2329
2330 try
2331 {
2332 if (shouldIndent() && m_isStandalone)
2333 indent();
2334
2335 final int limit = start + length;
2336 boolean wasDash = false;
2337 if (m_cdataTagOpen)
2479 * @param name The name of the skipped entity. If it is a
2480 * parameter entity, the name will begin with '%',
2481 * and if it is the external DTD subset, it will be the string
2482 * "[dtd]".
2483 * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2484 * another exception.
2485 */
2486 public void skippedEntity(String name) throws org.xml.sax.SAXException
2487 { // TODO: Should handle
2488 }
2489
2490 /**
2491 * Report the start of a CDATA section.
2492 *
2493 * @throws org.xml.sax.SAXException The application may raise an exception.
2494 * @see #endCDATA
2495 */
2496 public void startCDATA() throws org.xml.sax.SAXException
2497 {
2498 if (m_doIndent) {
2499 flushCharactersBuffer(true);
2500 }
2501
2502 m_cdataStartCalled = true;
2503 }
2504
2505 /**
2506 * Report the beginning of an entity.
2507 *
2508 * The start and end of the document entity are not reported.
2509 * The start and end of the external DTD subset are reported
2510 * using the pseudo-name "[dtd]". All other events must be
2511 * properly nested within start/end entity events.
2512 *
2513 * @param name The name of the entity. If it is a parameter
2514 * entity, the name will begin with '%'.
2515 * @throws org.xml.sax.SAXException The application may raise an exception.
2516 * @see #endEntity
2517 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2518 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2519 */
|