src.org.python.core.io.TextIOWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython Show documentation
Show all versions of jython Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/* Copyright (c) 2007 Jython Developers */
package org.python.core.io;
import java.nio.ByteBuffer;
import java.util.regex.Pattern;
/**
* A Buffered text stream.
*
* This differs from py3k TextIOWrapper, which currently handles both
* text mode (py3k text mode is incompatible with Python 2.x's text
* mode) as well as universal mode.
*
* @see UniversalIOWrapper
*
* @author Philip Jenvey
*/
public class TextIOWrapper extends BinaryIOWrapper {
/** LF RE pattern, used for writes */
private static final Pattern LF_PATTERN = Pattern.compile("\n");
/** The platform's Newline character */
private String newline;
/** Whether or not newline is \n */
private boolean newlineIsLF;
/**
* Contruct a TextIOWrapper wrapping the given BufferedIOBase.
*
* @param bufferedIO {@inheritDoc}
*/
public TextIOWrapper(BufferedIOBase bufferedIO) {
super(bufferedIO);
newline = System.getProperty("line.separator");
newlineIsLF = newline.equals("\n");
}
@Override
public String read(int size) {
if (newlineIsLF) {
return super.read(size);
}
if (size < 0) {
return readall();
}
// Avoid ByteBuffer (this.readahead) method calls in the inner
// loop by reading directly from the readahead's backing array
byte[] readaheadArray;
int readaheadPos;
char[] builderArray = new char[size];
int builderPos = 0;
do {
readaheadArray = readahead.array();
readaheadPos = readahead.position();
while (readaheadPos < readahead.limit() && builderPos < size) {
char next = (char)(readaheadArray[readaheadPos++] & 0xff);
if (next == '\r') {
// Don't translate CR at EOF
if (readaheadPos == readahead.limit()) {
if (readChunk() == 0) {
// EOF
builderArray[builderPos++] = next;
return new String(builderArray, 0, builderPos);
}
// Not EOF and readChunk replaced the
// readahead; reset the readahead info
readaheadArray = readahead.array();
readaheadPos = readahead.position();
}
if (readaheadArray[readaheadPos] == LF_BYTE) {
next = '\n';
readaheadPos++;
}
}
builderArray[builderPos++] = next;
}
} while (builderPos < size && readChunk(size - builderPos) > 0);
// Finally reposition the readahead to where we ended. The
// position is invalid if the readahead is empty (at EOF;
// readChunk() returned 0)
if (readahead.hasRemaining()) {
readahead.position(readaheadPos);
}
// Shrink the readahead if it grew
packReadahead();
return new String(builderArray, 0, builderPos);
}
@Override
public String readall() {
if (newlineIsLF) {
return super.readall();
}
// Read the remainder of file
ByteBuffer remaining = bufferedIO.readall();
// Detect CRLF spanning the readahead and remaining
int length = 0;
if (readahead.hasRemaining() && readahead.get(readahead.limit() - 1) == CR_BYTE &&
remaining.hasRemaining() && remaining.get(remaining.position()) == LF_BYTE) {
// The trailing value of readahead is CR, the first value
// of remaining is LF. Overwrite CR with that LF (CRLF ->
// LF)
length--;
}
// Create an array that accommodates the readahead and the
// remainder
char[] all = new char[readahead.remaining() + remaining.remaining()];
// Consume the readahead
length += readLoop(readahead.array(), readahead.position(), all, 0, readahead.remaining());
readahead.position(readahead.limit());
// Consume the remainder of the file
length += readLoop(remaining.array(), remaining.position(), all, length,
remaining.remaining());
return new String(all, 0, length);
}
/**
* Read and convert the src byte array into the dest char array.
*
* Converts CRLF to LF. src is assumed to be the end of the file;
* that is, when src ends with CR, no attempt is made to peek into
* the underlying file to detect a succeeding LF.
*
* @param src the source byte array
* @param srcPos starting position in the source array
* @param dest the destination char array
* @param destPos starting position in the destination array
* @param length the number of array elements to be copied
* @return the number of chars written to the destination array
*/
private int readLoop(byte[] src, int srcPos, char[] dest, int destPos, int length) {
int destStartPos = destPos;
int srcEndPos = srcPos + length;
while (srcPos < srcEndPos) {
char next = (char)(src[srcPos++] & 0xff);
if (next == '\r') {
// Don't translate CR at EOF
if (srcPos == srcEndPos) {
dest[destPos++] = next;
continue;
}
if (src[srcPos] == LF_BYTE) {
next = '\n';
srcPos++;
}
}
dest[destPos++] = next;
}
return destPos - destStartPos;
}
@Override
public String readline(int size) {
if (newlineIsLF) {
return super.readline(size);
}
// Avoid ByteBuffer (this.readahead) and StringBuilder
// (this.builder) method calls in the inner loop by reading
// directly from the readahead's backing array and writing to
// an interim char array (this.interimBuilder)
byte[] readaheadArray;
int readaheadPos;
int interimBuilderPos;
do {
readaheadArray = readahead.array();
readaheadPos = readahead.position();
interimBuilderPos = 0;
while (readaheadPos < readahead.limit() &&
(size < 0 || builder.length() + interimBuilderPos < size)) {
char next = (char)(readaheadArray[readaheadPos++] & 0xff);
interimBuilder[interimBuilderPos++] = next;
if (next == '\r') {
boolean flushInterimBuilder = false;
// Don't translate CR at EOF
if (readaheadPos == readahead.limit()) {
if (readChunk() == 0) {
// EOF
builder.append(interimBuilder, 0, interimBuilderPos);
return drainBuilder();
}
// Not EOF and readChunk replaced the
// readahead; reset the readahead info and
// flush the interimBuilder (it's full)
readaheadArray = readahead.array();
readaheadPos = readahead.position();
flushInterimBuilder = true;
}
if (readaheadArray[readaheadPos] == LF_BYTE) {
// A CRLF: overwrite CR with the LF
readaheadPos++;
interimBuilder[interimBuilderPos - 1] = next = '\n';
}
if (flushInterimBuilder) {
// Safe to flush now, incase a CRLF was
// changed
builder.append(interimBuilder, 0, interimBuilderPos);
interimBuilderPos = 0;
}
}
if (next == '\n') {
builder.append(interimBuilder, 0, interimBuilderPos);
// Reposition the readahead to where we ended
readahead.position(readaheadPos);
return drainBuilder();
}
}
builder.append(interimBuilder, 0, interimBuilderPos);
} while ((size < 0 || builder.length() < size) && readChunk() > 0);
// Finally reposition the readahead to where we ended. The
// position is invalid if the readahead is empty (at EOF;
// readChunk() returned 0)
if (readahead.hasRemaining()) {
readahead.position(readaheadPos);
}
return drainBuilder();
}
@Override
public int write(String buf) {
if (!newlineIsLF) {
buf = LF_PATTERN.matcher(buf).replaceAll(newline);
}
return super.write(buf);
}
}