package com.limegroup.gnutella.library;
import java.io.File;
import java.util.Set;
import org.limewire.collection.IntervalSet;
import org.limewire.collection.Range;
import org.limewire.listener.SourcedEventMulticaster;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.downloader.VerifyingFile;
import com.limegroup.gnutella.licenses.LicenseFactory;
/**
* This class extends FileDesc and wraps an incomplete File, so it can be used
* for partial file sharing.
*/
class IncompleteFileDescImpl extends FileDescImpl implements IncompleteFileDesc {
/**
* Ranges smaller than this will never be offered to other servents.
*/
private final static int MIN_CHUNK_SIZE = 102400; // 100K
/**
* Needed to find out what ranges are available.
*/
private VerifyingFile _verifyingFile;
/**
* The name of the file, as returned by IncompleteFileManager
* .getCompletedName(FILE).
*/
private final String _name;
/**
* The size of the file, casted to an <tt>int</tt>.
*/
private final long _size;
/**
* Constructor for the IncompleteFileDesc object.
*/
public IncompleteFileDescImpl(RareFileStrategy rareFileStrategy,
LicenseFactory licenseFactory,
SourcedEventMulticaster<FileDescChangeEvent, FileDesc> fileDescMulticaster, File file,
Set<? extends URN> urns, int index, String completedName, long completedSize,
VerifyingFile vf) {
super(rareFileStrategy, licenseFactory, fileDescMulticaster, file, urns, index);
_name = completedName;
_size = completedSize;
_verifyingFile = vf;
}
/*
* (non-Javadoc)
*
* @see com.limegroup.gnutella.library.IncompleteFileDesc#getFileSize()
*/
@Override
public long getFileSize() {
return _size;
}
/*
* (non-Javadoc)
*
* @see com.limegroup.gnutella.library.IncompleteFileDesc#getFileName()
*/
@Override
public String getFileName() {
return _name;
}
/*
* (non-Javadoc)
*
* @see com.limegroup.gnutella.library.IncompleteFileDesc#getRangesAsByte()
*/
public IntervalSet.ByteIntervals getRangesAsByte() {
return _verifyingFile.toBytes();
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#getAvailableRanges()
*/
public String getAvailableRanges() {
StringBuilder ret = new StringBuilder("bytes");
boolean added = false;
// This must be synchronized so that downloaders writing
// to the verifying file do not cause concurrent mod
// exceptions.
synchronized (_verifyingFile) {
for (Range interval : _verifyingFile.getVerifiedBlocks()) {
// don't offer ranges that are smaller than MIN_CHUNK_SIZE
// ( we add one because HTTP values are exclusive )
if (interval.getHigh() - interval.getLow() + 1 < MIN_CHUNK_SIZE)
continue;
added = true;
// ( we subtract one because HTTP values are exclusive )
ret.append(" ").append(interval.getLow()).append("-")
.append(interval.getHigh() - 1).append(",");
}
}
// truncate off the last ',' if atleast one was added.
// it is necessary to do this (instead of checking hasNext when
// adding the comma) because it's possible that the last range
// is smaller than MIN_CHUNK_SIZE, leaving an extra comma at the end.
if (added)
ret.setLength(ret.length() - 1);
return ret.toString();
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#loadResponseRanges(
* org.limewire.collection.IntervalSet)
*/
public boolean loadResponseRanges(IntervalSet dest) {
synchronized (_verifyingFile) {
if (!hasUrnsAndPartialData()) {
assert getUrns().size() > 1
&& _verifyingFile.getBlockSize() + _verifyingFile.getAmountLost() >= MIN_CHUNK_SIZE : "urns : "
+ getUrns().size()
+ " size "
+ _verifyingFile.getBlockSize()
+ " lost "
+ _verifyingFile.getAmountLost();
}
if (_verifyingFile.getVerifiedBlockSize() > 0) {
dest.add(_verifyingFile.getVerifiedIntervalSet());
return true;
}
dest.add(_verifyingFile.getPartialIntervalSet());
return false;
}
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#hasUrnsAndPartialData()
*/
public boolean hasUrnsAndPartialData() {
return getUrns().size() > 1 && // must have both ttroot & sha1
_verifyingFile.getBlockSize() >= MIN_CHUNK_SIZE;
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#isRangeSatisfiable(
* long, long)
*/
public boolean isRangeSatisfiable(long low, long high) {
// This must be synchronized so that downloaders writing
// to the verifying file do not cause concurrent mod
// exceptions.
synchronized (_verifyingFile) {
for (Range interval : _verifyingFile.getVerifiedBlocks()) {
if (low >= interval.getLow() && high <= interval.getHigh())
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#getAvailableSubRange
* (long, long)
*/
public Range getAvailableSubRange(long low, long high) {
synchronized (_verifyingFile) {
for (Range interval : _verifyingFile.getVerifiedBlocks()) {
if ((interval.getLow() <= high && low <= interval.getHigh()))
// overlap found
return Range.createRange(Math.max(interval.getLow(), low), Math.min(interval
.getHigh(), high));
else if (interval.getLow() > high) // passed all viable
// intervals
break;
}
return null;
}
}
/*
* (non-Javadoc)
*
* @see
* com.limegroup.gnutella.library.IncompleteFileDesc#isRangeSatisfiable(
* org.limewire.collection.Range)
*/
public boolean isRangeSatisfiable(Range range) {
return isRangeSatisfiable(range.getLow(), range.getHigh());
}
// implements HTTPHeaderValue
/*
* (non-Javadoc)
*
* @see com.limegroup.gnutella.library.IncompleteFileDesc#httpStringValue()
*/
public String httpStringValue() {
return getAvailableRanges();
}
// overrides Object.toString to provide a more useful description
/*
* (non-Javadoc)
*
* @see com.limegroup.gnutella.library.IncompleteFileDesc#toString()
*/
@Override
public String toString() {
return ("IncompleteFileDesc:\r\n" + "name: " + _name + "\r\n" + "index: "
+ getIndex() + "\r\n");
}
}