001 /*
002 * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.nio.channels;
027
028 import java.io.FileInputStream;
029 import java.io.FileOutputStream;
030 import java.io.InputStream;
031 import java.io.OutputStream;
032 import java.io.Reader;
033 import java.io.Writer;
034 import java.io.IOException;
035 import java.nio.ByteBuffer;
036 import java.nio.CharBuffer;
037 import java.nio.BufferOverflowException;
038 import java.nio.BufferUnderflowException;
039 import java.nio.charset.Charset;
040 import java.nio.charset.CharsetDecoder;
041 import java.nio.charset.CharsetEncoder;
042 import java.nio.charset.CoderResult;
043 import java.nio.charset.UnsupportedCharsetException;
044 import java.nio.channels.spi.AbstractInterruptibleChannel;
045 import sun.nio.ch.ChannelInputStream;
046 import sun.nio.cs.StreamDecoder;
047 import sun.nio.cs.StreamEncoder;
048
049 /**
050 * Utility methods for channels and streams.
051 *
052 * <p> This class defines static methods that support the interoperation of the
053 * stream classes of the <tt>{@link java.io}</tt> package with the channel
054 * classes of this package. </p>
055 *
056 *
057 * @author Mark Reinhold
058 * @author Mike McCloskey
059 * @author JSR-51 Expert Group
060 * @version 1.31, 07/05/05
061 * @since 1.4
062 */
063
064 public final class Channels {
065
066 private Channels() {
067 } // No instantiation
068
069 private static int write(WritableByteChannel ch, ByteBuffer bb)
070 throws IOException {
071 if (ch instanceof SelectableChannel) {
072 SelectableChannel sc = (SelectableChannel) ch;
073 synchronized (sc.blockingLock()) {
074 if (!sc.isBlocking())
075 throw new IllegalBlockingModeException();
076 return ch.write(bb);
077 }
078 } else {
079 return ch.write(bb);
080 }
081 }
082
083 // -- Byte streams from channels --
084
085 /**
086 * Constructs a stream that reads bytes from the given channel.
087 *
088 * <p> The <tt>read</tt> methods of the resulting stream will throw an
089 * {@link IllegalBlockingModeException} if invoked while the underlying
090 * channel is in non-blocking mode. The stream will not be buffered, and
091 * it will not support the {@link InputStream#mark mark} or {@link
092 * InputStream#reset reset} methods. The stream will be safe for access by
093 * multiple concurrent threads. Closing the stream will in turn cause the
094 * channel to be closed. </p>
095 *
096 * @param ch
097 * The channel from which bytes will be read
098 *
099 * @return A new input stream
100 */
101 public static InputStream newInputStream(ReadableByteChannel ch) {
102 return new sun.nio.ch.ChannelInputStream(ch);
103 }
104
105 /**
106 * Constructs a stream that writes bytes to the given channel.
107 *
108 * <p> The <tt>write</tt> methods of the resulting stream will throw an
109 * {@link IllegalBlockingModeException} if invoked while the underlying
110 * channel is in non-blocking mode. The stream will not be buffered. The
111 * stream will be safe for access by multiple concurrent threads. Closing
112 * the stream will in turn cause the channel to be closed. </p>
113 *
114 * @param ch
115 * The channel to which bytes will be written
116 *
117 * @return A new output stream
118 */
119 public static OutputStream newOutputStream(
120 final WritableByteChannel ch) {
121 return new OutputStream() {
122
123 private ByteBuffer bb = null;
124 private byte[] bs = null; // Invoker's previous array
125 private byte[] b1 = null;
126
127 public synchronized void write(int b) throws IOException {
128 if (b1 == null)
129 b1 = new byte[1];
130 b1[0] = (byte) b;
131 this .write(b1);
132 }
133
134 public synchronized void write(byte[] bs, int off, int len)
135 throws IOException {
136 if ((off < 0) || (off > bs.length) || (len < 0)
137 || ((off + len) > bs.length)
138 || ((off + len) < 0)) {
139 throw new IndexOutOfBoundsException();
140 } else if (len == 0) {
141 return;
142 }
143 ByteBuffer bb = ((this .bs == bs) ? this .bb : ByteBuffer
144 .wrap(bs));
145 bb.limit(Math.min(off + len, bb.capacity()));
146 bb.position(off);
147 this .bb = bb;
148 this .bs = bs;
149 Channels.write(ch, bb);
150 }
151
152 public void close() throws IOException {
153 ch.close();
154 }
155
156 };
157 }
158
159 // -- Channels from streams --
160
161 /**
162 * Constructs a channel that reads bytes from the given stream.
163 *
164 * <p> The resulting channel will not be buffered; it will simply redirect
165 * its I/O operations to the given stream. Closing the channel will in
166 * turn cause the stream to be closed. </p>
167 *
168 * @param in
169 * The stream from which bytes are to be read
170 *
171 * @return A new readable byte channel
172 */
173 public static ReadableByteChannel newChannel(final InputStream in) {
174 if (in == null) {
175 throw new NullPointerException();
176 }
177
178 if (in instanceof FileInputStream
179 && FileInputStream.class.equals(in.getClass())) {
180 return ((FileInputStream) in).getChannel();
181 }
182
183 return new ReadableByteChannelImpl(in);
184 }
185
186 private static class ReadableByteChannelImpl extends
187 AbstractInterruptibleChannel // Not really interruptible
188 implements ReadableByteChannel {
189 InputStream in;
190 private static final int TRANSFER_SIZE = 8192;
191 private byte buf[] = new byte[0];
192 private boolean open = true;
193 private Object readLock = new Object();
194
195 ReadableByteChannelImpl(InputStream in) {
196 this .in = in;
197 }
198
199 public int read(ByteBuffer dst) throws IOException {
200 int len = dst.remaining();
201 int totalRead = 0;
202 int bytesRead = 0;
203 synchronized (readLock) {
204 while (totalRead < len) {
205 int bytesToRead = Math.min((len - totalRead),
206 TRANSFER_SIZE);
207 if (buf.length < bytesToRead)
208 buf = new byte[bytesToRead];
209 if ((totalRead > 0) && !(in.available() > 0))
210 break; // block at most once
211 try {
212 begin();
213 bytesRead = in.read(buf, 0, bytesToRead);
214 } finally {
215 end(bytesRead > 0);
216 }
217 if (bytesRead < 0)
218 break;
219 else
220 totalRead += bytesRead;
221 dst.put(buf, 0, bytesRead);
222 }
223 if ((bytesRead < 0) && (totalRead == 0))
224 return -1;
225
226 return totalRead;
227 }
228 }
229
230 protected void implCloseChannel() throws IOException {
231 in.close();
232 open = false;
233 }
234 }
235
236 /**
237 * Constructs a channel that writes bytes to the given stream.
238 *
239 * <p> The resulting channel will not be buffered; it will simply redirect
240 * its I/O operations to the given stream. Closing the channel will in
241 * turn cause the stream to be closed. </p>
242 *
243 * @param out
244 * The stream to which bytes are to be written
245 *
246 * @return A new writable byte channel
247 */
248 public static WritableByteChannel newChannel(final OutputStream out) {
249 if (out == null) {
250 throw new NullPointerException();
251 }
252
253 if (out instanceof FileOutputStream
254 && FileOutputStream.class.equals(out.getClass())) {
255 return ((FileOutputStream) out).getChannel();
256 }
257
258 return new WritableByteChannelImpl(out);
259 }
260
261 private static class WritableByteChannelImpl extends
262 AbstractInterruptibleChannel // Not really interruptible
263 implements WritableByteChannel {
264 OutputStream out;
265 private static final int TRANSFER_SIZE = 8192;
266 private byte buf[] = new byte[0];
267 private boolean open = true;
268 private Object writeLock = new Object();
269
270 WritableByteChannelImpl(OutputStream out) {
271 this .out = out;
272 }
273
274 public int write(ByteBuffer src) throws IOException {
275 int len = src.remaining();
276 int totalWritten = 0;
277 synchronized (writeLock) {
278 while (totalWritten < len) {
279 int bytesToWrite = Math.min((len - totalWritten),
280 TRANSFER_SIZE);
281 if (buf.length < bytesToWrite)
282 buf = new byte[bytesToWrite];
283 src.get(buf, 0, bytesToWrite);
284 try {
285 begin();
286 out.write(buf, 0, bytesToWrite);
287 } finally {
288 end(bytesToWrite > 0);
289 }
290 totalWritten += bytesToWrite;
291 }
292 return totalWritten;
293 }
294 }
295
296 protected void implCloseChannel() throws IOException {
297 out.close();
298 open = false;
299 }
300 }
301
302 // -- Character streams from channels --
303
304 /**
305 * Constructs a reader that decodes bytes from the given channel using the
306 * given decoder.
307 *
308 * <p> The resulting stream will contain an internal input buffer of at
309 * least <tt>minBufferCap</tt> bytes. The stream's <tt>read</tt> methods
310 * will, as needed, fill the buffer by reading bytes from the underlying
311 * channel; if the channel is in non-blocking mode when bytes are to be
312 * read then an {@link IllegalBlockingModeException} will be thrown. The
313 * resulting stream will not otherwise be buffered, and it will not support
314 * the {@link Reader#mark mark} or {@link Reader#reset reset} methods.
315 * Closing the stream will in turn cause the channel to be closed. </p>
316 *
317 * @param ch
318 * The channel from which bytes will be read
319 *
320 * @param dec
321 * The charset decoder to be used
322 *
323 * @param minBufferCap
324 * The minimum capacity of the internal byte buffer,
325 * or <tt>-1</tt> if an implementation-dependent
326 * default capacity is to be used
327 *
328 * @return A new reader
329 */
330 public static Reader newReader(ReadableByteChannel ch,
331 CharsetDecoder dec, int minBufferCap) {
332 dec.reset();
333 return StreamDecoder.forDecoder(ch, dec, minBufferCap);
334 }
335
336 /**
337 * Constructs a reader that decodes bytes from the given channel according
338 * to the named charset.
339 *
340 * <p> An invocation of this method of the form
341 *
342 * <blockquote><pre>
343 * Channels.newReader(ch, csname)</pre></blockquote>
344 *
345 * behaves in exactly the same way as the expression
346 *
347 * <blockquote><pre>
348 * Channels.newReader(ch,
349 * Charset.forName(csName)
350 * .newDecoder(),
351 * -1);</pre></blockquote>
352 *
353 * @param ch
354 * The channel from which bytes will be read
355 *
356 * @param csName
357 * The name of the charset to be used
358 *
359 * @return A new reader
360 *
361 * @throws UnsupportedCharsetException
362 * If no support for the named charset is available
363 * in this instance of the Java virtual machine
364 */
365 public static Reader newReader(ReadableByteChannel ch, String csName) {
366 return newReader(ch, Charset.forName(csName).newDecoder(), -1);
367 }
368
369 /**
370 * Constructs a writer that encodes characters using the given encoder and
371 * writes the resulting bytes to the given channel.
372 *
373 * <p> The resulting stream will contain an internal output buffer of at
374 * least <tt>minBufferCap</tt> bytes. The stream's <tt>write</tt> methods
375 * will, as needed, flush the buffer by writing bytes to the underlying
376 * channel; if the channel is in non-blocking mode when bytes are to be
377 * written then an {@link IllegalBlockingModeException} will be thrown.
378 * The resulting stream will not otherwise be buffered. Closing the stream
379 * will in turn cause the channel to be closed. </p>
380 *
381 * @param ch
382 * The channel to which bytes will be written
383 *
384 * @param enc
385 * The charset encoder to be used
386 *
387 * @param minBufferCap
388 * The minimum capacity of the internal byte buffer,
389 * or <tt>-1</tt> if an implementation-dependent
390 * default capacity is to be used
391 *
392 * @return A new writer
393 */
394 public static Writer newWriter(final WritableByteChannel ch,
395 final CharsetEncoder enc, final int minBufferCap) {
396 enc.reset();
397 return StreamEncoder.forEncoder(ch, enc, minBufferCap);
398 }
399
400 /**
401 * Constructs a writer that encodes characters according to the named
402 * charset and writes the resulting bytes to the given channel.
403 *
404 * <p> An invocation of this method of the form
405 *
406 * <blockquote><pre>
407 * Channels.newWriter(ch, csname)</pre></blockquote>
408 *
409 * behaves in exactly the same way as the expression
410 *
411 * <blockquote><pre>
412 * Channels.newWriter(ch,
413 * Charset.forName(csName)
414 * .newEncoder(),
415 * -1);</pre></blockquote>
416 *
417 * @param ch
418 * The channel to which bytes will be written
419 *
420 * @param csName
421 * The name of the charset to be used
422 *
423 * @return A new writer
424 *
425 * @throws UnsupportedCharsetException
426 * If no support for the named charset is available
427 * in this instance of the Java virtual machine
428 */
429 public static Writer newWriter(WritableByteChannel ch, String csName) {
430 return newWriter(ch, Charset.forName(csName).newEncoder(), -1);
431 }
432
433 }
|