Skip to content

Commit 95c3109

Browse files
mchesgarydgregory
authored andcommitted
IO-578: Support java.nio.Path and non-default filesystems for ReversedLinesFileReader (#62)
1 parent e921bc6 commit 95c3109

3 files changed

Lines changed: 122 additions & 45 deletions

File tree

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ file comparators, endian transformation classes, and much more.
232232
<version>4.12</version>
233233
<scope>test</scope>
234234
</dependency>
235+
<dependency>
236+
<groupId>com.google.jimfs</groupId>
237+
<artifactId>jimfs</artifactId>
238+
<version>1.1</version>
239+
<scope>test</scope>
240+
</dependency>
235241
<dependency>
236242
<groupId>org.apache.commons</groupId>
237243
<artifactId>commons-lang3</artifactId>

src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
import java.io.Closeable;
2020
import java.io.File;
2121
import java.io.IOException;
22-
import java.io.RandomAccessFile;
2322
import java.io.UnsupportedEncodingException;
23+
import java.nio.ByteBuffer;
24+
import java.nio.channels.SeekableByteChannel;
2425
import java.nio.charset.Charset;
2526
import java.nio.charset.CharsetEncoder;
2627
import java.nio.charset.StandardCharsets;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.nio.file.StandardOpenOption;
2731

2832
import org.apache.commons.io.Charsets;
2933

@@ -41,7 +45,7 @@ public class ReversedLinesFileReader implements Closeable {
4145
private final int blockSize;
4246
private final Charset encoding;
4347

44-
private final RandomAccessFile randomAccessFile;
48+
private final SeekableByteChannel channel;
4549

4650
private final long totalByteLength;
4751
private final long totalBlockCount;
@@ -79,6 +83,20 @@ public ReversedLinesFileReader(final File file) throws IOException {
7983
* @since 2.5
8084
*/
8185
public ReversedLinesFileReader(final File file, final Charset charset) throws IOException {
86+
this(file.toPath(), charset);
87+
}
88+
89+
/**
90+
* Creates a ReversedLinesFileReader with default block size of 4KB and the
91+
* specified encoding.
92+
*
93+
* @param file
94+
* the file to be read
95+
* @param charset the encoding to use
96+
* @throws IOException if an I/O error occurs
97+
* @since 2.7
98+
*/
99+
public ReversedLinesFileReader(final Path file, final Charset charset) throws IOException {
82100
this(file, DEFAULT_BLOCK_SIZE, charset);
83101
}
84102

@@ -96,6 +114,23 @@ public ReversedLinesFileReader(final File file, final Charset charset) throws IO
96114
* @since 2.3
97115
*/
98116
public ReversedLinesFileReader(final File file, final int blockSize, final Charset encoding) throws IOException {
117+
this(file.toPath(), blockSize, encoding);
118+
}
119+
120+
/**
121+
* Creates a ReversedLinesFileReader with the given block size and encoding.
122+
*
123+
* @param file
124+
* the file to be read
125+
* @param blockSize
126+
* size of the internal buffer (for ideal performance this should
127+
* match with the block size of the underlying file system).
128+
* @param encoding
129+
* the encoding of the file
130+
* @throws IOException if an I/O error occurs
131+
* @since 2.7
132+
*/
133+
public ReversedLinesFileReader(final Path file, final int blockSize, final Charset encoding) throws IOException {
99134
this.blockSize = blockSize;
100135
this.encoding = encoding;
101136

@@ -135,8 +170,8 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
135170
avoidNewlineSplitBufferSize = newLineSequences[0].length;
136171

137172
// Open file
138-
randomAccessFile = new RandomAccessFile(file, "r");
139-
totalByteLength = randomAccessFile.length();
173+
channel = Files.newByteChannel(file, StandardOpenOption.READ);
174+
totalByteLength = channel.size();
140175
int lastBlockLength = (int) (totalByteLength % blockSize);
141176
if (lastBlockLength > 0) {
142177
totalBlockCount = totalByteLength / blockSize + 1;
@@ -165,6 +200,25 @@ public ReversedLinesFileReader(final File file, final int blockSize, final Chars
165200
* version 2.2 if the encoding is not supported.
166201
*/
167202
public ReversedLinesFileReader(final File file, final int blockSize, final String encoding) throws IOException {
203+
this(file.toPath(), blockSize, encoding);
204+
}
205+
206+
/**
207+
* Creates a ReversedLinesFileReader with the given block size and encoding.
208+
*
209+
* @param file
210+
* the file to be read
211+
* @param blockSize
212+
* size of the internal buffer (for ideal performance this should
213+
* match with the block size of the underlying file system).
214+
* @param encoding
215+
* the encoding of the file
216+
* @throws IOException if an I/O error occurs
217+
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in
218+
* version 2.2 if the encoding is not supported.
219+
* @since 2.7
220+
*/
221+
public ReversedLinesFileReader(final Path file, final int blockSize, final String encoding) throws IOException {
168222
this(file, blockSize, Charsets.toCharset(encoding));
169223
}
170224

@@ -203,7 +257,7 @@ public String readLine() throws IOException {
203257
*/
204258
@Override
205259
public void close() throws IOException {
206-
randomAccessFile.close();
260+
channel.close();
207261
}
208262

209263
private class FilePart {
@@ -230,8 +284,8 @@ private FilePart(final long no, final int length, final byte[] leftOverOfLastFil
230284

231285
// read data
232286
if (no > 0 /* file not empty */) {
233-
randomAccessFile.seek(off);
234-
final int countRead = randomAccessFile.read(data, 0, length);
287+
channel.position(off);
288+
final int countRead = channel.read(ByteBuffer.wrap(data, 0, length));
235289
if (countRead != length) {
236290
throw new IllegalStateException("Count of requested bytes and actually read bytes don't match");
237291
}

src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@
1616
*/
1717
package org.apache.commons.io.input;
1818

19-
2019
import static org.junit.Assert.assertEquals;
2120

2221
import java.io.BufferedReader;
23-
import java.io.File;
24-
import java.io.FileInputStream;
2522
import java.io.IOException;
26-
import java.io.InputStreamReader;
2723
import java.net.URISyntaxException;
24+
import java.nio.charset.Charset;
25+
import java.nio.file.*;
2826
import java.util.Arrays;
2927
import java.util.Collection;
3028
import java.util.Stack;
3129

30+
import com.google.common.jimfs.Configuration;
31+
import com.google.common.jimfs.Jimfs;
3232
import org.junit.After;
33+
import org.junit.Before;
3334
import org.junit.Test;
3435
import org.junit.runner.RunWith;
3536
import org.junit.runners.Parameterized;
@@ -40,52 +41,66 @@
4041
*/
4142
@RunWith(Parameterized.class)
4243
public class ReversedLinesFileReaderTestParamFile {
43-
44-
@Parameters(name = "{0}, charset={1}")
45-
public static Collection<Object[]> blockSizes() {
44+
@Parameters(name = "{0}, encoding={1}, blockSize={2}, useNonDefaultFileSystem={3}")
45+
public static Collection<Object[]> parameters() {
4646
return Arrays.asList(new Object[][]{
47-
{"test-file-20byteslength.bin", "ISO_8859_1", null},
48-
{"test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null},
49-
{"test-file-iso8859-1.bin", "ISO_8859_1", null},
50-
{"test-file-shiftjis.bin", "Shift_JIS", null},
51-
{"test-file-utf16be.bin", "UTF-16BE", null},
52-
{"test-file-utf16le.bin", "UTF-16LE", null},
53-
{"test-file-utf8-cr-only.bin", "UTF-8", null},
54-
{"test-file-utf8-win-linebr.bin", "UTF-8", null},
55-
{"test-file-utf8-win-linebr.bin", "UTF-8", 1},
56-
{"test-file-utf8-win-linebr.bin", "UTF-8", 2},
57-
{"test-file-utf8-win-linebr.bin", "UTF-8", 3},
58-
{"test-file-utf8-win-linebr.bin", "UTF-8", 4},
59-
{"test-file-utf8.bin", "UTF-8", null},
60-
{"test-file-windows-31j.bin", "windows-31j", null},
61-
{"test-file-gbk.bin", "gbk", null},
62-
{"test-file-x-windows-949.bin", "x-windows-949", null},
63-
{"test-file-x-windows-950.bin", "x-windows-950", null},
47+
{"test-file-20byteslength.bin", "ISO_8859_1", null, false},
48+
{"test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null, false},
49+
{"test-file-iso8859-1.bin", "ISO_8859_1", null, false},
50+
{"test-file-shiftjis.bin", "Shift_JIS", null, false},
51+
{"test-file-utf16be.bin", "UTF-16BE", null, false},
52+
{"test-file-utf16le.bin", "UTF-16LE", null, false},
53+
{"test-file-utf8-cr-only.bin", "UTF-8", null, false},
54+
{"test-file-utf8-win-linebr.bin", "UTF-8", null, false},
55+
{"test-file-utf8-win-linebr.bin", "UTF-8", 1, false},
56+
{"test-file-utf8-win-linebr.bin", "UTF-8", 2, false},
57+
{"test-file-utf8-win-linebr.bin", "UTF-8", 3, false},
58+
{"test-file-utf8-win-linebr.bin", "UTF-8", 4, false},
59+
{"test-file-utf8.bin", "UTF-8", null, false},
60+
{"test-file-utf8.bin", "UTF-8", null, true},
61+
{"test-file-windows-31j.bin", "windows-31j", null, false},
62+
{"test-file-gbk.bin", "gbk", null, false},
63+
{"test-file-x-windows-949.bin", "x-windows-949", null, false},
64+
{"test-file-x-windows-950.bin", "x-windows-950", null, false},
6465
});
6566
}
6667

68+
private Path file;
69+
private FileSystem fileSystem;
6770
private ReversedLinesFileReader reversedLinesFileReader;
6871
private BufferedReader bufferedReader;
6972

7073
private final String fileName;
71-
private final String encoding;
72-
private final int buffSize;
74+
private final Charset encoding;
75+
private final Integer blockSize;
76+
private final boolean useNonDefaultFileSystem;
7377

74-
public ReversedLinesFileReaderTestParamFile(final String fileName, final String encoding, final Integer buffsize) {
78+
public ReversedLinesFileReaderTestParamFile(final String fileName, final String encoding, final Integer blockSize, final boolean useNonDefaultFileSystem) {
7579
this.fileName = fileName;
76-
this.encoding = encoding;
77-
this.buffSize = buffsize == null ? 4096 : buffsize;
80+
this.encoding = Charset.forName(encoding);
81+
this.blockSize = blockSize;
82+
this.useNonDefaultFileSystem = useNonDefaultFileSystem;
83+
}
84+
85+
@Before
86+
public void prepareFile() throws URISyntaxException, IOException {
87+
file = Paths.get(getClass().getResource("/" + fileName).toURI());
88+
if (useNonDefaultFileSystem) {
89+
fileSystem = Jimfs.newFileSystem(Configuration.unix());
90+
file = Files.copy(file, fileSystem.getPath("/" + fileName));
91+
}
7892
}
7993

8094
@Test
81-
public void testDataIntegrityWithBufferedReader() throws URISyntaxException, IOException {
82-
final File testFileIso = new File(this.getClass().getResource("/" + fileName).toURI());
83-
reversedLinesFileReader = new ReversedLinesFileReader(testFileIso, buffSize, encoding);
95+
public void testDataIntegrityWithBufferedReader() throws IOException {
96+
reversedLinesFileReader = blockSize == null
97+
? new ReversedLinesFileReader(file, encoding)
98+
: new ReversedLinesFileReader(file, blockSize, encoding);
8499

85100
final Stack<String> lineStack = new Stack<>();
86101

87-
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(testFileIso), encoding));
88-
String line = null;
102+
bufferedReader = Files.newBufferedReader(file, encoding);
103+
String line;
89104

90105
// read all lines in normal order
91106
while ((line = bufferedReader.readLine()) != null) {
@@ -97,11 +112,10 @@ public void testDataIntegrityWithBufferedReader() throws URISyntaxException, IOE
97112
final String lineFromBufferedReader = lineStack.pop();
98113
assertEquals(lineFromBufferedReader, line);
99114
}
100-
101115
}
102116

103117
@After
104-
public void closeReader() {
118+
public void releaseResources() {
105119
try {
106120
bufferedReader.close();
107121
} catch (final Exception e) {
@@ -112,7 +126,10 @@ public void closeReader() {
112126
} catch (final Exception e) {
113127
// ignore
114128
}
129+
try {
130+
fileSystem.close();
131+
} catch (final Exception e) {
132+
// ignore
133+
}
115134
}
116-
117-
118135
}

0 commit comments

Comments
 (0)