| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.nio.channels; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.nio.ByteBuffer; |
| import java.nio.CharBuffer; |
| import java.nio.channels.spi.AbstractInterruptibleChannel; |
| import java.nio.charset.Charset; |
| import java.nio.charset.CharsetDecoder; |
| import java.nio.charset.CharsetEncoder; |
| import org.apache.harmony.nio.internal.IOUtil; |
| |
| /** |
| * This class provides several utilities to get I/O streams from channels. |
| */ |
| public final class Channels { |
| |
| /* |
| * Not intended to be instantiated. |
| */ |
| private Channels() { |
| super(); |
| } |
| |
| /** |
| * Returns an input stream on the given channel. The resulting stream has |
| * the following properties: |
| * <ul> |
| * <li>If the stream is closed, then the underlying channel is closed as |
| * well.</li> |
| * <li>It is thread safe.</li> |
| * <li>It throws an {@link IllegalBlockingModeException} if the channel is |
| * in non-blocking mode and {@code read} is called.</li> |
| * <li>Neither {@code mark} nor {@code reset} is supported.</li> |
| * <li>It is not buffered.</li> |
| * </ul> |
| * |
| * @param channel |
| * the channel to be wrapped by an InputStream. |
| * @return an InputStream that takes bytes from the given byte channel. |
| */ |
| public static InputStream newInputStream(ReadableByteChannel channel) { |
| return new ReadableByteChannelInputStream(channel); |
| } |
| |
| /** |
| * Returns an output stream on the given channel. The resulting stream has |
| * the following properties: |
| * <ul> |
| * <li>If the stream is closed, then the underlying channel is closed as |
| * well.</li> |
| * <li>It is thread safe.</li> |
| * <li>It throws an {@link IllegalBlockingModeException} if the channel is |
| * in non-blocking mode and {@code write} is called.</li> |
| * <li>It is not buffered.</li> |
| * </ul> |
| * |
| * @param channel |
| * the channel to be wrapped by an OutputStream. |
| * @return an OutputStream that puts bytes onto the given byte channel. |
| */ |
| public static OutputStream newOutputStream(WritableByteChannel channel) { |
| return new WritableByteChannelOutputStream(channel); |
| } |
| |
| /** |
| * Returns a readable channel on the given input stream. The resulting |
| * channel has the following properties: |
| * <ul> |
| * <li>If the channel is closed, then the underlying stream is closed as |
| * well.</li> |
| * <li>It is not buffered.</li> |
| * </ul> |
| * |
| * @param inputStream |
| * the stream to be wrapped by a byte channel. |
| * @return a byte channel that reads bytes from the input stream. |
| */ |
| public static ReadableByteChannel newChannel(InputStream inputStream) { |
| return new ReadableByteChannelImpl(inputStream); |
| } |
| |
| /** |
| * Returns a writable channel on the given output stream. |
| * |
| * The resulting channel has following properties: |
| * <ul> |
| * <li>If the channel is closed, then the underlying stream is closed as |
| * well.</li> |
| * <li>It is not buffered.</li> |
| * </ul> |
| * |
| * @param outputStream |
| * the stream to be wrapped by a byte channel. |
| * @return a byte channel that writes bytes to the output stream. |
| */ |
| public static WritableByteChannel newChannel(OutputStream outputStream) { |
| return new WritableByteChannelImpl(outputStream); |
| } |
| |
| /** |
| * Returns a reader that decodes bytes from a channel. |
| * |
| * @param channel |
| * the Channel to be read. |
| * @param decoder |
| * the Charset decoder to be used. |
| * @param minBufferCapacity |
| * The minimum size of the byte buffer, -1 means to use the |
| * default size. |
| * @return the reader. |
| */ |
| public static Reader newReader(ReadableByteChannel channel, |
| CharsetDecoder decoder, int minBufferCapacity) { |
| return new ByteChannelReader(new ReaderInputStream(channel), decoder, |
| minBufferCapacity); |
| } |
| |
| /** |
| * Returns a reader that decodes bytes from a channel. This method creates a |
| * reader with a buffer of default size. |
| * |
| * @param channel |
| * the Channel to be read. |
| * @param charsetName |
| * the name of the charset. |
| * @return the reader. |
| * @throws java.nio.charset.UnsupportedCharsetException |
| * if the given charset name is not supported. |
| */ |
| public static Reader newReader(ReadableByteChannel channel, |
| String charsetName) { |
| return newReader(channel, Charset.forName(charsetName).newDecoder(), -1); |
| } |
| |
| /** |
| * Returns a writer that encodes characters with the specified |
| * {@code encoder} and sends the bytes to the specified channel. |
| * |
| * @param channel |
| * the Channel to write to. |
| * @param encoder |
| * the CharsetEncoder to be used. |
| * @param minBufferCapacity |
| * the minimum size of the byte buffer, -1 means to use the |
| * default size. |
| * @return the writer. |
| */ |
| public static Writer newWriter(WritableByteChannel channel, |
| CharsetEncoder encoder, int minBufferCapacity) { |
| return new ByteChannelWriter(new WritableByteChannelOutputStream( |
| channel), encoder, minBufferCapacity); |
| } |
| |
| /** |
| * Returns a writer that encodes characters with the specified |
| * {@code encoder} and sends the bytes to the specified channel. This method |
| * creates a writer with a buffer of default size. |
| * |
| * @param channel |
| * the Channel to be written to. |
| * @param charsetName |
| * the name of the charset. |
| * @return the writer. |
| * @throws java.nio.charset.UnsupportedCharsetException |
| * if the given charset name is not supported. |
| */ |
| public static Writer newWriter(WritableByteChannel channel, |
| String charsetName) { |
| return newWriter(channel, Charset.forName(charsetName).newEncoder(), -1); |
| } |
| |
| /* |
| * wrap a byte array to a ByteBuffer |
| */ |
| static ByteBuffer wrapByteBuffer(byte[] bytes, int offset, int length) { |
| ByteBuffer buffer = ByteBuffer.wrap(bytes); |
| int newLimit = offset + length <= buffer.capacity() ? offset + length |
| : buffer.capacity(); |
| buffer.limit(newLimit); |
| buffer.position(offset); |
| return buffer; |
| } |
| |
| private static class ChannelInputStream extends InputStream { |
| |
| protected ReadableByteChannel channel; |
| |
| public ChannelInputStream(ReadableByteChannel aChannel) { |
| super(); |
| channel = aChannel; |
| } |
| |
| @Override |
| public synchronized int read() throws IOException { |
| byte[] oneByte = new byte[1]; |
| int n = read(oneByte); |
| if (n == 1) { |
| // reads a single byte 0-255 |
| return oneByte[0] & 0xff; |
| } |
| return -1; |
| } |
| |
| @Override |
| public synchronized void close() throws IOException { |
| channel.close(); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newInputStream(ReadableByteChannel channel) |
| */ |
| private static class ReadableByteChannelInputStream extends ChannelInputStream { |
| |
| public ReadableByteChannelInputStream(ReadableByteChannel aChannel) { |
| super(aChannel); |
| } |
| |
| @Override |
| public synchronized int read(byte[] target, int offset, int length) |
| throws IOException { |
| // avoid int overflow, check null target |
| if (length + offset > target.length || length < 0 || offset < 0) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| if (0 == length) { |
| return 0; |
| } |
| if (channel instanceof SelectableChannel) { |
| if (!((SelectableChannel) channel).isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| } |
| ByteBuffer buffer = ByteBuffer.wrap(target, offset, length); |
| return channel.read(buffer); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newReader(ReadableByteChannel channel, |
| * CharsetDecoder decoder, int minBufferCapacity) |
| */ |
| private static class ReaderInputStream extends ChannelInputStream { |
| |
| public ReaderInputStream(ReadableByteChannel aChannel) { |
| super(aChannel); |
| } |
| |
| @Override |
| public synchronized int read(byte[] target, int offset, int length) |
| throws IOException { |
| // avoid int overflow, check null target |
| if (length + offset > target.length || length < 0 || offset < 0) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| if (0 == length) { |
| return 0; |
| } |
| ByteBuffer buffer = ByteBuffer.wrap(target, offset, length); |
| return channel.read(buffer); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newOutputStream(WritableByteChannel channel) |
| */ |
| private static class WritableByteChannelOutputStream extends OutputStream { |
| |
| private WritableByteChannel channel; |
| |
| public WritableByteChannelOutputStream(WritableByteChannel aChannel) { |
| super(); |
| channel = aChannel; |
| } |
| |
| @Override |
| public synchronized void write(int oneByte) throws IOException { |
| byte[] wrappedByte = new byte[1]; |
| wrappedByte[0] = (byte) oneByte; |
| write(wrappedByte); |
| } |
| |
| @Override |
| public synchronized void write(byte[] source, int offset, int length) |
| throws IOException { |
| // avoid int overflow, check null source |
| if (length + offset > source.length || length < 0 || offset < 0) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| if (0 == length) { |
| return; |
| } |
| if (channel instanceof SelectableChannel) { |
| if (!((SelectableChannel) channel).isBlocking()) { |
| throw new IllegalBlockingModeException(); |
| } |
| } |
| ByteBuffer buffer = ByteBuffer.wrap(source, offset, length); |
| channel.write(buffer); |
| } |
| |
| @Override |
| public synchronized void close() throws IOException { |
| channel.close(); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newChannel(InputStream inputStream) |
| */ |
| private static class ReadableByteChannelImpl extends |
| AbstractInterruptibleChannel implements ReadableByteChannel { |
| private InputStream inputStream; |
| |
| ReadableByteChannelImpl(InputStream aInputStream) { |
| super(); |
| inputStream = aInputStream; |
| } |
| |
| public synchronized int read(ByteBuffer target) throws IOException { |
| if (!isOpen()) { |
| throw new ClosedChannelException(); |
| } |
| int bytesRemain = target.remaining(); |
| byte[] bytes = new byte[bytesRemain]; |
| int readCount = 0; |
| try { |
| begin(); |
| readCount = inputStream.read(bytes); |
| } finally { |
| end(readCount >= 0); |
| } |
| if (readCount > 0) { |
| target.put(bytes, 0, readCount); |
| } |
| return readCount; |
| } |
| |
| @Override |
| protected void implCloseChannel() throws IOException { |
| inputStream.close(); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newChannel(OutputStream outputStream) |
| */ |
| private static class WritableByteChannelImpl extends |
| AbstractInterruptibleChannel implements WritableByteChannel { |
| private OutputStream outputStream; |
| |
| WritableByteChannelImpl(OutputStream aOutputStream) { |
| super(); |
| outputStream = aOutputStream; |
| } |
| |
| public synchronized int write(ByteBuffer source) throws IOException { |
| if (!isOpen()) { |
| throw new ClosedChannelException(); |
| } |
| int bytesRemain = source.remaining(); |
| if (bytesRemain == 0) { |
| return 0; |
| } |
| byte[] buf = new byte[bytesRemain]; |
| source.get(buf); |
| try { |
| begin(); |
| outputStream.write(buf, 0, bytesRemain); |
| } finally { |
| end(bytesRemain >= 0); |
| } |
| return bytesRemain; |
| } |
| |
| @Override |
| protected void implCloseChannel() throws IOException { |
| outputStream.close(); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newReader(ReadableByteChannel channel, |
| * CharsetDecoder decoder, int minBufferCapacity) |
| */ |
| private static class ByteChannelReader extends Reader { |
| |
| private InputStream inputStream; |
| |
| private static final int BUFFER_SIZE = 8192; |
| |
| CharsetDecoder decoder; |
| |
| ByteBuffer bytes; |
| |
| CharBuffer chars; |
| |
| public ByteChannelReader(InputStream aInputStream, |
| CharsetDecoder aDecoder, int minBufferCapacity) { |
| super(aInputStream); |
| aDecoder.reset(); |
| inputStream = aInputStream; |
| int bufferSize = Math.max(minBufferCapacity, BUFFER_SIZE); |
| bytes = ByteBuffer.allocate(bufferSize); |
| chars = CharBuffer.allocate(bufferSize); |
| decoder = aDecoder; |
| chars.limit(0); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| synchronized (lock) { |
| decoder = null; |
| if (inputStream != null) { |
| inputStream.close(); |
| inputStream = null; |
| } |
| } |
| } |
| |
| @Override |
| public boolean ready() { |
| synchronized (lock) { |
| if (null == inputStream) { |
| return false; |
| } |
| try { |
| return chars.limit() > chars.position() |
| || inputStream.available() > 0; |
| } catch (IOException e) { |
| return false; |
| } |
| } |
| } |
| |
| @Override |
| public int read() throws IOException { |
| return IOUtil.readInputStreamReader(inputStream, bytes, chars, |
| decoder, lock); |
| } |
| |
| @Override |
| public int read(char[] buf, int offset, int length) throws IOException { |
| return IOUtil.readInputStreamReader(buf, offset, length, |
| inputStream, bytes, chars, decoder, lock); |
| } |
| } |
| |
| /* |
| * Wrapper class used for newWriter(WritableByteChannel channel, |
| * CharsetEncoder encoder, int minBufferCapacity) |
| */ |
| private static class ByteChannelWriter extends Writer { |
| |
| private static final int BUFFER_SIZE = 8192; |
| |
| private OutputStream outputStream; |
| |
| private CharsetEncoder encoder; |
| |
| private ByteBuffer byteBuf; |
| |
| public ByteChannelWriter(OutputStream aOutputStream, |
| CharsetEncoder aEncoder, int minBufferCap) { |
| super(aOutputStream); |
| aEncoder.charset(); |
| outputStream = aOutputStream; |
| byteBuf = ByteBuffer.allocate(Math.max(minBufferCap, BUFFER_SIZE)); |
| encoder = aEncoder; |
| } |
| |
| @Override |
| public void close() throws IOException { |
| synchronized (lock) { |
| if (encoder != null) { |
| flush(); |
| outputStream.flush(); |
| outputStream.close(); |
| encoder = null; |
| byteBuf = null; |
| } |
| } |
| } |
| |
| @Override |
| public void flush() throws IOException { |
| IOUtil |
| .flushOutputStreamWriter(outputStream, byteBuf, encoder, |
| lock); |
| } |
| |
| @Override |
| public void write(char[] buf, int offset, int count) throws IOException { |
| IOUtil.writeOutputStreamWriter(buf, offset, count, outputStream, |
| byteBuf, encoder, lock); |
| } |
| |
| @Override |
| public void write(int oneChar) throws IOException { |
| IOUtil.writeOutputStreamWriter(oneChar, outputStream, byteBuf, |
| encoder, lock); |
| } |
| |
| @Override |
| public void write(String str, int offset, int count) throws IOException { |
| IOUtil.writeOutputStreamWriter(str, offset, count, outputStream, |
| byteBuf, encoder, lock); |
| } |
| } |
| } |