package com.limegroup.gnutella.gui.upload;
import javax.swing.Icon;
import com.limegroup.gnutella.Assert;
import com.limegroup.gnutella.FileDesc;
import com.limegroup.gnutella.InsufficientDataException;
import com.limegroup.gnutella.Uploader;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.GUIUtils;
import com.limegroup.gnutella.gui.IconManager;
import com.limegroup.gnutella.gui.tables.AbstractDataLine;
import com.limegroup.gnutella.gui.tables.ChatHolder;
import com.limegroup.gnutella.gui.tables.IconAndNameHolder;
import com.limegroup.gnutella.gui.tables.IconAndNameHolderImpl;
import com.limegroup.gnutella.gui.tables.LimeTableColumn;
import com.limegroup.gnutella.gui.tables.ProgressBarHolder;
import com.limegroup.gnutella.gui.tables.SizeHolder;
import com.limegroup.gnutella.gui.tables.SpeedRenderer;
import com.limegroup.gnutella.gui.tables.TimeRemainingHolder;
import com.limegroup.gnutella.http.HTTPRequestMethod;
import com.limegroup.gnutella.util.CommonUtils;
/**
* This class handles all of the data for a single upload, representing
* one "line" in the upload window. It continually updates the
* displayed data for the upload from the contained <tt>Uploader</tt>
* instance.
*/
public final class UploadDataLine extends AbstractDataLine {
/**
* Constant for the <tt>Uploader</tt> instance for this upload.
*/
private Uploader UPLOADER;
/**
* Constant for the "connecting" upload state.
*/
private static final String CONNECTING_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_CONNECTING");
/**
* Constant for the "uploading" upload state.
*/
private static final String UPLOADING_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_UPLOADING");
/**
* Constant for the "limit reached" upload state.
*/
private static final String LIMIT_REACHED_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_LIMITREACHED");
/**
* Constant for the "freeloader" upload state.
*/
private static final String FREELOADER_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_FREELOADER");
/**
* Constant for the "interrupted" upload state.
*/
private static final String INTERRUPTED_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_INTERRUPTED");
/**
* Constant for the "complete" upload state.
*/
private static final String COMPLETE_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_COMPLETE");
/**
* Constant for the "file not found" upload state.
*/
private static final String FILE_NOT_FOUND_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_FNF");
/**
* Constant for the "Queued" upload state.
*/
private static final String QUEUED_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_QUEUED");
/**
* Constant for "Unavailable Range" upload state.
*/
private static final String UNAVAILABLE_RANGE_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_UNAVAILABLE_RANGE");
/**
* Constant for the "Malformed Request" upload state.
*/
private static final String MALFORMED_REQUEST_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_MALFORMED_REQUEST");
/**
* Constant for the "Banned Greedy Servent" upload state.
*/
private static final String BANNED_GREEDY_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_BANNED_GREEDY");
/**
* Constant for the "Uploading Hash Tree" upload state.
*/
private static final String HASH_TREE_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_HASH_TREE");
/** Constant for the 'Validating' upload state. */
private static final String VALIDATING_STATE =
GUIMediator.getStringResource("UPLOAD_TABLE_STRING_VALIDATING");
/**
* Constant for "average bandwidth" tip
*/
private static final String AVERAGE_BANDWIDTH =
GUIMediator.getStringResource("GENERAL_AVERAGE_BANDWIDTH");
/**
* Constant for "Started on" tip
*/
private static final String STARTED_ON =
GUIMediator.getStringResource("GENERAL_STARTED_ON");
/**
* Constant for "Finished on" tip
*/
private static final String FINISHED_ON =
GUIMediator.getStringResource("GENERAL_FINISHED_ON");
/**
* Constant for "Time Spent" tip
*/
private static final String TIME_SPENT =
GUIMediator.getStringResource("GENERAL_TIME_SPENT");
/**
* Variable for the name of the file being uploaded.
*/
private String _fileName;
/**
* Variable for the status of the upload.
*/
private String _status;
/**
* Variable for the hostname
*/
private String _hostName;
/**
* Variable for the userAgent
*/
private String _userAgent;
/**
* Variable for the progress bar
*/
private int _progress;
/**
* Variable for whether or not chat is enabled
*/
private boolean _chatEnabled;
/**
* Variable for whether or not browse is enabled
*/
private boolean _browseEnabled;
/**
* Variable for the speed
*/
private double _speed;
/**
* Variable for the time left
*/
private int _timeLeft;
/**
* Variable for whether or not cleanup should do anything
*/
private boolean _persistConnection;
/**
* Variable for the time the upload started.
*/
private long _startTime;
/**
* Variable for the time the upload ended.
*/
private long _endTime = -1;
/**
* Stores the current state of this upload, as of the last update.
* This is the state the everything should work off of to avoid the
* <tt>Uploader</tt> instance being in a different state than
* this data line.
*/
private int _state;
/**
* Column index for the file name.
*/
static final int FILE_INDEX = 0;
private static final LimeTableColumn FILE_COLUMN =
new LimeTableColumn(FILE_INDEX, "UPLOAD_TABLE_STRING_NAME",
160, true, IconAndNameHolder.class);
/**
* Column index for the host name.
*/
static final int HOST_INDEX = 1;
private static final LimeTableColumn HOST_COLUMN =
new LimeTableColumn(HOST_INDEX, "UPLOAD_TABLE_STRING_HOST",
70, true, String.class);
/**
* Column index for the file size.
*/
static final int SIZE_INDEX = 2;
private static final LimeTableColumn SIZE_COLUMN =
new LimeTableColumn(SIZE_INDEX, "UPLOAD_TABLE_STRING_SIZE",
25, true, SizeHolder.class);
/**
* Column index for the file upload status.
*/
static final int STATUS_INDEX = 3;
private static final LimeTableColumn STATUS_COLUMN =
new LimeTableColumn(STATUS_INDEX, "UPLOAD_TABLE_STRING_STATUS",
100, true, String.class);
/**
* Column index for whether or not the uploader is chat-enabled.
*/
static final int CHAT_INDEX = 4;
private static final LimeTableColumn CHAT_COLUMN =
new LimeTableColumn(CHAT_INDEX, "UPLOAD_TABLE_STRING_CHAT",
10, true, ChatHolder.class);
/**
* Column index for the progress of the upload.
*/
static final int PROGRESS_INDEX = 5;
private static final LimeTableColumn PROGRESS_COLUMN =
new LimeTableColumn(PROGRESS_INDEX, "UPLOAD_TABLE_STRING_PROGRESS",
25, true, ProgressBarHolder.class);
/**
* Column index for the upload speed.
*/
static final int SPEED_INDEX = 6;
private static final LimeTableColumn SPEED_COLUMN =
new LimeTableColumn(SPEED_INDEX, "UPLOAD_TABLE_STRING_SPEED",
15, true, SpeedRenderer.class);
/**
* Column index for the upload time remaining.
*/
static final int TIME_INDEX = 7;
private static final LimeTableColumn TIME_COLUMN =
new LimeTableColumn(TIME_INDEX, "UPLOAD_TABLE_STRING_TIME_REMAINING",
15, true, TimeRemainingHolder.class);
/**
* Column index for the user agent
*/
static final int USER_AGENT_INDEX = 8;
private static final LimeTableColumn USER_AGENT_COLUMN =
new LimeTableColumn(USER_AGENT_INDEX, "UPLOAD_TABLE_STRING_USER_AGENT",
70, true, String.class);
/** Number of columns visible
*
*/
static final int NUMBER_OF_COLUMNS = 9;
//implements DataLine interface
public int getColumnCount() { return NUMBER_OF_COLUMNS; }
/**
* Must initialize data.
*
* @param uploader the <tt>Uploader</tt>
* that provides access to
* information about the upload
*/
public void initialize(Object uploader) {
super.initialize(uploader);
if ( UPLOADER != null ) {
UPLOADER = (Uploader)uploader;
} else {
UPLOADER = (Uploader)uploader;
_startTime = System.currentTimeMillis();
_chatEnabled = UPLOADER.isChatEnabled();
_browseEnabled = UPLOADER.isBrowseHostEnabled();
_fileName = UPLOADER.getFileName();
_hostName = UPLOADER.getHost();
_userAgent = UPLOADER.getUserAgent();
}
_endTime = -1;
_status = "";
_persistConnection = false;
update();
}
// implements DataLine interface
public void cleanup() { if ( !_persistConnection) UPLOADER.stop(); }
/*
* Returns the <tt>Object</tt> stored at the specified column in this
* line of data.
*
* @param index the index of the column to retrieve data from
* @return the <tt>Object</tt> stored at that index
* @implements DataLine interface
*/
public Object getValueAt(int index) {
switch(index) {
case FILE_INDEX:
FileDesc fd = UPLOADER.getFileDesc();
Icon icon = fd == null ? null :
IconManager.instance().getIconForFile(fd.getFile());
return new IconAndNameHolderImpl(icon, _fileName);
case HOST_INDEX:
return _hostName;
case SIZE_INDEX:
return new SizeHolder(getLength());
case STATUS_INDEX:
return _status;
case CHAT_INDEX:
return _chatEnabled ? Boolean.TRUE : Boolean.FALSE;
case PROGRESS_INDEX:
return new Integer(_progress);
case SPEED_INDEX:
return new Double(_speed);
case TIME_INDEX:
return new TimeRemainingHolder(_timeLeft);
case USER_AGENT_INDEX:
return _userAgent;
}
return null;
}
// Implements DataLine interface
public LimeTableColumn getColumn(int idx) {
switch (idx) {
case FILE_INDEX: return FILE_COLUMN;
case HOST_INDEX: return HOST_COLUMN;
case SIZE_INDEX: return SIZE_COLUMN;
case STATUS_INDEX: return STATUS_COLUMN;
case CHAT_INDEX: return CHAT_COLUMN;
case PROGRESS_INDEX: return PROGRESS_COLUMN;
case SPEED_INDEX: return SPEED_COLUMN;
case TIME_INDEX: return TIME_COLUMN;
case USER_AGENT_INDEX: return USER_AGENT_COLUMN;
}
return null;
}
public boolean isClippable(int idx) {
switch(idx) {
case CHAT_INDEX:
case PROGRESS_INDEX:
return false;
default:
return true;
}
}
public int getTypeAheadColumn() {
return FILE_INDEX;
}
public String[] getToolTipArray(int col) {
String[] info = new String[ _endTime != -1 ? 5 : 4];
String tp = AVERAGE_BANDWIDTH + ": " + GUIUtils.rate2speed(
UPLOADER.getAverageBandwidth()
);
info[0] = STARTED_ON + " " + GUIUtils.msec2DateTime( _startTime );
if( _endTime != -1 ) {
info[1] = FINISHED_ON + " " + GUIUtils.msec2DateTime( _endTime );
info[2] = TIME_SPENT + ": " + CommonUtils.seconds2time(
(int)((_endTime - _startTime) / 1000 ) );
info[3] = "";
info[4] = tp;
} else {
info[1] = TIME_SPENT + ": " + CommonUtils.seconds2time(
(int) ((System.currentTimeMillis() - _startTime) / 1000 ) );
info[2] = "";
info[3] = tp;
}
return info;
}
// Implements DataLine interface
public boolean isDynamic(int idx) {
switch(idx) {
case STATUS_INDEX:
case PROGRESS_INDEX:
case SPEED_INDEX:
case TIME_INDEX:
return true;
}
return false;
}
/**
* Returns the total size in bytes of the file being uploaded.
*
* @return the total size in bytes of the file being uploaded
*/
long getLength() {
return UPLOADER == null ? 0 : UPLOADER.getFileSize();
}
/**
* Returns whether or not the <tt>Uploader</tt> for this upload
* is equal to the one passed in.
*
* @return <tt>true</tt> if the passed-in uploader is equal to the
* <tt>Uploader</tt> for this upload, <tt>false</tt> otherwise
*/
boolean containsUploader(Uploader uploader) {
return UPLOADER.equals(uploader);
}
/**
* Returns the <tt>Uploader</tt> associated with this upload.
*
* @return the <tt>Uploader</tt> associated with this upload
*/
Uploader getUploader() {
return UPLOADER;
}
/**
* Returns the ip address string of the host we are uploading from.
*
* @return the ip address string of the host we are uploading from
*/
String getHost() {
return UPLOADER.getHost();
}
/**
* Returns the index of the file of the uploader
*
* @return the index of the file of the uploader
*/
int getFileIndex() {
return UPLOADER.getIndex();
}
/**
* Return the state of the Uploader
*
* @return the state of the uploader
*/
int getState() {
return _state;
}
/**
* Returns whether or not the upload has completed.
*
* @return <tt>true</tt> if the upload is complete, <tt>false</tt> otherwise
*/
boolean isCompleted() {
return _state == Uploader.COMPLETE;
}
/**
* Returns whether or not chat is enabled for this upload.
*
* @return <tt>true</tt> if the host we're uploading from is chattable,
* <tt>false</tt> otherwise
*/
boolean isChatEnabled() {
return _chatEnabled;
}
/**
* Returns whether or not browse is enabled for this upload.
*/
boolean isBrowseEnabled() {
return _browseEnabled;
}
/**
* Updates all of the data for this upload, obtaining fresh information
* from the contained <tt>Uploader</tt> instance.
* @implements DataLine interface
*/
public void update() {
// do not change the display if we are at an intermediary
// complete or connecting state.
// (meaning that this particular chunk finished, but more will come)
// we use _endTime to tell us when it's finished, because that is
// set when remove is called, which is only called when the entire
// upload has finished.
// we use getTotalAmountUploaded to know if a byte has been read
// (which would mean we're not connecting anymore)
int state = UPLOADER.getState();
int lastState = UPLOADER.getLastTransferState();
if ( (state == Uploader.COMPLETE && _endTime == -1) ||
(state == Uploader.CONNECTING &&
UPLOADER.getTotalAmountUploaded() != 0)
) {
state = lastState;
}
// Reset the current state to be the lastState if we're complete now,
// but our last transfer wasn't uploading, queued, or thex.
if(state == Uploader.COMPLETE &&
lastState != Uploader.UPLOADING &&
lastState != Uploader.QUEUED &&
lastState != Uploader.THEX_REQUEST) {
state = lastState;
}
_speed = -1;
_timeLeft = 0;
this.updateStatus(state);
}
/**
* Updates the status of the upload based on the state stored in the
* <tt>Uploader</tt> instance for this <tt>UploadDataLine</tt>.
*/
private void updateStatus(int state) {
_state = state;
switch (_state) {
case Uploader.CONNECTING:
_status = CONNECTING_STATE;
break;
case Uploader.FREELOADER:
_status = FREELOADER_STATE;
break;
case Uploader.COMPLETE:
//must set progress for the case of when
//an upload completes when someone isn't watching the screen
//when they come back to it, it would have displayed as 0%
//since that's the first update to the dataline.
if ( _status != COMPLETE_STATE ) {
setProgress();
if ( _progress == 99 )
_progress = 100;
}
_status = COMPLETE_STATE;
break;
case Uploader.UNAVAILABLE_RANGE:
_status = UNAVAILABLE_RANGE_STATE;
break;
case Uploader.MALFORMED_REQUEST:
_status = MALFORMED_REQUEST_STATE;
break;
case Uploader.NOT_VALIDATED:
_status = VALIDATING_STATE;
break;
case Uploader.LIMIT_REACHED:
_status = LIMIT_REACHED_STATE;
break;
case Uploader.INTERRUPTED:
//must set progress for the case of when
//an upload completes when someone isn't watching the screen
//when they come back to it, it would have displayed as 0%
//since that's the first update to the dataline.
if ( _status != INTERRUPTED_STATE )
setProgress();
_status = INTERRUPTED_STATE;
break;
case Uploader.FILE_NOT_FOUND:
_status = FILE_NOT_FOUND_STATE;
break;
case Uploader.THEX_REQUEST:
_status = HASH_TREE_STATE;
setProgress();
setSpeedAndTimeLeft();
break;
case Uploader.UPLOADING:
_status = UPLOADING_STATE;
setProgress();
setSpeedAndTimeLeft();
break;
case Uploader.BROWSE_HOST:
Assert.that(false, "Browse Host status in GUI Upload view");
break;
case Uploader.QUEUED:
_status = QUEUED_STATE + " (" +
(UPLOADER.getQueuePosition() + 1) + ")";
setProgress();
break;
case Uploader.BANNED_GREEDY:
_status = BANNED_GREEDY_STATE;
break;
default:
Assert.that(false,
"Unknown status "+UPLOADER.getState()+" of uploader");
}
}
/**
* Sets the speed & time left.
*/
private void setSpeedAndTimeLeft() {
try {
_speed = (double)UPLOADER.getMeasuredBandwidth();
} catch(InsufficientDataException ide) {
_speed = 0;
}
// If we have a valid rate (can't compute if rate is 0),
// then determine how much time (in seconds) is remaining.
if ( _speed > 0) {
double kbLeft = (
(double)getLength() -
(double)UPLOADER.getTotalAmountUploaded()
) / 1024.0;
_timeLeft = (int)(kbLeft / _speed);
}
}
/**
* Set the _progress variable based on the cumulative amount
* read, current amount uploaded & the filesize.
*/
private void setProgress() {
double d = (double)(UPLOADER.getTotalAmountUploaded())/(double)getLength();
_progress = (int)Math.round(d*100);
}
/**
* Returns whether or not this upload is in what is considered an "inactive"
* state, such as completeed, aborted, failed, etc.
*
* @return <tt>true</tt> if this upload is in an inactive state,
* <tt>false</tt> otherwise
*/
boolean isInactive() {
//The upload is active up until 'remove' has been called on it.
return _endTime != -1;
}
/**
* Returns whether or not the upload for this line is currently uploading
*
* @return <tt>true</tt> if this upload is currently uploading,
* <tt>false</tt> otherwise
*/
boolean isUploading() {
return _state == Uploader.UPLOADING;
}
/**
* Updates the connection persistance. Changes how cleanup() works.
*/
void setPersistConnection(boolean persist) {
_persistConnection = persist;
}
/**
* Sets the time this upload finished.
* When this is called, we create a fake "Uploader" object so that the real
* Uploader can have all its references garbage collected. Otherwise, we
* can end up holding too many things in memory.
*/
void setEndTime(long time) {
_endTime = time;
UPLOADER = new FakeUploader(UPLOADER);
super.initialize(UPLOADER);
}
private static class FakeUploader implements Uploader {
private final int idx;
private final long tUp;
private final int gPort;
private final float mBand;
private final float aBand;
private final String name;
private final long size;
private final String host;
private final int state;
private final String agent;
private final boolean chat;
private final boolean browse;
private final int lastState;
private final FileDesc fd;
FakeUploader(Uploader u) {
idx = u.getIndex();
tUp = u.getTotalAmountUploaded();
gPort = u.getGnutellaPort();
float bandwidth;
try {
bandwidth = u.getMeasuredBandwidth();
} catch(InsufficientDataException e) {
bandwidth = 0;
}
mBand = bandwidth;
aBand = u.getAverageBandwidth();
name = u.getFileName();
size = u.getFileSize();
host = u.getHost();
state = u.getState();
chat = u.isChatEnabled();
browse = u.isBrowseHostEnabled();
agent = u.getUserAgent();
lastState = u.getLastTransferState();
fd = u.getFileDesc();
}
public void stop() { }
public String getFileName() { return name; }
public long getFileSize() { return size; }
public long getAmountRequested() { return 0; }
public FileDesc getFileDesc() { return fd; }
public int getIndex() { return idx; }
public long amountUploaded() { return 0; }
public long getTotalAmountUploaded() { return tUp; }
public String getHost() { return host; }
public int getState() { return state; }
public int getLastTransferState() { return lastState; }
public void setState(int s) { }
public void writeResponse() { }
public boolean isChatEnabled() { return chat; }
public boolean isBrowseHostEnabled() { return browse; }
public int getGnutellaPort() { return gPort; }
public String getUserAgent() { return agent; }
public boolean isHeaderParsed() { return true; }
public boolean supportsQueueing() { return false; }
public HTTPRequestMethod getMethod() { return HTTPRequestMethod.HEAD; }
public int getQueuePosition() { return -1; }
public boolean isInactive() { return true; }
public void measureBandwidth() { }
public float getMeasuredBandwidth() { return mBand; }
public float getAverageBandwidth() { return aBand; }
}
}