1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
/*
2 * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 /*
27 */
28
29 package <a href="http://www.docjar.com/docs/api/sun/nio/cs/package-index.html" target="_blank">sun.nio.cs</a>;
30
31 import <a href="http://www.docjar.com/docs/api/java/io/package-index.html" target="_blank">java.io</a>;
32 import <a href="http://www.docjar.com/docs/api/java/nio/package-index.html" target="_blank">java.nio</a>;
33 import <a href="http://www.docjar.com/docs/api/java/nio/channels/package-index.html" target="_blank">java.nio.channels</a>;
34 import <a href="http://www.docjar.com/docs/api/java/nio/charset/package-index.html" target="_blank">java.nio.charset</a>;
35
36 public class StreamEncoder extends Writer
37 {
38
39 private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
40
41 private volatile boolean isOpen = true;
42
43 private void ensureOpen() throws IOException {
44 if (!isOpen)
45 throw new IOException("Stream closed");
46 }
47
48 // Factories for java.io.OutputStreamWriter
49 public static StreamEncoder forOutputStreamWriter(OutputStream out,
50 Object lock,
51 String charsetName)
52 throws UnsupportedEncodingException
53 {
54 String csn = charsetName;
55 if (csn == null)
56 csn = Charset.defaultCharset().name();
57 try {
58 if (Charset.isSupported(csn))
59 return new StreamEncoder(out, lock, Charset.forName(csn));
60 } catch (IllegalCharsetNameException x) { }
61 throw new UnsupportedEncodingException (csn);
62 }
63
64 public static StreamEncoder forOutputStreamWriter(OutputStream out,
65 Object lock,
66 Charset cs)
67 {
68 return new StreamEncoder(out, lock, cs);
69 }
70
71 public static StreamEncoder forOutputStreamWriter(OutputStream out,
72 Object lock,
73 CharsetEncoder enc)
74 {
75 return new StreamEncoder(out, lock, enc);
76 }
77
78
79 // Factory for java.nio.channels.Channels.newWriter
80
81 public static StreamEncoder forEncoder(WritableByteChannel ch,
82 CharsetEncoder enc,
83 int minBufferCap)
84 {
85 return new StreamEncoder(ch, enc, minBufferCap);
86 }
87
88
89 // -- Public methods corresponding to those in OutputStreamWriter --
90
91 // All synchronization and state/argument checking is done in these public
92 // methods; the concrete stream-encoder subclasses defined below need not
93 // do any such checking.
94
95 public String getEncoding() {
96 if (isOpen())
97 return encodingName();
98 return null;
99 }
100
101 public void flushBuffer() throws IOException {
102 synchronized (lock) {
103 if (isOpen())
104 implFlushBuffer();
105 else
106 throw new IOException("Stream closed");
107 }
108 }
109
110 public void write(int c) throws IOException {
111 char cbuf[] = new char[1];
112 cbuf[0] = (char) c;
113 write(cbuf, 0, 1);
114 }
115
116 public void write(char cbuf[], int off, int len) throws IOException {
117 synchronized (lock) {
118 ensureOpen();
119 if ((off < 0) || (off > cbuf.length) || (len < 0) ||
120 ((off + len) > cbuf.length) || ((off + len) < 0)) {
121 throw new IndexOutOfBoundsException();
122 } else if (len == 0) {
123 return;
124 }
125 implWrite(cbuf, off, len);
126 }
127 }
128
129 public void write(String str, int off, int len) throws IOException {
130 /* Check the len before creating a char buffer */
131 if (len < 0)
132 throw new IndexOutOfBoundsException();
133 char cbuf[] = new char[len];
134 str.getChars(off, off + len, cbuf, 0);
135 write(cbuf, 0, len);
136 }
137
138 public void flush() throws IOException {
139 synchronized (lock) {
140 ensureOpen();
141 implFlush();
142 }
143 }
144
145 public void close() throws IOException {
146 synchronized (lock) {
147 if (!isOpen)
148 return;
149 implClose();
150 isOpen = false;
151 }
152 }
153
154 private boolean isOpen() {
155 return isOpen;
156 }
157
158
159 // -- Charset-based stream encoder impl --
160
161 private Charset cs;
162 private CharsetEncoder encoder;
163 private ByteBuffer bb;
164
165 // Exactly one of these is non-null
166 private final OutputStream out;
167 private WritableByteChannel ch;
168
169 // Leftover first char in a surrogate pair
170 private boolean haveLeftoverChar = false;
171 private char leftoverChar;
172 private CharBuffer lcb = null;
173
174 private StreamEncoder(OutputStream out, Object lock, Charset cs) {
175 this(out, lock,
176 cs.newEncoder()
177 .onMalformedInput(CodingErrorAction.REPLACE)
178 .onUnmappableCharacter(CodingErrorAction.REPLACE));
179 }
180
181 private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {
182 super(lock);
183 this.out = out;
184 this.ch = null;
185 this.cs = enc.charset();
186 this.encoder = enc;
187
188 // This path disabled until direct buffers are faster
189 if (false && out instanceof FileOutputStream) {
190 ch = ((FileOutputStream)out).getChannel();
191 if (ch != null)
192 bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
193 }
194 if (ch == null) {
195 bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
196 }
197 }
198
199 private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {
200 this.out = null;
201 this.ch = ch;
202 this.cs = enc.charset();
203 this.encoder = enc;
204 this.bb = ByteBuffer.allocate(mbc < 0
205 ? DEFAULT_BYTE_BUFFER_SIZE
206 : mbc);
207 }
208
209 private void writeBytes() throws IOException {
210 bb.flip();
211 int lim = bb.limit();
212 int pos = bb.position();
213 assert (pos <= lim);
214 int rem = (pos <= lim ? lim - pos : 0);
215
216 if (rem > 0) {
217 if (ch != null) {
218 if (ch.write(bb) != rem)
219 assert false : rem;
220 } else {
221 out.write(bb.array(), bb.arrayOffset() + pos, rem);
222 }
223 }
224 bb.clear();
225 }
226
227 private void flushLeftoverChar(CharBuffer cb, boolean endOfInput)
228 throws IOException
229 {
230 if (!haveLeftoverChar && !endOfInput)
231 return;
232 if (lcb == null)
233 lcb = CharBuffer.allocate(2);
234 else
235 lcb.clear();
236 if (haveLeftoverChar)
237 lcb.put(leftoverChar);
238 if ((cb != null) && cb.hasRemaining())
239 lcb.put(cb.get());
240 lcb.flip();
241 while (lcb.hasRemaining() || endOfInput) {
242 CoderResult cr = encoder.encode(lcb, bb, endOfInput);
243 if (cr.isUnderflow()) {
244 if (lcb.hasRemaining()) {
245 leftoverChar = lcb.get();
246 if (cb != null && cb.hasRemaining())
247 flushLeftoverChar(cb, endOfInput);
248 return;
249 }
250 break;
251 }
252 if (cr.isOverflow()) {
253 assert bb.position() > 0;
254 writeBytes();
255 continue;
256 }
257 cr.throwException();
258 }
259 haveLeftoverChar = false;
260 }
261
262 void implWrite(char cbuf[], int off, int len)
263 throws IOException
264 {
265 CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
266
267 if (haveLeftoverChar)
268 flushLeftoverChar(cb, false);
269
270 while (cb.hasRemaining()) {
271 CoderResult cr = encoder.encode(cb, bb, false);
272 if (cr.isUnderflow()) {
273 assert (cb.remaining() <= 1) : cb.remaining();
274 if (cb.remaining() == 1) {
275 haveLeftoverChar = true;
276 leftoverChar = cb.get();
277 }
278 break;
279 }
280 if (cr.isOverflow()) {
281 assert bb.position() > 0;
282 writeBytes();
283 continue;
284 }
285 cr.throwException();
286 }
287 }
288
289 void implFlushBuffer() throws IOException {
290 if (bb.position() > 0)
291 writeBytes();
292 }
293
294 void implFlush() throws IOException {
295 implFlushBuffer();
296 if (out != null)
297 out.flush();
298 }
299
300 void implClose() throws IOException {
301 flushLeftoverChar(null, true);
302 try {
303 for (;;) {
304 CoderResult cr = encoder.flush(bb);
305 if (cr.isUnderflow())
306 break;
307 if (cr.isOverflow()) {
308 assert bb.position() > 0;
309 writeBytes();
310 continue;
311 }
312 cr.throwException();
313 }
314
315 if (bb.position() > 0)
316 writeBytes();
317 if (ch != null)
318 ch.close();
319 else
320 out.close();
321 } catch (IOException x) {
322 encoder.reset();
323 throw x;
324 }
325 }
326
327 String encodingName() {
328 return ((cs instanceof HistoricallyNamedCharset)
329 ? ((HistoricallyNamedCharset)cs).historicalName()
330 : cs.name());
331 }
332 } |