1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.serialize;
  58. import java.io.*;
  59. import org.apache.xalan.res.XSLMessages;
  60. import org.apache.xalan.res.XSLTErrorResources;
  61. /**
  62. * This class writes ASCII to a byte stream as quickly as possible. For the
  63. * moment it does not do buffering, though I reserve the right to do some
  64. * buffering down the line if I can prove that it will be faster even if the
  65. * output stream is buffered.
  66. */
  67. public final class WriterToUTF8Buffered extends Writer
  68. {
  69. /** The byte stream to write to. (sc & sb remove final to compile in JDK 1.1.8) */
  70. private OutputStream m_os;
  71. /**
  72. * The internal buffer where data is stored.
  73. * (sc & sb remove final to compile in JDK 1.1.8)
  74. */
  75. private byte buf[];
  76. /**
  77. * The number of valid bytes in the buffer. This value is always
  78. * in the range <tt>0</tt> through <tt>buf.length</tt> elements
  79. * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid
  80. * byte data.
  81. */
  82. private int count;
  83. /**
  84. * Create an buffered UTF-8 writer.
  85. *
  86. *
  87. * @param out the underlying output stream.
  88. *
  89. * @throws UnsupportedEncodingException
  90. */
  91. public WriterToUTF8Buffered(OutputStream out)
  92. throws UnsupportedEncodingException
  93. {
  94. this(out, 8 * 1024);
  95. }
  96. /**
  97. * Create an buffered UTF-8 writer to write data to the
  98. * specified underlying output stream with the specified buffer
  99. * size.
  100. *
  101. * @param out the underlying output stream.
  102. * @param size the buffer size.
  103. * @exception IllegalArgumentException if size <= 0.
  104. */
  105. public WriterToUTF8Buffered(final OutputStream out, final int size)
  106. {
  107. m_os = out;
  108. if (size <= 0)
  109. {
  110. throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_BUFFER_SIZE_LESSTHAN_ZERO, null)); //"Buffer size <= 0");
  111. }
  112. buf = new byte[size];
  113. count = 0;
  114. }
  115. /**
  116. * Write a single character. The character to be written is contained in
  117. * the 16 low-order bits of the given integer value; the 16 high-order bits
  118. * are ignored.
  119. *
  120. * <p> Subclasses that intend to support efficient single-character output
  121. * should override this method.
  122. *
  123. * @param c int specifying a character to be written.
  124. * @exception IOException If an I/O error occurs
  125. */
  126. public void write(final int c) throws IOException
  127. {
  128. if (c < 0x80)
  129. {
  130. if (count >= buf.length)
  131. flushBuffer();
  132. buf[count++] = (byte) (c);
  133. }
  134. else if (c < 0x800)
  135. {
  136. if (count+1 >= buf.length)
  137. flushBuffer();
  138. buf[count++] = (byte) (0xc0 + (c >> 6));
  139. buf[count++] = (byte) (0x80 + (c & 0x3f));
  140. }
  141. else
  142. {
  143. if (count+2 >= buf.length)
  144. flushBuffer();
  145. buf[count++] = (byte) (0xe0 + (c >> 12));
  146. buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
  147. buf[count++] = (byte) (0x80 + (c & 0x3f));
  148. }
  149. }
  150. /**
  151. * Write a portion of an array of characters.
  152. *
  153. * @param chars Array of characters
  154. * @param start Offset from which to start writing characters
  155. * @param length Number of characters to write
  156. *
  157. * @exception IOException If an I/O error occurs
  158. *
  159. * @throws java.io.IOException
  160. */
  161. private final void writeDirect(
  162. final char chars[], final int start, final int length)
  163. throws java.io.IOException
  164. {
  165. final OutputStream os = m_os;
  166. int n = length+start;
  167. for (int i = start; i < n; i++)
  168. {
  169. final char c = chars[i];
  170. if (c < 0x80)
  171. os.write(c);
  172. else if (c < 0x800)
  173. {
  174. os.write(0xc0 + (c >> 6));
  175. os.write(0x80 + (c & 0x3f));
  176. }
  177. else
  178. {
  179. os.write(0xe0 + (c >> 12));
  180. os.write(0x80 + ((c >> 6) & 0x3f));
  181. os.write(0x80 + (c & 0x3f));
  182. }
  183. }
  184. }
  185. /**
  186. * Write a string.
  187. *
  188. * @param s String to be written
  189. *
  190. * @exception IOException If an I/O error occurs
  191. */
  192. private final void writeDirect(final String s) throws IOException
  193. {
  194. final int n = s.length();
  195. final OutputStream os = m_os;
  196. for (int i = 0; i < n; i++)
  197. {
  198. final char c = s.charAt(i);
  199. if (c < 0x80)
  200. os.write(c);
  201. else if (c < 0x800)
  202. {
  203. os.write(0xc0 + (c >> 6));
  204. os.write(0x80 + (c & 0x3f));
  205. }
  206. else
  207. {
  208. os.write(0xe0 + (c >> 12));
  209. os.write(0x80 + ((c >> 6) & 0x3f));
  210. os.write(0x80 + (c & 0x3f));
  211. }
  212. }
  213. }
  214. /**
  215. * Write a portion of an array of characters.
  216. *
  217. * @param chars Array of characters
  218. * @param start Offset from which to start writing characters
  219. * @param length Number of characters to write
  220. *
  221. * @exception IOException If an I/O error occurs
  222. *
  223. * @throws java.io.IOException
  224. */
  225. public void write(final char chars[], final int start, final int length)
  226. throws java.io.IOException
  227. {
  228. // We multiply the length by three since this is the maximum length
  229. // of the characters that we can put into the buffer. It is possible
  230. // for each Unicode character to expand to three bytes.
  231. int lengthx3 = (length << 1) + length;
  232. if (lengthx3 >= buf.length)
  233. {
  234. /* If the request length exceeds the size of the output buffer,
  235. flush the output buffer and then write the data directly.
  236. In this way buffered streams will cascade harmlessly. */
  237. flushBuffer();
  238. writeDirect(chars, start, length);
  239. return;
  240. }
  241. if (lengthx3 > buf.length - count)
  242. {
  243. flushBuffer();
  244. }
  245. final OutputStream os = m_os;
  246. int n = length+start;
  247. for (int i = start; i < n; i++)
  248. {
  249. final char c = chars[i];
  250. if (c < 0x80)
  251. buf[count++] = (byte) (c);
  252. else if (c < 0x800)
  253. {
  254. buf[count++] = (byte) (0xc0 + (c >> 6));
  255. buf[count++] = (byte) (0x80 + (c & 0x3f));
  256. }
  257. else
  258. {
  259. buf[count++] = (byte) (0xe0 + (c >> 12));
  260. buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
  261. buf[count++] = (byte) (0x80 + (c & 0x3f));
  262. }
  263. }
  264. }
  265. /**
  266. * Write a string.
  267. *
  268. * @param s String to be written
  269. *
  270. * @exception IOException If an I/O error occurs
  271. */
  272. public void write(final String s) throws IOException
  273. {
  274. final int length = s.length();
  275. // We multiply the length by three since this is the maximum length
  276. // of the characters that we can put into the buffer. It is possible
  277. // for each Unicode character to expand to three bytes.
  278. int lengthx3 = (length << 1) + length;
  279. if (lengthx3 >= buf.length)
  280. {
  281. /* If the request length exceeds the size of the output buffer,
  282. flush the output buffer and then write the data directly.
  283. In this way buffered streams will cascade harmlessly. */
  284. flushBuffer();
  285. writeDirect(s);
  286. return;
  287. }
  288. if (lengthx3 > buf.length - count)
  289. {
  290. flushBuffer();
  291. }
  292. final OutputStream os = m_os;
  293. for (int i = 0; i < length; i++)
  294. {
  295. final char c = s.charAt(i);
  296. if (c < 0x80)
  297. buf[count++] = (byte) (c);
  298. else if (c < 0x800)
  299. {
  300. buf[count++] = (byte) (0xc0 + (c >> 6));
  301. buf[count++] = (byte) (0x80 + (c & 0x3f));
  302. }
  303. else
  304. {
  305. buf[count++] = (byte) (0xe0 + (c >> 12));
  306. buf[count++] = (byte) (0x80 + ((c >> 6) & 0x3f));
  307. buf[count++] = (byte) (0x80 + (c & 0x3f));
  308. }
  309. }
  310. }
  311. /**
  312. * Flush the internal buffer
  313. *
  314. * @throws IOException
  315. */
  316. public void flushBuffer() throws IOException
  317. {
  318. if (count > 0)
  319. {
  320. m_os.write(buf, 0, count);
  321. count = 0;
  322. }
  323. }
  324. /**
  325. * Flush the stream. If the stream has saved any characters from the
  326. * various write() methods in a buffer, write them immediately to their
  327. * intended destination. Then, if that destination is another character or
  328. * byte stream, flush it. Thus one flush() invocation will flush all the
  329. * buffers in a chain of Writers and OutputStreams.
  330. *
  331. * @exception IOException If an I/O error occurs
  332. *
  333. * @throws java.io.IOException
  334. */
  335. public void flush() throws java.io.IOException
  336. {
  337. flushBuffer();
  338. m_os.flush();
  339. }
  340. /**
  341. * Close the stream, flushing it first. Once a stream has been closed,
  342. * further write() or flush() invocations will cause an IOException to be
  343. * thrown. Closing a previously-closed stream, however, has no effect.
  344. *
  345. * @exception IOException If an I/O error occurs
  346. *
  347. * @throws java.io.IOException
  348. */
  349. public void close() throws java.io.IOException
  350. {
  351. flushBuffer();
  352. m_os.close();
  353. }
  354. /**
  355. * Get the output stream where the events will be serialized to.
  356. *
  357. * @return reference to the result stream, or null of only a writer was
  358. * set.
  359. */
  360. public OutputStream getOutputStream()
  361. {
  362. return m_os;
  363. }
  364. }