// Commented for the Learning branch
package com.limegroup.bittorrent;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.Endpoint;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.SaveLocationException;
import com.limegroup.gnutella.URN;
/**
* A BTDownloader object provides information about what we're downloading for this torrent.
*
* BTDownloader implements LimeWire's Download interface.
* This lets LimeWire's GUI list this torrent in the same list as the Gnutella downloads.
*
* A ManagedTorrent makes and keeps a single BTDownloader object.
*/
public class BTDownloader implements Downloader {
/** A link back up to the ManagedTorrent object that made this BTDownloader. */
private final ManagedTorrent _torrent;
/** A link to the BTMetaInfo object that represents the .torrent file for this torrent. */
private final BTMetaInfo _info;
/** A BTDownloader has a SimpleBandwidthTracker that measures how fast we're downloading this torrent. */
private SimpleBandwidthTracker _tracker;
/** A HashMap the GUI uses to keep additional information about this download. */
protected Map attributes = new HashMap();
/**
* Make a new BTDownloader object that the GUI can use to get information about how this download is progressing.
*
* @param torrent The ManagedTorrent that's making this BTDownloader
* @param info A BTMetaInfo object we made from the bencoded data in the .torrent file
*/
public BTDownloader(ManagedTorrent torrent, BTMetaInfo info) {
// Save the given objects
_torrent = torrent;
_info = info;
// Make a new SimpleBandwidthTracker to keep track of how fast we're downloading data
_tracker = new SimpleBandwidthTracker();
}
/**
* Stop this download.
* Calls stop() on the ManagedTorrent, and then removes it from the TorrentManager's list of them.
*/
public void stop() {
// Tell the ManagedTorrent to stop
if (!_torrent.hasStopped()) _torrent.stop();
// Remove it from the program's list
RouterService.getTorrentManager().removeTorrent(_torrent, true);
}
/**
* Pause this download.
* Calls pause() on the ManagedTorrent.
*/
public void pause() {
// Have the ManagedTorrent do it
_torrent.pause();
}
/**
* Determine if this BitTorrent download is paused.
* Asks the ManagedTorrent object.
*
* @return True if the download is paused
*/
public boolean isPaused() {
// Ask the ManagedTorrent object
return _torrent.isPaused();
}
/**
* Determine if this BitTorrent download is paused or stopped.
*
* @return True if the ManagedTorrent is paused or has stopped.
* False if it's going.
*/
public boolean isInactive() {
// Return true if the ManagedTorrent is paused or stopped
return _torrent.isPaused() || _torrent.hasStopped();
}
/**
* The TorrentManager keeps torrents in a queue, get our position in it.
*
* @return Our queue position number
*/
public int getInactivePriority() {
// Ask the program's TorrentManager where we are in its list
return RouterService.getTorrentManager().getPositionInQueue(_torrent);
}
/**
* Resume downloading this torrent.
* Has the ManagedTorrent object do it.
*/
public boolean resume() {
// Have the ManagedTorrent object do it
return _torrent.resume();
}
/**
* Get the path of this BitTorrent download.
* While we're still getting the torrent, returns a path like "C:\Documents and Settings\Kevin\Incomplete\File Name.ext".
* After it's done, returns a path like "C:\Documents and Settings\Kevin\Shared\File Name.ext".
*
* @return A Java File object with the path
*/
public File getFile() {
// Return the path in the "Saved" or "Incomplete" folder
if (_torrent.isComplete()) return _info.getCompleteFile();
return _info.getBaseFile();
}
/**
* Get the start of the file that we can preview play for the user.
* BitTorrent downloads don't happen from the start, so we can't do this.
* Returns null if the file isn't done yet, and the whole file if it is.
*
* @return null if the torrent is still downloading.
* A Java File object with the path to the saved file.
*/
public File getDownloadFragment() {
// Return null until we have the entire torrent
if (!_torrent.isComplete()) return null;
return getFile();
}
/**
* Determine what state this BitTorrent download is in right now.
*
* The possible states are:
* QUEUED
* CONNECTING
* DOWNLOADING
* WAITING_FOR_RETRY
* COMPLETE
* ABORTED
* GAVE_UP
* COULDNT_MOVE_TO_LIBRARY
* WAITING_FOR_RESULTS
* CORRUPT_FILE
*
* @return The int code for the state
*/
public int getState() {
// Ask the ManagedTorrent object
return _torrent.getState();
}
/**
* Get the total number of bytes we've downloaded while getting this torrent.
* If we download the same part of the file twice, this number counts it both times.
*
* @return The size of data in bytes
*/
public long getTotalAmountDownloaded() {
// Ask our SimpleBandwidthTracker
return _tracker.getTotalAmount();
}
/**
* Returns 0, this is not applicable to BitTorrent.
*
* getRemainingStateTime() is supposed to return the longest time we'll stay in our current state, in seconds.
* If it's not known, getRemainingStateTime() is supposed to return Integer.MAX_VALUE.
*
* @return 0
*/
public int getRemainingStateTime() {
// Return 0, we can't support this part of the interface
return 0;
}
/**
* Get the file name of this torrent.
* If this is a multifile torrent, returns the folder name.
*
* @return The file or folder name as a String
*/
public String getFileName() {
// Ask the BTMetaInfo object, get the name from the bencoded data in the .torrent file
return _info.getName();
}
/**
* Get the file size of this torrent.
* If this is a multifile torrent, returns the total size of all the files placed together.
*
* @return The size in bytes
*/
public long getContentLength() {
// Get this information from the bencoded data in the .torrent file
return _info.getTotalSize();
}
/**
* Get the size of the portion of the file we've downloaded.
* If we download the same part of the file twice, this number counts it just once.
*
* @return The size of data in bytes
*/
public long getAmountRead() {
// Ask the VerifyingFolder object
return _info.getVerifyingFolder().getBlockSize();
}
/**
* Get a list of the IP addresses of the remote computers we're connected to in order to get this torrent.
*
* @return An Iterator you can move down a list of Endpoint objects
*/
public Iterator getHosts() {
// Make an empty HashSet we'll fill with Endpoint objects and return
Set ret = new HashSet();
// Loop for each BTConnection in the ManagedTorrent's list of them
for (Iterator iter = _torrent.getConnections().iterator(); iter.hasNext(); ) {
BTConnection btc = (BTConnection) iter.next();
// If we're not choking this connection right now, add it's IP address to our list
if (!btc.isChoking()) ret.add(btc.getEndpoint());
}
// Return the HashSet of Endpoint objects with the addresses
return ret.iterator();
}
/**
* Returns "BitTorrent" instead of a Gnutella vendor code like "LIME".
*
* @return The String "BitTorrent"
*/
public String getVendor() {
// Return the String "BitTorrent"
return "BitTorrent";
}
/**
* BitTorent doesn't support chat.
*
* @return null
*/
public Endpoint getChatEnabledHost() {
// Return null instead of an Endpoint object
return null;
}
/**
* Determine if we can chat with any of the remote computers that we're getting this torrent from.
*
* @return false, BitTorrent can't do this
*/
public boolean hasChatEnabledHost() {
// Return false, we can't chat
return false;
}
/**
* Does nothing, BitTorrent never gives up because of corrupt data.
*
* @param delete Not used
*/
public void discardCorruptDownload(boolean delete) {
// Do nothing
}
/**
* Returns null, BitTorrent programs don't support Gnutella's browse host.
*
* @return null
*/
public RemoteFileDesc getBrowseEnabledHost() {
// Return null instead of address information
return null;
}
/**
* Determine if one of our sources can do the Gnutella browse host feature.
*
* @return false, this is BitTorrent, so none can
*/
public boolean hasBrowseEnabledHost() {
// BitTorrent can't do Gnutella browse host
return false;
}
/**
* Find out what position we are in the queue.
*
* @return 1, a queue position doesn't really make sense for BitTorrent
*/
public int getQueuePosition() {
// Always return 1, say we're first in line
return 1;
}
/**
* Find out how many computers we're connected to in order to get this torrent.
*
* @return The number of connections we have for this torrent
*/
public int getNumberOfAlternateLocations() {
// Count our connections
return _torrent.getNumAltLocs();
}
/**
* Find out how many addresses we tried to connect to and we're unable to connect to while making connections to get this torrent.
*
* @return The number of addresses we couldn't connect to
*/
public int getNumberOfInvalidAlternateLocations() {
// Count the number of Endpoint objects the ManagedTorrent placed in its _badPeers list
return _torrent.getNumBadPeers();
}
/**
* Find out how many more addresses we have of remote computers that have this torrent.
*
* @return The number of addressess we could connect to
*/
public int getPossibleHostCount() {
// Get the number of addresses in the ManagedTorrent object's _peers list
return _torrent.getNumPeers();
}
/**
* Find out how many remote computers don't have any information that we need.
* Loops through our connections for this torrent, counting those that are not interesting to us.
*
* @return The number of connections we have open, but aren't using
*/
public int getBusyHostCount() {
// Have the ManagedTorrent count our connections that have no data we need
return _torrent.getNumBusyPeers();
}
/**
* Get the number of hosts we are remotely queued on.
* For BitTorrent, these are remote computers we're connected to for this torrent, but that are choking us.
*
* @param The number of connections we have but are witholding data from us
*/
public int getQueuedHostCount() {
// Loop through the ManagedTorrent object's list of BTConnection objects
int qd = 0;
for (Iterator iter = _torrent.getConnections().iterator(); iter.hasNext(); ) {
BTConnection c = (BTConnection)iter.next();
// If this remote computer is choking us, count it
if (c.isChoking()) qd++;
}
// Return the count
return qd;
}
/**
* Determine if this BitTorrent download is finished yet.
*
* @return true if it's done, false if not
*/
public boolean isCompleted() {
// Ask the ManagedTorrent object
return _torrent.isComplete();
}
/**
* Find out how much of this file we've verified as valid.
*
* @return The size of verified data, in bytes
*/
public long getAmountVerified() {
// Ask the VerifyingFolder object how many verified blocks we have, and multiply that by the block size to get bytes
return _info.getVerifyingFolder().getVerifiedBlockSize();
}
/**
* Get the size of file pieces we're downloading.
*
* @return The size of each file piece
*/
public int getChunkSize() {
// Return the number we parsed from the .torrent file
return (int)_info.getPieceLength();
}
/**
* Get the number of bytes we downloaded but then discarded because they didn't hash correctly.
*
* @return The size in bytes
*/
public long getAmountLost() {
// Ask the VerifyingFolder object
return _info.getVerifyingFolder().getNumCorruptedBytes();
}
/**
* Have our SimpleBandwidthTracker calculate the current speed it's downloading data.
*
* @return The speed, in KB/s
*/
public void measureBandwidth() {
// Ask our SimpleBandwidthTracker
_tracker.measureBandwidth();
}
/**
* Find out how fast we are downloading data for this torrent right now.
*
* @return The speed, in KB/s
*/
public float getMeasuredBandwidth() {
// Have our SimpleBandwidthTracker update its current speed right now
measureBandwidth();
// Get the current speed our SimpleBandwidthTracker has calculated
return _tracker.getMeasuredBandwidth();
}
/**
* Get the total average bandwidth, the number of bytes we've downloaded divided by the time since we started downloading this torrent.
*
* @return The total average bandwidth speed, in KB/s
*/
public float getAverageBandwidth() {
// Ask the SimpleBandwidthTracker
return _tracker.getAverageBandwidth();
}
/**
* No, this BitTorrent download isn't relocatable.
*
* @return false
*/
public boolean isRelocatable() {
// Always return false
return false;
}
/**
* Does nothing.
*/
public void setSaveFile(File saveDirectory, String fileName, boolean overwrite) throws SaveLocationException {
// TODO: decide how to deal with this
}
/**
* Get the path to where the program will save this BitTorrent download when it's done.
*
* @param The path in a Java File object, like "C:\Documents and Settings\Kevin\Shared\File Name.ext"
*/
public File getSaveFile() {
// Get the path from the BTMetaInfo object
return _info.getCompleteFile();
}
/**
* Have this object record that we downloaded some more bytes of file data.
*
* @param read The number of bytes we read
*/
void readBytes(int read) {
// Give the number to our SimpleBandwidthTracker
_tracker.count(read);
}
/**
* Get the torrent's info hash as a text URN, like "urn:sha1:JAZSGOLT6UP4I5N5KGJRZPSF6RZCEJKQ".
* The info hash is the SHA1 hash of the "info" section of the bencoded data of the .torrent file.
*
* @return A String like "urn:sha1:JAZSGOLT6UP4I5N5KGJRZPSF6RZCEJKQ"
*/
public URN getSHA1Urn() {
// Get it from the BTMetaInfo object
return _torrent.getMetaInfo().getURN();
}
/**
* Remove an attribute from the attributes HashMap this BTDownloader keeps for external code to save notes in.
*
* @param key The key name to remove from the HashMap
* @return The result Object of the remove() method
*/
public Object removeAttribute(String key) {
// Remove the key, and return the result
return attributes.remove(key);
}
/**
* Look up an attribute from the attributes HashMap this BTDownloader keeps for external code to save notes in.
*
* @param key The key name to look up
* @return The Object stored under that key
*/
public Object getAttribute(String key) {
// Look up the key name in the HashMap, and return the Object stored under it
return attributes.get(key);
}
/**
* Find out how many bytes of torrent data we've received, but are waiting for a thread to write to disk.
*
* @return The number of bytes
*/
public int getAmountPending() {
// Ask our torrent's VerifyingFolder object
return _info.getVerifyingFolder().getAmountPending();
}
/**
* Find out how many remote computers we have open connections to sharing this torrent through.
*
* @return The number of connections we have
*/
public int getNumHosts() {
// Ask the ManagedTorrent that made us, and represents this torrent
return _torrent.getNumConnections();
}
/**
* Set an attribute in the attributes HashMap this BTDownloader keeps for external code to save notes in.
*
* @param key The key name to save the object under.
* @param value The value object to store under the key.
* @return The object that was stored under that key before this one displaced it.
* null if this is the first object stored under that key.
*/
public Object setAttribute(String key, Object value) {
// Put the key and value in the attributes HashMap
return attributes.put(key, value);
}
}