Author: mduerig Date: Wed Mar 11 10:21:58 2015 New Revision: 1665833 URL: http://svn.apache.org/r1665833 Log: OAK-2605: Support for additional encodings needed in ReversedLinesFileReader Copying ReversedLinesFileReader from commons-io with the fix for IO-471. Credits to Leandro Reis for the patch.
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReader.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamBlockSize.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamFile.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesReaderTestData.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java?rev=1665833&r1=1665832&r2=1665833&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java Wed Mar 11 10:21:58 2015 @@ -25,7 +25,6 @@ import java.io.IOException; import java.util.Iterator; import com.google.common.collect.AbstractIterator; -import org.apache.commons.io.input.ReversedLinesFileReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,4 +70,5 @@ public final class JournalReader impleme public void close() throws IOException { journal.close(); } + } Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReader.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReader.java?rev=1665833&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReader.java (added) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReader.java Wed Mar 11 10:21:58 2015 @@ -0,0 +1,350 @@ +/* + * 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 org.apache.jackrabbit.oak.plugins.segment.file; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.UnsupportedCharsetException; + +import org.apache.commons.io.Charsets; + +/** + * Reads lines in a file reversely (similar to a BufferedReader, but starting at + * the last line). Useful for e.g. searching in log files. + * + * FIXME: this is a copy of org.apache.commons.io.input.ReversedLinesFileReader + * with a fix for IO-471. Replace again once commons-io has released a fixed version. + */ +class ReversedLinesFileReader implements Closeable { + + private final int blockSize; + private final Charset encoding; + + private final RandomAccessFile randomAccessFile; + + private final long totalByteLength; + private final long totalBlockCount; + + private final byte[][] newLineSequences; + private final int avoidNewlineSplitBufferSize; + private final int byteDecrement; + + private FilePart currentFilePart; + + private boolean trailingNewlineOfFileSkipped = false; + + /** + * Creates a ReversedLinesFileReader with default block size of 4KB and the + * platform's default encoding. + * + * @param file + * the file to be read + * @throws IOException if an I/O error occurs + */ + public ReversedLinesFileReader(final File file) throws IOException { + this(file, 4096, Charset.defaultCharset().toString()); + } + + /** + * 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.3 + */ + public ReversedLinesFileReader(final File file, final int blockSize, final Charset encoding) throws IOException { + this.blockSize = blockSize; + this.encoding = encoding; + + randomAccessFile = new RandomAccessFile(file, "r"); + totalByteLength = randomAccessFile.length(); + int lastBlockLength = (int) (totalByteLength % blockSize); + if (lastBlockLength > 0) { + totalBlockCount = totalByteLength / blockSize + 1; + } else { + totalBlockCount = totalByteLength / blockSize; + if (totalByteLength > 0) { + lastBlockLength = blockSize; + } + } + currentFilePart = new FilePart(totalBlockCount, lastBlockLength, null); + + // --- check & prepare encoding --- + Charset charset = Charsets.toCharset(encoding); + CharsetEncoder charsetEncoder = charset.newEncoder(); + float maxBytesPerChar = charsetEncoder.maxBytesPerChar(); + if(maxBytesPerChar==1f) { + // all one byte encodings are no problem + byteDecrement = 1; + } else if(charset == Charset.forName("UTF-8")) { + // UTF-8 works fine out of the box, for multibyte sequences a second UTF-8 byte can never be a newline byte + // http://en.wikipedia.org/wiki/UTF-8 + byteDecrement = 1; + } else if(charset == Charset.forName("Shift_JIS") || // Same as for UTF-8 http://www.herongyang.com/Unicode/JIS-Shift-JIS-Encoding.html + charset == Charset.forName("windows-31j") || // Windows code page 932 (Japanese) + charset == Charset.forName("x-windows-949") || // Windows code page 949 (Korean) + charset == Charset.forName("gbk") || // Windows code page 936 (Simplified Chinese) + charset == Charset.forName("x-windows-950")) { // Windows code page 950 (Traditional Chinese) + byteDecrement = 1; + } else if(charset == Charset.forName("UTF-16BE") || charset == Charset.forName("UTF-16LE")) { + // UTF-16 new line sequences are not allowed as second tuple of four byte sequences, + // however byte order has to be specified + byteDecrement = 2; + } else if(charset == Charset.forName("UTF-16")) { + throw new UnsupportedEncodingException( + "For UTF-16, you need to specify the byte order (use UTF-16BE or UTF-16LE)"); + } else { + throw new UnsupportedEncodingException( + "Encoding "+encoding+" is not supported yet (feel free to submit a patch)"); + } + // NOTE: The new line sequences are matched in the order given, so it is important that \r\n is BEFORE \n + newLineSequences = new byte[][] { "\r\n".getBytes(encoding), "\n".getBytes(encoding), "\r".getBytes(encoding) }; + + avoidNewlineSplitBufferSize = newLineSequences[0].length; + } + + /** + * 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 UnsupportedCharsetException + * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not + * supported. + */ + public ReversedLinesFileReader(final File file, final int blockSize, final String encoding) throws IOException { + this(file, blockSize, Charsets.toCharset(encoding)); + } + + /** + * Returns the lines of the file from bottom to top. + * + * @return the next line or null if the start of the file is reached + * @throws IOException if an I/O error occurs + */ + public String readLine() throws IOException { + + String line = currentFilePart.readLine(); + while (line == null) { + currentFilePart = currentFilePart.rollOver(); + if (currentFilePart != null) { + line = currentFilePart.readLine(); + } else { + // no more fileparts: we're done, leave line set to null + break; + } + } + + // aligned behaviour wiht BufferedReader that doesn't return a last, emtpy line + if("".equals(line) && !trailingNewlineOfFileSkipped) { + trailingNewlineOfFileSkipped = true; + line = readLine(); + } + + return line; + } + + /** + * Closes underlying resources. + * + * @throws IOException if an I/O error occurs + */ + public void close() throws IOException { + randomAccessFile.close(); + } + + private class FilePart { + private final long no; + + private final byte[] data; + + private byte[] leftOver; + + private int currentLastBytePos; + + /** + * ctor + * @param no the part number + * @param length its length + * @param leftOverOfLastFilePart remainder + * @throws IOException if there is a problem reading the file + */ + private FilePart(final long no, final int length, final byte[] leftOverOfLastFilePart) throws IOException { + this.no = no; + int dataLength = length + (leftOverOfLastFilePart != null ? leftOverOfLastFilePart.length : 0); + this.data = new byte[dataLength]; + final long off = (no - 1) * blockSize; + + // read data + if (no > 0 /* file not empty */) { + randomAccessFile.seek(off); + final int countRead = randomAccessFile.read(data, 0, length); + if (countRead != length) { + throw new IllegalStateException("Count of requested bytes and actually read bytes don't match"); + } + } + // copy left over part into data arr + if (leftOverOfLastFilePart != null) { + System.arraycopy(leftOverOfLastFilePart, 0, data, length, leftOverOfLastFilePart.length); + } + this.currentLastBytePos = data.length - 1; + this.leftOver = null; + } + + /** + * Handles block rollover + * + * @return the new FilePart or null + * @throws IOException if there was a problem reading the file + */ + private FilePart rollOver() throws IOException { + + if (currentLastBytePos > -1) { + throw new IllegalStateException("Current currentLastCharPos unexpectedly positive... " + + "last readLine() should have returned something! currentLastCharPos=" + currentLastBytePos); + } + + if (no > 1) { + return new FilePart(no - 1, blockSize, leftOver); + } else { + // NO 1 was the last FilePart, we're finished + if (leftOver != null) { + throw new IllegalStateException("Unexpected leftover of the last block: leftOverOfThisFilePart=" + + new String(leftOver, encoding)); + } + return null; + } + } + + /** + * Reads a line. + * + * @return the line or null + * @throws IOException if there is an error reading from the file + */ + private String readLine() throws IOException { + + String line = null; + int newLineMatchByteCount; + + boolean isLastFilePart = no == 1; + + int i = currentLastBytePos; + while (i > -1) { + + if (!isLastFilePart && i < avoidNewlineSplitBufferSize) { + // avoidNewlineSplitBuffer: for all except the last file part we + // take a few bytes to the next file part to avoid splitting of newlines + createLeftOver(); + break; // skip last few bytes and leave it to the next file part + } + + // --- check for newline --- + if ((newLineMatchByteCount = getNewLineMatchByteCount(data, i)) > 0 /* found newline */) { + final int lineStart = i + 1; + int lineLengthBytes = currentLastBytePos - lineStart + 1; + + if (lineLengthBytes < 0) { + throw new IllegalStateException("Unexpected negative line length="+lineLengthBytes); + } + byte[] lineData = new byte[lineLengthBytes]; + System.arraycopy(data, lineStart, lineData, 0, lineLengthBytes); + + line = new String(lineData, encoding); + + currentLastBytePos = i - newLineMatchByteCount; + break; // found line + } + + // --- move cursor --- + i -= byteDecrement; + + // --- end of file part handling --- + if (i < 0) { + createLeftOver(); + break; // end of file part + } + } + + // --- last file part handling --- + if (isLastFilePart && leftOver != null) { + // there will be no line break anymore, this is the first line of the file + line = new String(leftOver, encoding); + leftOver = null; + } + + return line; + } + + /** + * Creates the buffer containing any left over bytes. + */ + private void createLeftOver() { + int lineLengthBytes = currentLastBytePos + 1; + if (lineLengthBytes > 0) { + // create left over for next block + leftOver = new byte[lineLengthBytes]; + System.arraycopy(data, 0, leftOver, 0, lineLengthBytes); + } else { + leftOver = null; + } + currentLastBytePos = -1; + } + + /** + * Finds the new-line sequence and return its length. + * + * @param data buffer to scan + * @param i start offset in buffer + * @return length of newline sequence or 0 if none found + */ + private int getNewLineMatchByteCount(byte[] data, int i) { + for (byte[] newLineSequence : newLineSequences) { + boolean match = true; + for (int j = newLineSequence.length - 1; j >= 0; j--) { + int k = i + j - (newLineSequence.length - 1); + match &= k >= 0 && data[k] == newLineSequence[j]; + } + if (match) { + return newLineSequence.length; + } + } + return 0; + } + } + +} Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamBlockSize.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamBlockSize.java?rev=1665833&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamBlockSize.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamBlockSize.java Wed Mar 11 10:21:58 2015 @@ -0,0 +1,129 @@ +/* + * 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 org.apache.jackrabbit.oak.plugins.segment.file; + +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.GBK_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.WINDOWS_31J_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.X_WINDOWS_949_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.X_WINDOWS_950_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.createFile; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test checks symmetric behaviour with BufferedReader + * FIXME: this is mostly taken from a copy of org.apache.commons.io.input + * with a fix for IO-471. Replace again once commons-io has released a fixed version. + */ +@RunWith(Parameterized.class) +public class ReversedLinesFileReaderTestParamBlockSize { + + private static final String UTF_8 = "UTF-8"; + private static final String ISO_8859_1 = "ISO-8859-1"; + + @SuppressWarnings("boxing") + @Parameters // small and uneven block sizes are not used in reality but are good to show that the algorithm is solid + public static Collection<Integer[]> blockSizes() { + return Arrays.asList(new Integer[][] { {1}, {3}, {8}, {256}, {4096} }); + } + + private ReversedLinesFileReader reversedLinesFileReader; + private final int testParamBlockSize; + + public ReversedLinesFileReaderTestParamBlockSize(Integer testWithBlockSize) { + testParamBlockSize = testWithBlockSize; + } + + // Strings are escaped in constants to avoid java source encoding issues (source file enc is UTF-8): + + // windows-31j characters + private static final String TEST_LINE_WINDOWS_31J_1 = "\u3041\u3042\u3043\u3044\u3045"; + private static final String TEST_LINE_WINDOWS_31J_2 = "\u660E\u8F38\u5B50\u4EAC"; + // gbk characters (Simplified Chinese) + private static final String TEST_LINE_GBK_1 = "\u660E\u8F38\u5B50\u4EAC"; + private static final String TEST_LINE_GBK_2 = "\u7B80\u4F53\u4E2D\u6587"; + // x-windows-949 characters (Korean) + private static final String TEST_LINE_X_WINDOWS_949_1 = "\uD55C\uAD6D\uC5B4"; + private static final String TEST_LINE_X_WINDOWS_949_2 = "\uB300\uD55C\uBBFC\uAD6D"; + // x-windows-950 characters (Traditional Chinese) + private static final String TEST_LINE_X_WINDOWS_950_1 = "\u660E\u8F38\u5B50\u4EAC"; + private static final String TEST_LINE_X_WINDOWS_950_2 = "\u7E41\u9AD4\u4E2D\u6587"; + + @After + public void closeReader() { + try { + reversedLinesFileReader.close(); + } catch(Exception e) { + // ignore + } + } + + @Test + public void testWindows31jFile() throws URISyntaxException, IOException { + File testFileWindows31J = createFile(WINDOWS_31J_BIN); + reversedLinesFileReader = new ReversedLinesFileReader(testFileWindows31J, testParamBlockSize, "windows-31j"); + assertEqualsAndNoLineBreaks(TEST_LINE_WINDOWS_31J_2, reversedLinesFileReader.readLine()); + assertEqualsAndNoLineBreaks(TEST_LINE_WINDOWS_31J_1, reversedLinesFileReader.readLine()); + } + + @Test + public void testGBK() throws URISyntaxException, IOException { + File testFileGBK = createFile(GBK_BIN); + reversedLinesFileReader = new ReversedLinesFileReader(testFileGBK, testParamBlockSize, "GBK"); + assertEqualsAndNoLineBreaks(TEST_LINE_GBK_2, reversedLinesFileReader.readLine()); + assertEqualsAndNoLineBreaks(TEST_LINE_GBK_1, reversedLinesFileReader.readLine()); + } + + @Test + public void testxWindows949File() throws URISyntaxException, IOException { + File testFilexWindows949 = createFile(X_WINDOWS_949_BIN); + reversedLinesFileReader = new ReversedLinesFileReader(testFilexWindows949, testParamBlockSize, "x-windows-949"); + assertEqualsAndNoLineBreaks(TEST_LINE_X_WINDOWS_949_2, reversedLinesFileReader.readLine()); + assertEqualsAndNoLineBreaks(TEST_LINE_X_WINDOWS_949_1, reversedLinesFileReader.readLine()); + } + + @Test + public void testxWindows950File() throws URISyntaxException, IOException { + File testFilexWindows950 = createFile(X_WINDOWS_950_BIN); + reversedLinesFileReader = new ReversedLinesFileReader(testFilexWindows950, testParamBlockSize, "x-windows-950"); + assertEqualsAndNoLineBreaks(TEST_LINE_X_WINDOWS_950_2, reversedLinesFileReader.readLine()); + assertEqualsAndNoLineBreaks(TEST_LINE_X_WINDOWS_950_1, reversedLinesFileReader.readLine()); + } + + static void assertEqualsAndNoLineBreaks(String msg, String expected, String actual) { + if(actual!=null) { + assertFalse("Line contains \\n: line="+actual, actual.contains("\n")); + assertFalse("Line contains \\r: line="+actual, actual.contains("\r")); + } + assertEquals(msg, expected, actual); + } + static void assertEqualsAndNoLineBreaks(String expected, String actual) { + assertEqualsAndNoLineBreaks(null, expected, actual); + } +} Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamFile.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamFile.java?rev=1665833&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamFile.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesFileReaderTestParamFile.java Wed Mar 11 10:21:58 2015 @@ -0,0 +1,111 @@ +/* + * 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 org.apache.jackrabbit.oak.plugins.segment.file; + + +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.GBK_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.WINDOWS_31J_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.X_WINDOWS_949_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.X_WINDOWS_950_BIN; +import static org.apache.jackrabbit.oak.plugins.segment.file.ReversedLinesReaderTestData.createFile; +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.util.Arrays; +import java.util.Collection; +import java.util.Stack; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +/** + * Test checks symmetric behaviour with BufferedReader + * FIXME: this is mostly taken from a copy of org.apache.commons.io.input + * with a fix for IO-471. Replace again once commons-io has released a fixed version. + */ +@RunWith(Parameterized.class) +public class ReversedLinesFileReaderTestParamFile { + + @Parameters + public static Collection<Object[]> blockSizes() { + return Arrays.asList(new Object[][] { + {WINDOWS_31J_BIN, "windows-31j", null}, + {GBK_BIN, "gbk", null}, + {X_WINDOWS_949_BIN, "x-windows-949", null}, + {X_WINDOWS_950_BIN, "x-windows-950", null}, + }); + } + + private ReversedLinesFileReader reversedLinesFileReader; + private BufferedReader bufferedReader; + + private final byte[] data; + private final String encoding; + private final int buffSize; + + public ReversedLinesFileReaderTestParamFile(byte[] data, String encoding, Integer buffSize) { + this.data = data; + this.encoding = encoding; + this.buffSize = buffSize == null ? 4096 : buffSize; + } + + @Test + public void testDataIntegrityWithBufferedReader() throws URISyntaxException, IOException { + File testFileIso = createFile(data); + reversedLinesFileReader = new ReversedLinesFileReader(testFileIso, buffSize, encoding); + + Stack<String> lineStack = new Stack<String>(); + + bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(testFileIso), encoding)); + String line; + + // read all lines in normal order + while((line = bufferedReader.readLine())!=null) { + lineStack.push(line); + } + + // read in reverse order and compare with lines from stack + while((line = reversedLinesFileReader.readLine())!=null) { + String lineFromBufferedReader = lineStack.pop(); + assertEquals(lineFromBufferedReader, line); + } + + } + + @After + public void closeReader() { + try { + bufferedReader.close(); + } catch(Exception e) { + // ignore + } + try { + reversedLinesFileReader.close(); + } catch(Exception e) { + // ignore + } + } + +} Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesReaderTestData.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesReaderTestData.java?rev=1665833&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesReaderTestData.java (added) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/ReversedLinesReaderTestData.java Wed Mar 11 10:21:58 2015 @@ -0,0 +1,47 @@ +package org.apache.jackrabbit.oak.plugins.segment.file; + +import static java.io.File.createTempFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Test checks symmetric behaviour with BufferedReader + * FIXME: this is mostly taken from a copy of org.apache.commons.io.input + * with a fix for IO-471. Replace again once commons-io has released a fixed version. + */ +public final class ReversedLinesReaderTestData { + private ReversedLinesReaderTestData() {} + + public static final byte[] WINDOWS_31J_BIN = new byte[]{ + -126, -97, -126, -96, -126, -95, -126, -94, -126, -93, 13, 10, -106, -66, -105, 65, -114, + 113, -117, -98, 13, 10, + }; + + public static final byte[] GBK_BIN = new byte[]{ + -61, -9, -35, -108, -41, -45, -66, -87, 13, 10, -68, -14, -52, -27, -42, -48, -50, -60, + 13, 10, + }; + + public static final byte[] X_WINDOWS_949_BIN = new byte[]{ + -57, -47, -79, -71, -66, -18, 13, 10, -76, -21, -57, -47, -71, -50, -79, -71, 13, 10, + }; + + public static final byte[] X_WINDOWS_950_BIN = new byte[]{ + -87, -6, -65, -23, -92, 108, -88, -54, 13, 10, -63, 99, -59, -23, -92, -92, -92, -27, + 13, 10, + }; + + public static File createFile(byte[] data) throws IOException { + File file = createTempFile(ReversedLinesReaderTestData.class.getSimpleName(), null); + FileOutputStream os = new FileOutputStream(file); + try { + os.write(data); + return file; + } finally { + os.close(); + } + } + +}