/* Copyright (c) 2007 Jython Developers */
package org.python.core.io;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import org.python.core.Py;
/**
* Raw I/O implementation for simple streams.
*
* Supports Input/Outputstreams and Readable/WritableByteChannels.
*
* @author Philip Jenvey
*/
public class StreamIO extends RawIOBase {
/** The underlying read channel */
private ReadableByteChannel readChannel;
/** The underlying write channel */
private WritableByteChannel writeChannel;
/** The original InputStream if one was passed in, used for
* __tojava__ */
private InputStream inputStream;
/** The original OutputStream if one was passed in, used for
* __tojava__ */
private OutputStream outputStream;
/** true if the underlying file is actually closed on close() */
private boolean closefd;
/**
* Construct a StreamIO for the given read channel.
*
* @param readChannel a ReadableByteChannel
* @param closefd boolean whether the underlying file is closed on
* close() (defaults to True)
*/
public StreamIO(ReadableByteChannel readChannel, boolean closefd) {
this.readChannel = readChannel;
this.closefd = closefd;
}
/**
* Construct a StreamIO for the given read channel.
*
* @param readChannel a ReadableByteChannel
*/
public StreamIO(ReadableByteChannel readChannel) {
this(readChannel, true);
}
/**
* Construct a StreamIO for the given write channel.
*
* @param writeChannel a WritableByteChannel
* @param closefd boolean whether the underlying file is closed on
* close() (defaults to True)
*/
public StreamIO(WritableByteChannel writeChannel, boolean closefd) {
this.writeChannel = writeChannel;
this.closefd = closefd;
}
/**
* Construct a StreamIO for the given write channel.
*
* @param writeChannel a WritableByteChannel
*/
public StreamIO(WritableByteChannel writeChannel) {
this(writeChannel, true);
}
/**
* Construct a StreamIO for the given read/write streams.
*
* @param inputStream an InputStream
* @param closefd boolean whether the underlying file is closed on
* close() (defaults to True)
*/
public StreamIO(InputStream inputStream, boolean closefd) {
this(Channels.newChannel(inputStream), closefd);
this.inputStream = inputStream;
}
/**
* Construct a StreamIO for the given read/write streams.
*
* @param outputStream an OutputStream
* @param closefd boolean whether the underlying file is closed on
* close() (defaults to True)
*/
public StreamIO(OutputStream outputStream, boolean closefd) {
this(Channels.newChannel(outputStream), closefd);
this.outputStream = outputStream;
}
@Override
public int readinto(ByteBuffer buf) {
checkClosed();
checkReadable();
try {
return readChannel.read(buf);
} catch (IOException ioe) {
throw Py.IOError(ioe);
}
}
@Override
public int write(ByteBuffer buf) {
checkClosed();
checkWritable();
try {
return writeChannel.write(buf);
} catch (IOException ioe) {
throw Py.IOError(ioe);
}
}
@Override
public void flush() {
if (outputStream == null) {
return;
}
try {
outputStream.flush();
} catch (IOException ioe) {
throw Py.IOError(ioe);
}
}
@Override
public void close() {
if (closed()) {
return;
}
if (closefd) {
try {
if (readChannel != null) {
readChannel.close();
if (writeChannel != null && readChannel != writeChannel) {
writeChannel.close();
}
} else {
writeChannel.close();
}
} catch (IOException ioe) {
throw Py.IOError(ioe);
}
}
super.close();
}
/** Unwrap one or more nested FilterInputStreams. */
private static FileDescriptor getInputFileDescriptor(InputStream stream) throws IOException {
if (stream == null) {
return null;
}
if (stream instanceof FileInputStream) {
return ((FileInputStream)stream).getFD();
}
if (stream instanceof FilterInputStream) {
Field inField = null;
try {
inField = FilterInputStream.class.getDeclaredField("in");
inField.setAccessible(true);
return getInputFileDescriptor((InputStream)inField.get(stream));
} catch (Exception e) {
// XXX: masking other exceptions
} finally {
if (inField != null && inField.isAccessible()) {
inField.setAccessible(false);
}
}
}
return null;
}
/** Unwrap one or more nested FilterOutputStreams. */
private static FileDescriptor getOutputFileDescriptor(OutputStream stream) throws IOException {
if (stream == null) {
return null;
}
if (stream instanceof FileOutputStream) {
return ((FileOutputStream)stream).getFD();
}
if (stream instanceof FilterOutputStream) {
Field outField = null;
try {
outField = FilterOutputStream.class.getDeclaredField("out");
outField.setAccessible(true);
return getOutputFileDescriptor((OutputStream)outField.get(stream));
} catch (Exception e) {
// XXX: masking other exceptions
} finally {
if (outField != null && outField.isAccessible()) {
outField.setAccessible(false);
}
}
}
return null;
}
@Override
public boolean isatty() {
checkClosed();
FileDescriptor fd;
try {
if ((fd = getInputFileDescriptor(inputStream)) == null
&& (fd = getOutputFileDescriptor(outputStream)) == null) {
return false;
}
} catch (IOException e) {
return false;
}
return false;
}
@Override
public boolean readable() {
return readChannel != null;
}
@Override
public boolean writable() {
return writeChannel != null;
}
@Override
public OutputStream asOutputStream() {
if (writable()) {
if (outputStream == null) {
return Channels.newOutputStream(writeChannel);
}
return outputStream;
}
return super.asOutputStream();
}
@Override
public InputStream asInputStream() {
if (readable()) {
if (inputStream == null) {
return Channels.newInputStream(readChannel);
}
return inputStream;
}
return super.asInputStream();
}
@Override
public Channel getChannel() {
return readable() ? readChannel : writeChannel;
}
}