com.adobe.cq.history.utils.ReverseFileReader Maven / Gradle / Ivy
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.cq.history.utils;
import org.apache.commons.lang.ArrayUtils;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ReverseFileReader {
private static final int BUFFER_SIZE = 8192;
private final RandomAccessFile randomAccessFile;
private long filePosition;
private final String encoding;
private ByteBuffer buffer;
private int bufferPosition;
private byte lastLineBreak = '\n';
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
public ReverseFileReader(File file, String encoding) throws IOException {
this.randomAccessFile = new RandomAccessFile(file, "r");
this.filePosition = randomAccessFile.length() - 1;
this.encoding = encoding;
}
public String readLine() throws IOException {
String line = "";
while (true) {
// Check if buffer is empty
if (bufferPosition < 0) {
// Check if file start reached
if (filePosition <= 0) {
if (baos == null) {
return null;
}
line = readLineFromBuffer();
baos.close();
baos = null;
return line;
}
// Calculate next range to read
long offset = Math.max(filePosition - BUFFER_SIZE, 0);
long length = filePosition - offset;
// Read next range
buffer = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, offset, length);
// Define new buffer position
bufferPosition = (int) length;
filePosition = offset;
}
// Iterate over buffer
while (bufferPosition-- > 0) {
byte c = buffer.get(bufferPosition);
if (c == '\r' || c == '\n') {
if (c != lastLineBreak) {
lastLineBreak = c;
continue;
}
lastLineBreak = c;
// Line has been read
return readLineFromBuffer();
}
baos.write(c);
}
}
}
private String readLineFromBuffer() throws UnsupportedEncodingException {
if (baos.size() == 0) {
return "";
}
// Reverse bytes as we added them from end to start
byte[] bytes = baos.toByteArray();
ArrayUtils.reverse(bytes);
baos.reset();
return new String(bytes, encoding);
}
public void close() {
try {
randomAccessFile.close();
} catch (IOException e) {
// Ignore
}
}
}