groovy.io.LineColumnReader Maven / Gradle / Ivy
/*
* 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 groovy.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
/**
* The LineColumnReader
is an extension to BufferedReader
* that keeps track of the line and column information of where the cursor is.
*
* @author Guillaume Laforge
* @since 1.8.0
*/
public class LineColumnReader extends BufferedReader {
/**
* The current line position
*/
private long line = 1;
/**
* The current column position
*/
private long column = 1;
/**
* The latest marked line position
*/
private long lineMark = 1;
/**
* The latest marked line position
*/
private long columnMark = 1;
private boolean newLineWasRead = false;
/**
* Constructor wrapping a Reader
* (FileReader
, FileReader
, InputStreamReader
, etc.)
*
* @param reader the reader to wrap
*/
public LineColumnReader(Reader reader) {
super(reader);
}
/**
* Marks the present position in the stream. Subsequent calls to reset() will attempt to reposition the stream to this point.
*
* @param readAheadLimit Limit on the number of characters that may be read while still preserving the mark.
* An attempt to reset the stream after reading characters up to this limit or beyond may fail.
* A limit value larger than the size of the input buffer will cause a new buffer to be allocated whose size is no smaller than limit.
* Therefore large values should be used with care.
*/
@Override
public void mark(int readAheadLimit) throws IOException {
lineMark = line;
columnMark = column;
super.mark(readAheadLimit);
}
/**
* Resets the stream to the most recent mark.
*/
@Override
public void reset() throws IOException {
line = lineMark;
column = columnMark;
super.reset();
}
/**
* Reads a single character.
*
* @return The character read, as an integer in the range 0 to 65535 (0x00-0xffff),
* or -1 if the end of the stream has been reached
*/
@Override
public int read() throws IOException {
if (newLineWasRead) {
line += 1;
column = 1;
newLineWasRead = false;
}
int charRead = super.read();
if (charRead > -1) {
char c = (char)charRead;
// found a \r or \n, like on Mac or Unix
// could also be Windows' \r\n
if (c == '\r' || c == '\n') {
newLineWasRead = true;
if (c == '\r') {
mark(1);
c = (char)super.read();
// check if we have \r\n like on Windows
// if it's not \r\n we reset, otherwise, the \n is just consummed
if (c != '\n') {
reset();
}
}
} else {
column += 1;
}
}
return charRead;
}
/**
* Reads characters into a portion of an array.
*
* @param chars Destination array of char
* @param startOffset Offset at which to start storing characters
* @param length Maximum number of characters to read
* @return an exception if an error occurs
*/
@Override
public int read(char[] chars, int startOffset, int length) throws IOException {
for (int i = startOffset; i <= startOffset + length; i++) {
int readInt = read();
if (readInt == -1) return i - startOffset;
chars[i] = (char)readInt;
}
return length;
}
/**
* Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'),
* a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
*
* @return A String containing the contents of the line, not including any line-termination characters,
* or null if the end of the stream has been reached
*/
@Override
public String readLine() throws IOException {
StringBuilder result = new StringBuilder();
for (;;) {
int intRead = read();
if (intRead == -1) {
return result.length() == 0 ? null : result.toString();
}
char c = (char)intRead;
if (c == '\n' || c == '\r') break;
result.append(c);
}
return result.toString();
}
/**
* Skips characters.
*
* @param toSkip the number of characters to skip
* @return The number of characters actually skipped
*/
@Override
public long skip(long toSkip) throws IOException {
for (long i = 0; i < toSkip; i++) {
int intRead = read();
if (intRead == -1) return i;
}
return toSkip;
}
/**
* Reads characters into an array.
* This method will block until some input is available, an I/O error occurs,
* or the end of the stream is reached.
*
* @param chars Destination buffer
* @return The number of characters read, or -1 if the end of the stream has been reached
*/
@Override
public int read(char[] chars) throws IOException {
return read(chars, 0, chars.length - 1);
}
/**
* Not implemented.
*
* @param buffer Destination buffer
* @return The number of characters read, or -1 if the end of the stream has been reached
* @throws UnsupportedOperationException as the method is not implemented
*/
@Override
public int read(CharBuffer buffer) {
throw new UnsupportedOperationException("read(CharBuffer) not yet implemented");
}
/**
* Closes the stream and releases any system resources associated with it.
* Once the stream has been closed, further read(), ready(), mark(), reset(), or skip() invocations
* will throw an IOException. Closing a previously closed stream has no effect.
*/
@Override
public void close() throws IOException {
super.close();
}
public long getColumn() {
return column;
}
public void setColumn(long column) {
this.column = column;
}
public long getColumnMark() {
return columnMark;
}
public void setColumnMark(long columnMark) {
this.columnMark = columnMark;
}
public long getLine() {
return line;
}
public void setLine(long line) {
this.line = line;
}
public long getLineMark() {
return lineMark;
}
public void setLineMark(long lineMark) {
this.lineMark = lineMark;
}
}