KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javolution > io > UTF8StreamWriter


1 /*
2  * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
3  * Copyright (C) 2006 - Javolution (http://javolution.org/)
4  * All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software is
7  * freely granted, provided that this notice is preserved.
8  */

9 package javolution.io;
10
11 import j2me.lang.CharSequence;
12 import j2me.lang.IllegalStateException;
13 import j2me.io.CharConversionException;
14 import java.io.IOException JavaDoc;
15 import java.io.OutputStream JavaDoc;
16 import java.io.Writer JavaDoc;
17
18 import javolution.lang.Reusable;
19
20 /**
21  * <p> This class represents a UTF-8 stream writer.</p>
22  *
23  * <p> This writer supports surrogate <code>char</code> pairs (representing
24  * characters in the range [U+10000 .. U+10FFFF]). It can also be used
25  * to write characters from their unicodes (31 bits) directly
26  * (ref. {@link #write(int)}).</p>
27  *
28  * <p> Instances of this class can be reused for different output streams
29  * and can be part of a higher level component (e.g. serializer) in order
30  * to avoid dynamic buffer allocation when the destination output changes.
31  * Also wrapping using a <code>java.io.BufferedWriter</code> is unnescessary
32  * as instances of this class embed their own data buffers.</p>
33  *
34  * <p> Note: This writer is unsynchronized and always produces well-formed
35  * UTF-8 sequences.</p>
36  *
37  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
38  * @version 2.0, December 9, 2004
39  */

40 public final class UTF8StreamWriter extends Writer JavaDoc implements Reusable {
41
42     /**
43      * Holds the current output stream or <code>null</code> if closed.
44      */

45     private OutputStream JavaDoc _outputStream;
46
47     /**
48      * Holds the bytes' buffer.
49      */

50     private final byte[] _bytes;
51
52     /**
53      * Holds the bytes buffer index.
54      */

55     private int _index;
56
57     /**
58      * Creates a UTF-8 writer having a byte buffer of moderate capacity (2048).
59      */

60     public UTF8StreamWriter() {
61         _bytes = new byte[2048];
62     }
63
64     /**
65      * Creates a UTF-8 writer having a byte buffer of specified capacity.
66      *
67      * @param capacity the capacity of the byte buffer.
68      */

69     public UTF8StreamWriter(int capacity) {
70         _bytes = new byte[capacity];
71     }
72
73     /**
74      * Sets the output stream to use for writing until this writer is closed.
75      * For example:[code]
76      * Writer writer = new UTF8StreamWriter().setOutputStream(out);
77      * [/code] is equivalent but writes faster than [code]
78      * Writer writer = new j2me.io.OutputStreamWriter(out, "UTF-8");
79      * [/code]
80      *
81      * @param out the output stream.
82      * @return this UTF-8 writer.
83      * @throws IllegalStateException if this writer is being reused and
84      * it has not been {@link #close closed} or {@link #reset reset}.
85      */

86     public UTF8StreamWriter setOutput(OutputStream JavaDoc out) {
87         if (_outputStream != null)
88             throw new IllegalStateException JavaDoc("Writer not closed or reset");
89         _outputStream = out;
90         return this;
91     }
92
93     /**
94      * Writes a single character. This method supports 16-bits
95      * character surrogates.
96      *
97      * @param c <code>char</code> the character to be written (possibly
98      * a surrogate).
99      * @throws IOException if an I/O error occurs.
100      */

101     public void write(char c) throws IOException JavaDoc {
102         if ((c < 0xd800) || (c > 0xdfff)) {
103             write((int) c);
104         } else if (c < 0xdc00) { // High surrogate.
105
_highSurrogate = c;
106         } else { // Low surrogate.
107
int code = ((_highSurrogate - 0xd800) << 10) + (c - 0xdc00)
108                     + 0x10000;
109             write(code);
110         }
111     }
112
113     private char _highSurrogate;
114
115     /**
116      * Writes a character given its 31-bits Unicode.
117      *
118      * @param code the 31 bits Unicode of the character to be written.
119      * @throws IOException if an I/O error occurs.
120      */

121     public void write(int code) throws IOException JavaDoc {
122         if ((code & 0xffffff80) == 0) {
123             _bytes[_index] = (byte) code;
124             if (++_index >= _bytes.length) {
125                 flushBuffer();
126             }
127         } else { // Writes more than one byte.
128
write2(code);
129         }
130     }
131
132     private void write2(int c) throws IOException JavaDoc {
133         if ((c & 0xfffff800) == 0) { // 2 bytes.
134
_bytes[_index] = (byte) (0xc0 | (c >> 6));
135             if (++_index >= _bytes.length) {
136                 flushBuffer();
137             }
138             _bytes[_index] = (byte) (0x80 | (c & 0x3f));
139             if (++_index >= _bytes.length) {
140                 flushBuffer();
141             }
142         } else if ((c & 0xffff0000) == 0) { // 3 bytes.
143
_bytes[_index] = (byte) (0xe0 | (c >> 12));
144             if (++_index >= _bytes.length) {
145                 flushBuffer();
146             }
147             _bytes[_index] = (byte) (0x80 | ((c >> 6) & 0x3f));
148             if (++_index >= _bytes.length) {
149                 flushBuffer();
150             }
151             _bytes[_index] = (byte) (0x80 | (c & 0x3f));
152             if (++_index >= _bytes.length) {
153                 flushBuffer();
154             }
155         } else if ((c & 0xff200000) == 0) { // 4 bytes.
156
_bytes[_index] = (byte) (0xf0 | (c >> 18));
157             if (++_index >= _bytes.length) {
158                 flushBuffer();
159             }
160             _bytes[_index] = (byte) (0x80 | ((c >> 12) & 0x3f));
161             if (++_index >= _bytes.length) {
162                 flushBuffer();
163             }
164             _bytes[_index] = (byte) (0x80 | ((c >> 6) & 0x3f));
165             if (++_index >= _bytes.length) {
166                 flushBuffer();
167             }
168             _bytes[_index] = (byte) (0x80 | (c & 0x3f));
169             if (++_index >= _bytes.length) {
170                 flushBuffer();
171             }
172         } else if ((c & 0xf4000000) == 0) { // 5 bytes.
173
_bytes[_index] = (byte) (0xf8 | (c >> 24));
174             if (++_index >= _bytes.length) {
175                 flushBuffer();
176             }
177             _bytes[_index] = (byte) (0x80 | ((c >> 18) & 0x3f));
178             if (++_index >= _bytes.length) {
179                 flushBuffer();
180             }
181             _bytes[_index] = (byte) (0x80 | ((c >> 12) & 0x3f));
182             if (++_index >= _bytes.length) {
183                 flushBuffer();
184             }
185             _bytes[_index] = (byte) (0x80 | ((c >> 6) & 0x3f));
186             if (++_index >= _bytes.length) {
187                 flushBuffer();
188             }
189             _bytes[_index] = (byte) (0x80 | (c & 0x3f));
190             if (++_index >= _bytes.length) {
191                 flushBuffer();
192             }
193         } else if ((c & 0x80000000) == 0) { // 6 bytes.
194
_bytes[_index] = (byte) (0xfc | (c >> 30));
195             if (++_index >= _bytes.length) {
196                 flushBuffer();
197             }
198             _bytes[_index] = (byte) (0x80 | ((c >> 24) & 0x3f));
199             if (++_index >= _bytes.length) {
200                 flushBuffer();
201             }
202             _bytes[_index] = (byte) (0x80 | ((c >> 18) & 0x3f));
203             if (++_index >= _bytes.length) {
204                 flushBuffer();
205             }
206             _bytes[_index] = (byte) (0x80 | ((c >> 12) & 0x3F));
207             if (++_index >= _bytes.length) {
208                 flushBuffer();
209             }
210             _bytes[_index] = (byte) (0x80 | ((c >> 6) & 0x3F));
211             if (++_index >= _bytes.length) {
212                 flushBuffer();
213             }
214             _bytes[_index] = (byte) (0x80 | (c & 0x3F));
215             if (++_index >= _bytes.length) {
216                 flushBuffer();
217             }
218         } else {
219             throw new CharConversionException("Illegal character U+"
220                     + Integer.toHexString(c));
221         }
222     }
223
224     /**
225      * Writes a portion of an array of characters.
226      *
227      * @param cbuf the array of characters.
228      * @param off the offset from which to start writing characters.
229      * @param len the number of characters to write.
230      * @throws IOException if an I/O error occurs.
231      */

232     public void write(char cbuf[], int off, int len) throws IOException JavaDoc {
233         final int off_plus_len = off + len;
234         for (int i = off; i < off_plus_len;) {
235             char c = cbuf[i++];
236             if (c < 0x80) {
237                 _bytes[_index] = (byte) c;
238                 if (++_index >= _bytes.length) {
239                     flushBuffer();
240                 }
241             } else {
242                 write(c);
243             }
244         }
245     }
246
247     /**
248      * Writes a portion of a string.
249      *
250      * @param str a String.
251      * @param off the offset from which to start writing characters.
252      * @param len the number of characters to write.
253      * @throws IOException if an I/O error occurs
254      */

255     public void write(String JavaDoc str, int off, int len) throws IOException JavaDoc {
256         final int off_plus_len = off + len;
257         for (int i = off; i < off_plus_len;) {
258             char c = str.charAt(i++);
259             if (c < 0x80) {
260                 _bytes[_index] = (byte) c;
261                 if (++_index >= _bytes.length) {
262                     flushBuffer();
263                 }
264             } else {
265                 write(c);
266             }
267         }
268     }
269
270     /**
271      * Writes the specified character sequence.
272      *
273      * @param csq the character sequence.
274      * @throws IOException if an I/O error occurs
275      */

276     public void write(CharSequence JavaDoc csq) throws IOException JavaDoc {
277         final int length = csq.length();
278         for (int i = 0; i < length;) {
279             char c = csq.charAt(i++);
280             if (c < 0x80) {
281                 _bytes[_index] = (byte) c;
282                 if (++_index >= _bytes.length) {
283                     flushBuffer();
284                 }
285             } else {
286                 write(c);
287             }
288         }
289     }
290
291     /**
292      * Flushes the stream. If the stream has saved any characters from the
293      * various write() methods in a buffer, write them immediately to their
294      * intended destination. Then, if that destination is another character or
295      * byte stream, flush it. Thus one flush() invocation will flush all the
296      * buffers in a chain of Writers and OutputStreams.
297      *
298      * @throws IOException if an I/O error occurs.
299      */

300     public void flush() throws IOException JavaDoc {
301         flushBuffer();
302         _outputStream.flush();
303     }
304
305     /**
306      * Closes and {@link #reset resets} this writer for reuse.
307      *
308      * @throws IOException if an I/O error occurs
309      */

310     public void close() throws IOException JavaDoc {
311         if (_outputStream != null) {
312             flushBuffer();
313             _outputStream.close();
314             reset();
315         }
316     }
317
318     /**
319      * Flushes the internal bytes buffer.
320      *
321      * @throws IOException if an I/O error occurs
322      */

323     private void flushBuffer() throws IOException JavaDoc {
324         if (_outputStream == null)
325             throw new IOException JavaDoc("Stream closed");
326         _outputStream.write(_bytes, 0, _index);
327         _index = 0;
328     }
329
330     // Implements Reusable.
331
public void reset() {
332         _highSurrogate = 0;
333         _index = 0;
334         _outputStream = null;
335     }
336     
337     /**
338      * @deprecated Replaced by {@link #setOutput(OutputStream)}
339      */

340     public UTF8StreamWriter setOutputStream(OutputStream JavaDoc out) {
341         return this.setOutput(out);
342     }
343 }
Popular Tags