package quickbase.internal.files;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import quickbase.exception.AbortVisitException;
import quickbase.exception.BasicFileOperationDatabaseException;
import quickbase.exception.ClearDatabaseException;
import quickbase.exception.IExceptionHandlingStrategy;
import quickbase.exception.InvalidDataDatabaseException;
import quickbase.exception.SerializationDatabaseException;
import quickbase.internal.consumer.IEntryConsumer;
import quickbase.internal.entries.Entry;
public class DataFile {
public static final String SUFFIX = ".db";
private File file;
private RandomAccessFile raf;
public DataFile(File file) throws BasicFileOperationDatabaseException {
assert file.getName().endsWith(SUFFIX);
this.file = file;
try {
file.createNewFile();
} catch (IOException e) {
throw new BasicFileOperationDatabaseException("Could create file " + file + ", cause: " + e.getMessage());
}
}
DataFile() {
}
public long getSize() {
return file.length();
}
private RandomAccessFile getRaf() throws BasicFileOperationDatabaseException {
if (raf == null) {
try {
raf = new RandomAccessFile(file, "rw");
} catch (FileNotFoundException e) {
throw new BasicFileOperationDatabaseException(e);
}
}
return raf;
}
@SuppressWarnings("unused")
public int put(Entry entry, long maxLen) throws MaxFileLenReachedException, DataFileAbandonedException, BasicFileOperationDatabaseException {
try {
RandomAccessFile raf = getRaf();
long end = raf.length();
if (end >= maxLen) {
throw new MaxFileLenReachedException();
} else {
raf.seek(end);
entry.write(raf);
return (int) end;
}
} catch (IOException e) {
throw new BasicFileOperationDatabaseException(e);
}
}
@SuppressWarnings("unused")
public boolean visit(IExceptionHandlingStrategy strategy, long offset, long currentPos, IEntryConsumer consumer) throws DataFileAbandonedException, BasicFileOperationDatabaseException, ClearDatabaseException, AbortVisitException {
boolean more = true;
boolean valueNeeded = consumer.needsValues();
RandomAccessFile raf = getRaf();
try {
try {
long len = raf.length();
while (currentPos < len && more) {
assert currentPos >= 0;
raf.seek(currentPos);
try {
Entry entry = Entry.readEntry(raf, valueNeeded);
long nextPos = currentPos + entry.getSize();
// remember nextPos already here, who knows what
// consumer does to the file...
// long nextPos = raf.getFilePointer();
// assert testPos == nextPos;
try {
more = consumer.consume(entry, offset + currentPos);
} catch (SerializationDatabaseException e) {
strategy.handleSerializationProblem(e);
}
currentPos = nextPos;
} catch (InvalidDataDatabaseException e) {
currentPos = strategy.handleInvalidEntry(currentPos, len, e);
}
}
} catch (EOFException e) {
// assume last entry incomplete -> truncate
raf.setLength(currentPos);
}
} catch (IOException e) {
throw new BasicFileOperationDatabaseException(e);
} finally {
consumer.flush();
}
return more;
}
@SuppressWarnings("unused")
public Entry get(long pos) throws DataFileAbandonedException, BasicFileOperationDatabaseException, InvalidDataDatabaseException {
try {
RandomAccessFile raf = getRaf();
raf.seek(pos);
return Entry.readEntry(raf, true);
} catch (EOFException e) {
//maybe index wrong?
throw new InvalidDataDatabaseException(e);
} catch (IOException e) {
throw new BasicFileOperationDatabaseException(e);
}
}
public void close() {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
// ignore
}
raf = null;
}
}
public void rename(String newName) throws BasicFileOperationDatabaseException {
close();
File neu = new File(file.getParent(), newName + SUFFIX);
if (neu.exists()) {
neu.delete();
}
boolean success = file.renameTo(neu);
if (!success) {
throw new BasicFileOperationDatabaseException("Could not rename " + neu);
}
this.file = neu;
assert file.getName().endsWith(SUFFIX);
}
public void destroy() throws BasicFileOperationDatabaseException {
close();
if (!file.delete()) {
throw new BasicFileOperationDatabaseException("Could not delete: " + file);
}
}
}