001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2006 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.io;
010:
011: import j2me.lang.CharSequence;
012: import j2me.lang.IllegalStateException;
013: import j2me.io.CharConversionException;
014: import j2me.nio.ByteBuffer;
015:
016: import java.io.IOException;
017: import java.io.Writer;
018:
019: import javolution.lang.Reusable;
020:
021: /**
022: * <p> This class represents a UTF-8 <code>j2me.nio.ByteBuffer</code>
023: * writer.</p>
024: *
025: * <p> This writer supports surrogate <code>char</code> pairs (representing
026: * characters in the range [U+10000 .. U+10FFFF]). It can also be used
027: * to write characters from their unicodes (31 bits) directly
028: * (ref. {@link #write(int)}).</p>
029: *
030: * <p> Instances of this class can be reused for different output streams
031: * and can be part of a higher level component (e.g. serializer) in order
032: * to avoid dynamic buffer allocation when the destination output changes.
033: * Also wrapping using a <code>java.io.BufferedWriter</code> is unnescessary
034: * as instances of this class embed their own data buffers.</p>
035: *
036: * <p> Note: This writer is unsynchronized and always produces well-formed
037: * UTF-8 sequences.</p>
038: *
039: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
040: * @version 2.0, December 9, 2004
041: * @see UTF8ByteBufferReader
042: */
043: public final class UTF8ByteBufferWriter extends Writer implements
044: Reusable {
045:
046: /**
047: * Holds the byte buffer destination.
048: */
049: private ByteBuffer _byteBuffer;
050:
051: /**
052: * Default constructor.
053: */
054: public UTF8ByteBufferWriter() {
055: }
056:
057: /**
058: * Sets the byte buffer to use for writing until this writer is closed.
059: *
060: * @param byteBuffer the destination byte buffer.
061: * @return this UTF-8 writer.
062: * @throws IllegalStateException if this writer is being reused and
063: * it has not been {@link #close closed} or {@link #reset reset}.
064: */
065: public UTF8ByteBufferWriter setOutput(ByteBuffer byteBuffer) {
066: if (_byteBuffer != null)
067: throw new IllegalStateException(
068: "Writer not closed or reset");
069: _byteBuffer = byteBuffer;
070: return this ;
071: }
072:
073: /**
074: * Writes a single character. This method supports 16-bits
075: * character surrogates.
076: *
077: * @param c <code>char</code> the character to be written (possibly
078: * a surrogate).
079: * @throws IOException if an I/O error occurs.
080: */
081: public void write(char c) throws IOException {
082: if ((c < 0xd800) || (c > 0xdfff)) {
083: write((int) c);
084: } else if (c < 0xdc00) { // High surrogate.
085: _highSurrogate = c;
086: } else { // Low surrogate.
087: int code = ((_highSurrogate - 0xd800) << 10) + (c - 0xdc00)
088: + 0x10000;
089: write(code);
090: }
091: }
092:
093: private char _highSurrogate;
094:
095: /**
096: * Writes a character given its 31-bits Unicode.
097: *
098: * @param code the 31 bits Unicode of the character to be written.
099: * @throws IOException if an I/O error occurs.
100: */
101: public void write(int code) throws IOException {
102: if ((code & 0xffffff80) == 0) {
103: _byteBuffer.put((byte) code);
104: } else { // Writes more than one byte.
105: write2(code);
106: }
107: }
108:
109: private void write2(int c) throws IOException {
110: if ((c & 0xfffff800) == 0) { // 2 bytes.
111: _byteBuffer.put((byte) (0xc0 | (c >> 6)));
112: _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
113: } else if ((c & 0xffff0000) == 0) { // 3 bytes.
114: _byteBuffer.put((byte) (0xe0 | (c >> 12)));
115: _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
116: _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
117: } else if ((c & 0xff200000) == 0) { // 4 bytes.
118: _byteBuffer.put((byte) (0xf0 | (c >> 18)));
119: _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3f)));
120: _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
121: _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
122: } else if ((c & 0xf4000000) == 0) { // 5 bytes.
123: _byteBuffer.put((byte) (0xf8 | (c >> 24)));
124: _byteBuffer.put((byte) (0x80 | ((c >> 18) & 0x3f)));
125: _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3f)));
126: _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
127: _byteBuffer.put((byte) (0x80 | (c & 0x3f)));
128: } else if ((c & 0x80000000) == 0) { // 6 bytes.
129: _byteBuffer.put((byte) (0xfc | (c >> 30)));
130: _byteBuffer.put((byte) (0x80 | ((c >> 24) & 0x3f)));
131: _byteBuffer.put((byte) (0x80 | ((c >> 18) & 0x3f)));
132: _byteBuffer.put((byte) (0x80 | ((c >> 12) & 0x3F)));
133: _byteBuffer.put((byte) (0x80 | ((c >> 6) & 0x3F)));
134: _byteBuffer.put((byte) (0x80 | (c & 0x3F)));
135: } else {
136: throw new CharConversionException("Illegal character U+"
137: + Integer.toHexString(c));
138: }
139: }
140:
141: /**
142: * Writes a portion of an array of characters.
143: *
144: * @param cbuf the array of characters.
145: * @param off the offset from which to start writing characters.
146: * @param len the number of characters to write.
147: * @throws IOException if an I/O error occurs.
148: */
149: public void write(char cbuf[], int off, int len) throws IOException {
150: final int off_plus_len = off + len;
151: for (int i = off; i < off_plus_len;) {
152: char c = cbuf[i++];
153: if (c < 0x80) {
154: _byteBuffer.put((byte) c);
155: } else {
156: write(c);
157: }
158: }
159: }
160:
161: /**
162: * Writes a portion of a string.
163: *
164: * @param str a String.
165: * @param off the offset from which to start writing characters.
166: * @param len the number of characters to write.
167: * @throws IOException if an I/O error occurs
168: */
169: public void write(String str, int off, int len) throws IOException {
170: final int off_plus_len = off + len;
171: for (int i = off; i < off_plus_len;) {
172: char c = str.charAt(i++);
173: if (c < 0x80) {
174: _byteBuffer.put((byte) c);
175: } else {
176: write(c);
177: }
178: }
179: }
180:
181: /**
182: * Writes the specified character sequence.
183: *
184: * @param csq the character sequence.
185: * @throws IOException if an I/O error occurs
186: */
187: public void write(CharSequence csq) throws IOException {
188: final int length = csq.length();
189: for (int i = 0; i < length;) {
190: char c = csq.charAt(i++);
191: if (c < 0x80) {
192: _byteBuffer.put((byte) c);
193: } else {
194: write(c);
195: }
196: }
197: }
198:
199: /**
200: * Flushes the stream (this method has no effect, the data is
201: * always directly written to the <code>ByteBuffer</code>).
202: *
203: * @throws IOException if an I/O error occurs.
204: */
205: public void flush() throws IOException {
206: if (_byteBuffer == null) {
207: throw new IOException("Writer closed");
208: }
209: }
210:
211: /**
212: * Closes and {@link #reset resets} this writer for reuse.
213: *
214: * @throws IOException if an I/O error occurs
215: */
216: public void close() throws IOException {
217: if (_byteBuffer != null) {
218: reset();
219: }
220: }
221:
222: // Implements Reusable.
223: public void reset() {
224: _byteBuffer = null;
225: _highSurrogate = 0;
226: }
227:
228: /**
229: * @deprecated Replaced by {@link #setOutput(ByteBuffer)}
230: */
231: public UTF8ByteBufferWriter setByteBuffer(ByteBuffer byteBuffer) {
232: return this.setOutput(byteBuffer);
233: }
234:
235: }
|