/*
* ====================================================================
* Copyright (c) 2004-2010 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.wc.patch;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNPatchFileStream {
public interface SVNPatchFileLineFilter {
boolean lineFilter(String line);
}
public interface SVNPatchFileLineTransformer {
String lineTransformer(String line);
}
private File path;
private boolean write;
private long start = 0;
private long end = -1;
private RandomAccessFile file;
private SVNPatchFileLineFilter lineFilter;
private SVNPatchFileLineTransformer lineTransformer;
private SVNPatchFileStream(File path, boolean write, long start, long end) {
this(path, write);
if (start >= 0 && end >= 0 && start <= end) {
this.start = start;
this.end = end;
} else {
throw new IllegalArgumentException("Bad start or end");
}
}
private SVNPatchFileStream(File path, boolean write) {
if (path != null) {
this.path = path;
} else {
throw new IllegalArgumentException("Bad path");
}
this.write = write;
}
public static SVNPatchFileStream openReadOnly(File path) throws IOException, SVNException {
final SVNPatchFileStream stream = new SVNPatchFileStream(path, false);
stream.reset();
return stream;
}
public static SVNPatchFileStream openRangeReadOnly(File path, long start, long end) throws IOException, SVNException {
final SVNPatchFileStream stream = new SVNPatchFileStream(path, false, start, end);
stream.reset();
return stream;
}
public static SVNPatchFileStream openForWrite(File path) throws IOException, SVNException {
final SVNPatchFileStream stream = new SVNPatchFileStream(path, true);
stream.reset();
return stream;
}
public File getPath() {
return path;
}
public void setLineFilter(SVNPatchFileLineFilter lineFilter) {
this.lineFilter = lineFilter;
}
public void setLineTransformer(SVNPatchFileLineTransformer lineTransfomer) {
this.lineTransformer = lineTransfomer;
}
private RandomAccessFile getFile() throws SVNException {
if (file == null) {
synchronized (this) {
if (file == null) {
if (write) {
file = SVNFileUtil.openRAFileForWriting(path, false);
} else {
file = SVNFileUtil.openRAFileForReading(path);
}
}
}
}
return file;
}
/**
* Reset a generic stream back to its origin. E.g. On a file this would be
* implemented as a seek to position 0). This function returns a
* #SVN_ERR_STREAM_RESET_NOT_SUPPORTED error when the stream doesn't
* implement resetting.
*
* @throws IOException
* @throws SVNException
*/
public void reset() throws IOException, SVNException {
final RandomAccessFile file = getFile();
if (start != file.getFilePointer()) {
file.seek(start);
}
}
public void close() throws IOException {
if (file != null) {
file.close();
}
}
public boolean isEOF() throws IOException, SVNException {
final RandomAccessFile file = getFile();
return file.getFilePointer() >= (end > 0 ? end : file.length());
}
public long getSeekPosition() throws SVNException, IOException {
final RandomAccessFile file = getFile();
return file.getFilePointer();
}
public void setSeekPosition(long pos) throws SVNException, IOException {
checkPos(pos);
final RandomAccessFile file = getFile();
file.seek(pos);
}
private void checkPos(long pos) throws SVNException {
if (!isPosValid(pos)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Bad position for file ''{0}'': {1}. Range is {2}..{3}.", new Object[] {
path, new Long(pos), new Long(start), new Long(end)
});
SVNErrorManager.error(err, Level.FINE, SVNLogType.DEFAULT);
}
}
private boolean isPosValid(long pos) {
return start <= pos && (end > 0 ? pos <= end : true);
}
public void write(String str) throws SVNException, IOException {
final RandomAccessFile file = getFile();
final long pos = file.getFilePointer();
checkPos(pos + str.length());
file.write(str.getBytes());
}
public void write(StringBuffer str) throws SVNException, IOException {
write(str.toString());
}
public void tryWrite(StringBuffer lineBuf) throws SVNException, IOException {
write(lineBuf);
}
public boolean readLineWithEol(StringBuffer lineBuf, StringBuffer eolStr) throws IOException, SVNException {
return readLine(lineBuf, eolStr, true);
}
public boolean readLine(StringBuffer lineBuf) throws IOException, SVNException {
return readLine(lineBuf, null);
}
public boolean readLine(StringBuffer lineBuf, String eolStr) throws IOException, SVNException {
final StringBuffer eol = eolStr != null ? new StringBuffer(eolStr) : null;
return readLine(lineBuf, eol, false);
}
private boolean readLine(StringBuffer input, StringBuffer eolStr, boolean detectEol) throws IOException, SVNException {
int c;
boolean eol;
boolean filtered;
do {
c = -1;
eol = false;
filtered = false;
input.setLength(0);
if (eolStr != null) {
eolStr.setLength(0);
}
while (!eol) {
switch (c = file.read()) {
case -1:
eol = true;
break;
case '\n':
if (detectEol && eolStr != null) {
eolStr.append((char) c);
}
eol = true;
break;
case '\r':
if (detectEol && eolStr != null) {
eolStr.append((char) c);
}
long cur = file.getFilePointer();
final int c2 = file.read();
if (c2 != '\n') {
file.seek(cur);
} else {
if (detectEol && eolStr != null) {
eolStr.append((char) c2);
}
}
eol = true;
break;
default:
input.append((char) c);
break;
}
}
if (lineFilter != null) {
filtered = lineFilter.lineFilter(input.toString());
if(filtered) {
input.setLength(0);
}
}
} while (filtered && !isEOF());
if (lineTransformer != null) {
final String line = lineTransformer.lineTransformer(input.toString());
input.setLength(0);
input.append(line);
}
return input.length() == 0 && c == -1 && isEOF();
}
}