package org.limewire.swarm.file;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.limewire.collection.Range;
import org.limewire.io.IOUtils;
import org.limewire.swarm.SwarmFile;
import org.limewire.swarm.SwarmFileSystem;
public class SwarmFileSystemImpl implements SwarmFileSystem {
private final List<SwarmFile> swarmFiles;
private final Map<SwarmFile, FileHandle> fileHandles;
private long completeSize = 0;
public SwarmFileSystemImpl() {
this.swarmFiles = new CopyOnWriteArrayList<SwarmFile>();
this.fileHandles = new HashMap<SwarmFile, FileHandle>();
}
public SwarmFileSystemImpl(SwarmFileImpl swarmFile) {
this();
addSwarmFile(swarmFile);
}
public long getCompleteSize() {
return completeSize;
}
public SwarmFile getSwarmFile(long position) {
for (SwarmFile swarmFile : swarmFiles) {
long startByte = swarmFile.getStartBytePosition();
long endByte = swarmFile.getEndBytePosition();
if (startByte <= position && endByte >= position) {
return swarmFile;
}
}
return null;
}
public List<SwarmFile> getSwarmFiles() {
return swarmFiles;
}
public synchronized void addSwarmFile(SwarmFileImpl swarmFile) {
swarmFiles.add(swarmFile);
swarmFile.setStartByte(completeSize);
completeSize += swarmFile.getFileSize();
}
public long write(ByteBuffer byteBuffer, long start) throws IOException {
initialize();
long currentPosition = start;
long wroteTotal = 0;
while (byteBuffer.position() < byteBuffer.limit()) {
SwarmFile swarmFile = getSwarmFile(currentPosition);
long writeStart = currentPosition - swarmFile.getStartBytePosition();
long wrote = getFileHandle(swarmFile).write(byteBuffer, writeStart);
currentPosition += wrote;
wroteTotal += wrote;
}
return wroteTotal;
}
public long read(ByteBuffer byteBuffer, long position) throws IOException {
initialize();
SwarmFile swarmFile = getSwarmFile(position);
long read = getFileHandle(swarmFile).read(byteBuffer, position);
return read;
}
public void closeSwarmFile(SwarmFile swarmFile) throws IOException {
FileHandle fileHandle = getFileHandle(swarmFile);
fileHandle.close();
fileHandles.put(swarmFile, null);
}
public synchronized void close() throws IOException {
IOException firstException = null;
for (SwarmFile swarmFile : swarmFiles) {
try {
closeSwarmFile(swarmFile);
} catch (IOException e) {
if (firstException == null) {
firstException = e;
}
}
}
if (firstException != null) {
throw firstException;
}
}
public List<SwarmFile> getSwarmFiles(Range range) {
ArrayList<SwarmFile> filesRet = new ArrayList<SwarmFile>();
long rangeStart = range.getLow();
long rangeEnd = range.getHigh();
for (SwarmFile swarmFile : swarmFiles) {
long startByte = swarmFile.getStartBytePosition();
long endByte = swarmFile.getEndBytePosition();
if (startByte <= rangeEnd && endByte >= rangeStart) {
filesRet.add(swarmFile);
}
}
return filesRet;
}
public synchronized void initialize() throws IOException {
}
private synchronized FileHandle getFileHandle(SwarmFile swarmFile) {
FileHandle fileHandle = fileHandles.get(swarmFile);
if (fileHandle == null) {
fileHandle = new FileHandle(swarmFile);
fileHandles.put(swarmFile, fileHandle);
}
return fileHandle;
}
private static class FileHandle {
private SwarmFile swarmFile = null;
private Object LOCK = new Object();
private RandomAccessFile raFile;
private FileChannel fileChannel;
private File file = null;
public FileHandle(SwarmFile swarmFile) {
this.swarmFile = swarmFile;
this.file = swarmFile.getFile();
}
public void initialize() throws IOException {
synchronized (LOCK) {
if (raFile == null) {
raFile = new RandomAccessFile(file, "rw");
fileChannel = raFile.getChannel();
}
}
}
public void close() throws IOException {
synchronized (LOCK) {
IOUtils.close(fileChannel);
IOUtils.close(raFile);
fileChannel = null;
raFile = null;
}
}
public void flush() throws IOException {
synchronized (LOCK) {
initialize();
fileChannel.force(true);
}
}
public long read(ByteBuffer byteBuffer, long position) throws IOException {
synchronized (LOCK) {
initialize();
long read = fileChannel.read(byteBuffer, position);
return read;
}
}
public long write(ByteBuffer byteBuffer, long start) throws IOException {
synchronized (LOCK) {
initialize();
long pendingBytes = swarmFile.getFileSize() - start;
int oldLimit = byteBuffer.limit();
int position = byteBuffer.position();
long wrote = 0;
try {
if (pendingBytes < Integer.MAX_VALUE) {
int pending = (int) pendingBytes;
int limit = position + pending;
if (limit < oldLimit) {
byteBuffer.limit(limit);
}
}
wrote = fileChannel.write(byteBuffer, start);
} finally {
byteBuffer.limit(oldLimit);
}
return wrote;
}
}
}
}