Support java.nio.Path and non-default filesystems for ReversedLinesFileReader
MODIFY
pom.xml
pom.xml
@@ -232,6 +232,12 @@ file comparators, endian transformation classes, and much more.
<version>4.12</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.jimfs</groupId>
+ <artifactId>jimfs</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>

MODIFY
src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
-import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
import org.apache.commons.io.Charsets;
private final int blockSize;
private final Charset encoding;
- private final RandomAccessFile randomAccessFile;
+ private final SeekableByteChannel channel;
private final long totalByteLength;
private final long totalBlockCount;
@@ -79,6 +83,20 @@ public ReversedLinesFileReader(final File file) throws IOException {
* @since 2.5
*/
public ReversedLinesFileReader(final File file, final Charset charset) throws IOException {
+ this(file.toPath(), charset);
+ }
+
+ /**
+ * Creates a ReversedLinesFileReader with default block size of 4KB and the
+ * specified encoding.
+ *
+ * @param file
+ * the file to be read
+ * @param charset the encoding to use
+ * @throws IOException if an I/O error occurs
+ * @since 2.7
+ */
+ public ReversedLinesFileReader(final Path file, final Charset charset) throws IOException {
this(file, DEFAULT_BLOCK_SIZE, charset);
}
@@ -96,6 +114,23 @@ public ReversedLinesFileReader(final File file, final Charset charset) throws IO
* @since 2.3
*/
public ReversedLinesFileReader(final File file, final int blockSize, final Charset encoding) throws IOException {
+ this(file.toPath(), blockSize, encoding);
+ }
+
+ /**
+ * Creates a ReversedLinesFileReader with the given block size and encoding.
+ *
+ * @param file
+ * the file to be read
+ * @param blockSize
+ * size of the internal buffer (for ideal performance this should
+ * match with the block size of the underlying file system).
+ * @param encoding
+ * the encoding of the file
+ * @throws IOException if an I/O error occurs
+ * @since 2.7
+ */
+ public ReversedLinesFileReader(final Path file, final int blockSize, final Charset encoding) throws IOException {
this.blockSize = blockSize;
this.encoding = encoding;
@@ -135,8 +170,8 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
avoidNewlineSplitBufferSize = newLineSequences[0].length;
// Open file
- randomAccessFile = new RandomAccessFile(file, "r");
- totalByteLength = randomAccessFile.length();
+ channel = Files.newByteChannel(file, StandardOpenOption.READ);
+ totalByteLength = channel.size();
int lastBlockLength = (int) (totalByteLength % blockSize);
if (lastBlockLength > 0) {
totalBlockCount = totalByteLength / blockSize + 1;
@@ -165,6 +200,25 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
* version 2.2 if the encoding is not supported.
*/
public ReversedLinesFileReader(final File file, final int blockSize, final String encoding) throws IOException {
+ this(file.toPath(), blockSize, encoding);
+ }
+
+ /**
+ * Creates a ReversedLinesFileReader with the given block size and encoding.
+ *
+ * @param file
+ * the file to be read
+ * @param blockSize
+ * size of the internal buffer (for ideal performance this should
+ * match with the block size of the underlying file system).
+ * @param encoding
+ * the encoding of the file
+ * @throws IOException if an I/O error occurs
+ * @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in
+ * version 2.2 if the encoding is not supported.
+ * @since 2.7
+ */
+ public ReversedLinesFileReader(final Path file, final int blockSize, final String encoding) throws IOException {
this(file, blockSize, Charsets.toCharset(encoding));
}
@@ -203,7 +257,7 @@ public String readLine() throws IOException {
*/
@Override
public void close() throws IOException {
- randomAccessFile.close();
+ channel.close();
}
private class FilePart {
@@ -230,8 +284,8 @@ private FilePart(final long no, final int length, final byte[] leftOverOfLastFil
// read data
if (no > 0 /* file not empty */) {
- randomAccessFile.seek(off);
- final int countRead = randomAccessFile.read(data, 0, length);
+ channel.position(off);
+ final int countRead = channel.read(ByteBuffer.wrap(data, 0, length));
if (countRead != length) {
throw new IllegalStateException("Count of requested bytes and actually read bytes don't match");
}

MODIFY
src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
*/
package org.apache.commons.io.input;
-
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.nio.file.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Stack;
+import com.google.common.jimfs.Configuration;
+import com.google.common.jimfs.Jimfs;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
*/
@RunWith(Parameterized.class)
public class ReversedLinesFileReaderTestParamFile {
-
- @Parameters(name = "{0}, charset={1}")
- public static Collection<Object[]> blockSizes() {
+ @Parameters(name = "{0}, encoding={1}, blockSize={2}, useNonDefaultFileSystem={3}")
+ public static Collection<Object[]> parameters() {
return Arrays.asList(new Object[][]{
- {"test-file-20byteslength.bin", "ISO_8859_1", null},
- {"test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null},
- {"test-file-iso8859-1.bin", "ISO_8859_1", null},
- {"test-file-shiftjis.bin", "Shift_JIS", null},
- {"test-file-utf16be.bin", "UTF-16BE", null},
- {"test-file-utf16le.bin", "UTF-16LE", null},
- {"test-file-utf8-cr-only.bin", "UTF-8", null},
- {"test-file-utf8-win-linebr.bin", "UTF-8", null},
- {"test-file-utf8-win-linebr.bin", "UTF-8", 1},
- {"test-file-utf8-win-linebr.bin", "UTF-8", 2},
- {"test-file-utf8-win-linebr.bin", "UTF-8", 3},
- {"test-file-utf8-win-linebr.bin", "UTF-8", 4},
- {"test-file-utf8.bin", "UTF-8", null},
- {"test-file-windows-31j.bin", "windows-31j", null},
- {"test-file-gbk.bin", "gbk", null},
- {"test-file-x-windows-949.bin", "x-windows-949", null},
- {"test-file-x-windows-950.bin", "x-windows-950", null},
+ {"test-file-20byteslength.bin", "ISO_8859_1", null, false},
+ {"test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null, false},
+ {"test-file-iso8859-1.bin", "ISO_8859_1", null, false},
+ {"test-file-shiftjis.bin", "Shift_JIS", null, false},
+ {"test-file-utf16be.bin", "UTF-16BE", null, false},
+ {"test-file-utf16le.bin", "UTF-16LE", null, false},
+ {"test-file-utf8-cr-only.bin", "UTF-8", null, false},
+ {"test-file-utf8-win-linebr.bin", "UTF-8", null, false},
+ {"test-file-utf8-win-linebr.bin", "UTF-8", 1, false},
+ {"test-file-utf8-win-linebr.bin", "UTF-8", 2, false},
+ {"test-file-utf8-win-linebr.bin", "UTF-8", 3, false},
+ {"test-file-utf8-win-linebr.bin", "UTF-8", 4, false},
+ {"test-file-utf8.bin", "UTF-8", null, false},
+ {"test-file-utf8.bin", "UTF-8", null, true},
+ {"test-file-windows-31j.bin", "windows-31j", null, false},
+ {"test-file-gbk.bin", "gbk", null, false},
+ {"test-file-x-windows-949.bin", "x-windows-949", null, false},
+ {"test-file-x-windows-950.bin", "x-windows-950", null, false},
});
}
+ private Path file;
+ private FileSystem fileSystem;
private ReversedLinesFileReader reversedLinesFileReader;
private BufferedReader bufferedReader;
private final String fileName;
- private final String encoding;
- private final int buffSize;
+ private final Charset encoding;
+ private final Integer blockSize;
+ private final boolean useNonDefaultFileSystem;
- public ReversedLinesFileReaderTestParamFile(final String fileName, final String encoding, final Integer buffsize) {
+ public ReversedLinesFileReaderTestParamFile(final String fileName, final String encoding, final Integer blockSize, final boolean useNonDefaultFileSystem) {
this.fileName = fileName;
- this.encoding = encoding;
- this.buffSize = buffsize == null ? 4096 : buffsize;
+ this.encoding = Charset.forName(encoding);
+ this.blockSize = blockSize;
+ this.useNonDefaultFileSystem = useNonDefaultFileSystem;
+ }
+
+ @Before
+ public void prepareFile() throws URISyntaxException, IOException {
+ file = Paths.get(getClass().getResource("/" + fileName).toURI());
+ if (useNonDefaultFileSystem) {
+ fileSystem = Jimfs.newFileSystem(Configuration.unix());
+ file = Files.copy(file, fileSystem.getPath("/" + fileName));
+ }
}
@Test
- public void testDataIntegrityWithBufferedReader() throws URISyntaxException, IOException {
- final File testFileIso = new File(this.getClass().getResource("/" + fileName).toURI());
- reversedLinesFileReader = new ReversedLinesFileReader(testFileIso, buffSize, encoding);
+ public void testDataIntegrityWithBufferedReader() throws IOException {
+ reversedLinesFileReader = blockSize == null
+ ? new ReversedLinesFileReader(file, encoding)
+ : new ReversedLinesFileReader(file, blockSize, encoding);
final Stack<String> lineStack = new Stack<>();
- bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(testFileIso), encoding));
- String line = null;
+ bufferedReader = Files.newBufferedReader(file, encoding);
+ String line;
// read all lines in normal order
while ((line = bufferedReader.readLine()) != null) {
@@ -97,11 +112,10 @@ public void testDataIntegrityWithBufferedReader() throws URISyntaxException, IOE
final String lineFromBufferedReader = lineStack.pop();
assertEquals(lineFromBufferedReader, line);
}
-
}
@After
- public void closeReader() {
+ public void releaseResources() {
try {
bufferedReader.close();
} catch (final Exception e) {
@@ -112,7 +126,10 @@ public void closeReader() {
} catch (final Exception e) {
// ignore
}
+ try {
+ fileSystem.close();
+ } catch (final Exception e) {
+ // ignore
+ }
}
-
-
}

IO-578: Support java.nio.Path and non-default filesystems for ReversedLinesFileReader (#62)