1 /*
2 * Copyright 2000-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 package java.nio.channels;
27
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.Reader;
33 import java.io.Writer;
34 import java.io.IOException;
35 import java.nio.ByteBuffer;
36 import java.nio.CharBuffer;
37 import java.nio.BufferOverflowException;
38 import java.nio.BufferUnderflowException;
39 import java.nio.charset.Charset;
40 import java.nio.charset.CharsetDecoder;
41 import java.nio.charset.CharsetEncoder;
42 import java.nio.charset.CoderResult;
43 import java.nio.charset.UnsupportedCharsetException;
44 import java.nio.channels.spi.AbstractInterruptibleChannel;
45 import sun.nio.ch.ChannelInputStream;
46 import sun.nio.cs.StreamDecoder;
47 import sun.nio.cs.StreamEncoder;
48
49
50 /**
51 * Utility methods for channels and streams.
52 *
53 * <p> This class defines static methods that support the interoperation of the
54 * stream classes of the <tt>{@link java.io}</tt> package with the channel
55 * classes of this package. </p>
56 *
57 *
58 * @author Mark Reinhold
59 * @author Mike McCloskey
60 * @author JSR-51 Expert Group
61 * @since 1.4
62 */
63
64 public final class Channels {
65
66 private Channels() { } // No instantiation
67
68
69 /**
70 * Write all remaining bytes in buffer to the given channel.
71 * If the channel is selectable then it must be configured blocking.
72 */
73 private static void writeFullyImpl(WritableByteChannel ch, ByteBuffer bb)
74 throws IOException
75 {
76 while (bb.remaining() > 0) {
77 int n = ch.write(bb);
78 if (n <= 0)
79 throw new RuntimeException("no bytes written");
80 }
81 }
82
83 /**
84 * Write all remaining bytes in buffer to the given channel.
85 *
86 * @throws IllegalBlockingException
87 * If the channel is selectable and configured non-blocking.
88 */
89 private static void writeFully(WritableByteChannel ch, ByteBuffer bb)
90 throws IOException
91 {
92 if (ch instanceof SelectableChannel) {
93 SelectableChannel sc = (SelectableChannel)ch;
94 synchronized (sc.blockingLock()) {
95 if (!sc.isBlocking())
96 throw new IllegalBlockingModeException();
97 writeFullyImpl(ch, bb);
98 }
99 } else {
100 writeFullyImpl(ch, bb);
101 }
102 }
103
104 // -- Byte streams from channels --
105
106 /**
107 * Constructs a stream that reads bytes from the given channel.
108 *
109 * <p> The <tt>read</tt> methods of the resulting stream will throw an
110 * {@link IllegalBlockingModeException} if invoked while the underlying
111 * channel is in non-blocking mode. The stream will not be buffered, and
112 * it will not support the {@link InputStream#mark mark} or {@link
113 * InputStream#reset reset} methods. The stream will be safe for access by
114 * multiple concurrent threads. Closing the stream will in turn cause the
115 * channel to be closed. </p>
116 *
117 * @param ch
118 * The channel from which bytes will be read
119 *
120 * @return A new input stream
121 */
122 public static InputStream newInputStream(ReadableByteChannel ch) {
123 return new sun.nio.ch.ChannelInputStream(ch);
124 }
125
126 /**
127 * Constructs a stream that writes bytes to the given channel.
128 *
129 * <p> The <tt>write</tt> methods of the resulting stream will throw an
130 * {@link IllegalBlockingModeException} if invoked while the underlying
131 * channel is in non-blocking mode. The stream will not be buffered. The
132 * stream will be safe for access by multiple concurrent threads. Closing
133 * the stream will in turn cause the channel to be closed. </p>
134 *
135 * @param ch
136 * The channel to which bytes will be written
137 *
138 * @return A new output stream
139 */
140 public static OutputStream newOutputStream(final WritableByteChannel ch) {
141 return new OutputStream() {
142
143 private ByteBuffer bb = null;
144 private byte[] bs = null; // Invoker's previous array
145 private byte[] b1 = null;
146
147 public synchronized void write(int b) throws IOException {
148 if (b1 == null)
149 b1 = new byte[1];
150 b1[0] = (byte)b;
151 this.write(b1);
152 }
153
154 public synchronized void write(byte[] bs, int off, int len)
155 throws IOException
156 {
157 if ((off < 0) || (off > bs.length) || (len < 0) ||
158 ((off + len) > bs.length) || ((off + len) < 0)) {
159 throw new IndexOutOfBoundsException();
160 } else if (len == 0) {
161 return;
162 }
163 ByteBuffer bb = ((this.bs == bs)
164 ? this.bb
165 : ByteBuffer.wrap(bs));
166 bb.limit(Math.min(off + len, bb.capacity()));
167 bb.position(off);
168 this.bb = bb;
169 this.bs = bs;
170 Channels.writeFully(ch, bb);
171 }
172
173 public void close() throws IOException {
174 ch.close();
175 }
176
177 };
178 }
179
180
181 // -- Channels from streams --
182
183 /**
184 * Constructs a channel that reads bytes from the given stream.
185 *
186 * <p> The resulting channel will not be buffered; it will simply redirect
187 * its I/O operations to the given stream. Closing the channel will in
188 * turn cause the stream to be closed. </p>
189 *
190 * @param in
191 * The stream from which bytes are to be read
192 *
193 * @return A new readable byte channel
194 */
195 public static ReadableByteChannel newChannel(final InputStream in) {
196 if (in == null) {
197 throw new NullPointerException();
198 }
199
200 if (in instanceof FileInputStream &&
201 FileInputStream.class.equals(in.getClass())) {
202 return ((FileInputStream)in).getChannel();
203 }
204
205 return new ReadableByteChannelImpl(in);
206 }
207
208 private static class ReadableByteChannelImpl
209 extends AbstractInterruptibleChannel // Not really interruptible
210 implements ReadableByteChannel
211 {
212 InputStream in;
213 private static final int TRANSFER_SIZE = 8192;
214 private byte buf[] = new byte[0];
215 private boolean open = true;
216 private Object readLock = new Object();
217
218 ReadableByteChannelImpl(InputStream in) {
219 this.in = in;
220 }
221
222 public int read(ByteBuffer dst) throws IOException {
223 int len = dst.remaining();
224 int totalRead = 0;
225 int bytesRead = 0;
226 synchronized (readLock) {
227 while (totalRead < len) {
228 int bytesToRead = Math.min((len - totalRead),
229 TRANSFER_SIZE);
230 if (buf.length < bytesToRead)
231 buf = new byte[bytesToRead];
232 if ((totalRead > 0) && !(in.available() > 0))
233 break; // block at most once
234 try {
235 begin();
236 bytesRead = in.read(buf, 0, bytesToRead);
237 } finally {
238 end(bytesRead > 0);
239 }
240 if (bytesRead < 0)
241 break;
242 else
243 totalRead += bytesRead;
244 dst.put(buf, 0, bytesRead);
245 }
246 if ((bytesRead < 0) && (totalRead == 0))
247 return -1;
248
249 return totalRead;
250 }
251 }
252
253 protected void implCloseChannel() throws IOException {
254 in.close();
255 open = false;
256 }
257 }
258
259
260 /**
261 * Constructs a channel that writes bytes to the given stream.
262 *
263 * <p> The resulting channel will not be buffered; it will simply redirect
264 * its I/O operations to the given stream. Closing the channel will in
265 * turn cause the stream to be closed. </p>
266 *
267 * @param out
268 * The stream to which bytes are to be written
269 *
270 * @return A new writable byte channel
271 */
272 public static WritableByteChannel newChannel(final OutputStream out) {
273 if (out == null) {
274 throw new NullPointerException();
275 }
276
277 if (out instanceof FileOutputStream &&
278 FileOutputStream.class.equals(out.getClass())) {
279 return ((FileOutputStream)out).getChannel();
280 }
281
282 return new WritableByteChannelImpl(out);
283 }
284
285 private static class WritableByteChannelImpl
286 extends AbstractInterruptibleChannel // Not really interruptible
287 implements WritableByteChannel
288 {
289 OutputStream out;
290 private static final int TRANSFER_SIZE = 8192;
291 private byte buf[] = new byte[0];
292 private boolean open = true;
293 private Object writeLock = new Object();
294
295 WritableByteChannelImpl(OutputStream out) {
296 this.out = out;
297 }
298
299 public int write(ByteBuffer src) throws IOException {
300 int len = src.remaining();
301 int totalWritten = 0;
302 synchronized (writeLock) {
303 while (totalWritten < len) {
304 int bytesToWrite = Math.min((len - totalWritten),
305 TRANSFER_SIZE);
306 if (buf.length < bytesToWrite)
307 buf = new byte[bytesToWrite];
308 src.get(buf, 0, bytesToWrite);
309 try {
310 begin();
311 out.write(buf, 0, bytesToWrite);
312 } finally {
313 end(bytesToWrite > 0);
314 }
315 totalWritten += bytesToWrite;
316 }
317 return totalWritten;
318 }
319 }
320
321 protected void implCloseChannel() throws IOException {
322 out.close();
323 open = false;
324 }
325 }
326
327
328 // -- Character streams from channels --
329
330 /**
331 * Constructs a reader that decodes bytes from the given channel using the
332 * given decoder.
333 *
334 * <p> The resulting stream will contain an internal input buffer of at
335 * least <tt>minBufferCap</tt> bytes. The stream's <tt>read</tt> methods
336 * will, as needed, fill the buffer by reading bytes from the underlying
337 * channel; if the channel is in non-blocking mode when bytes are to be
338 * read then an {@link IllegalBlockingModeException} will be thrown. The
339 * resulting stream will not otherwise be buffered, and it will not support
340 * the {@link Reader#mark mark} or {@link Reader#reset reset} methods.
341 * Closing the stream will in turn cause the channel to be closed. </p>
342 *
343 * @param ch
344 * The channel from which bytes will be read
345 *
346 * @param dec
347 * The charset decoder to be used
348 *
349 * @param minBufferCap
350 * The minimum capacity of the internal byte buffer,
351 * or <tt>-1</tt> if an implementation-dependent
352 * default capacity is to be used
353 *
354 * @return A new reader
355 */
356 public static Reader newReader(ReadableByteChannel ch,
357 CharsetDecoder dec,
358 int minBufferCap)
359 {
360 dec.reset();
361 return StreamDecoder.forDecoder(ch, dec, minBufferCap);
362 }
363
364 /**
365 * Constructs a reader that decodes bytes from the given channel according
366 * to the named charset.
367 *
368 * <p> An invocation of this method of the form
369 *
370 * <blockquote><pre>
371 * Channels.newReader(ch, csname)</pre></blockquote>
372 *
373 * behaves in exactly the same way as the expression
374 *
375 * <blockquote><pre>
376 * Channels.newReader(ch,
377 * Charset.forName(csName)
378 * .newDecoder(),
379 * -1);</pre></blockquote>
380 *
381 * @param ch
382 * The channel from which bytes will be read
383 *
384 * @param csName
385 * The name of the charset to be used
386 *
387 * @return A new reader
388 *
389 * @throws UnsupportedCharsetException
390 * If no support for the named charset is available
391 * in this instance of the Java virtual machine
392 */
393 public static Reader newReader(ReadableByteChannel ch,
394 String csName)
395 {
396 return newReader(ch, Charset.forName(csName).newDecoder(), -1);
397 }
398
399 /**
400 * Constructs a writer that encodes characters using the given encoder and
401 * writes the resulting bytes to the given channel.
402 *
403 * <p> The resulting stream will contain an internal output buffer of at
404 * least <tt>minBufferCap</tt> bytes. The stream's <tt>write</tt> methods
405 * will, as needed, flush the buffer by writing bytes to the underlying
406 * channel; if the channel is in non-blocking mode when bytes are to be
407 * written then an {@link IllegalBlockingModeException} will be thrown.
408 * The resulting stream will not otherwise be buffered. Closing the stream
409 * will in turn cause the channel to be closed. </p>
410 *
411 * @param ch
412 * The channel to which bytes will be written
413 *
414 * @param enc
415 * The charset encoder to be used
416 *
417 * @param minBufferCap
418 * The minimum capacity of the internal byte buffer,
419 * or <tt>-1</tt> if an implementation-dependent
420 * default capacity is to be used
421 *
422 * @return A new writer
423 */
424 public static Writer newWriter(final WritableByteChannel ch,
425 final CharsetEncoder enc,
426 final int minBufferCap)
427 {
428 enc.reset();
429 return StreamEncoder.forEncoder(ch, enc, minBufferCap);
430 }
431
432 /**
433 * Constructs a writer that encodes characters according to the named
434 * charset and writes the resulting bytes to the given channel.
435 *
436 * <p> An invocation of this method of the form
437 *
438 * <blockquote><pre>
439 * Channels.newWriter(ch, csname)</pre></blockquote>
440 *
441 * behaves in exactly the same way as the expression
442 *
443 * <blockquote><pre>
444 * Channels.newWriter(ch,
445 * Charset.forName(csName)
446 * .newEncoder(),
447 * -1);</pre></blockquote>
448 *
449 * @param ch
450 * The channel to which bytes will be written
451 *
452 * @param csName
453 * The name of the charset to be used
454 *
455 * @return A new writer
456 *
457 * @throws UnsupportedCharsetException
458 * If no support for the named charset is available
459 * in this instance of the Java virtual machine
460 */
461 public static Writer newWriter(WritableByteChannel ch,
462 String csName)
463 {
464 return newWriter(ch, Charset.forName(csName).newEncoder(), -1);
465 }
466
467 }