You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2015/03/11 11:21:59 UTC
svn commit: r1665833 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/segment/file/
test/java/org/apache/jackrabbit/oak/plugins/segment/file/
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();
+ }
+ }
+
+}