/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* PowerFolder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
package de.dal33t.powerfolder.transfer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Date;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.disk.Folder;
import de.dal33t.powerfolder.light.FileInfo;
import de.dal33t.powerfolder.light.MemberInfo;
import de.dal33t.powerfolder.util.Reject;
import de.dal33t.powerfolder.util.TransferCounter;
import de.dal33t.powerfolder.util.logging.Loggable;
/**
* Abstract version of a Transfer.<BR>
* Serializable for remembering completed Downloads in DownLoadTableModel.
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a>
* @version $Revision: 1.14 $
*/
public abstract class Transfer extends Loggable implements Serializable {
private static final long serialVersionUID = 100L;
private transient TransferManager transferManager;
private transient Member partner;
private MemberInfo partnerInfo;
private FileInfo file;
private Date startTime;
// time where this transfer was initialized
private Date initTime;
private long startOffset;
private TransferCounter counter;
// Details of the latest transfer problem.
private TransferProblem transferProblem;
private String problemInformation;
protected final State state = new State();
public enum TransferState {
NONE("None", 10), // NONE WHAT DO YOU THINK?
FILERECORD_REQUEST("transfers.requested", 20), // DOWNLOAD
FILEHASHING("transfers.hashing", 30), // UPLOAD only
REMOTEMATCHING("transfers.remote_hashing", 40), // UPLOAD only
DOWNLOADING("Downloading", 50), // DOWNLOAD only
UPLOADING("Uploading", 60), // UPLOAD only
MATCHING("transfers.hashing", 70), // DOWNLOAD ONLY
COPYING("transfers.copying", 80), // DOWNLOAD ONLY
VERIFYING("transfers.verifying", 90), // DOWNLOAD ONLY
DONE("transfers.completed", 100);
private int orderIndex;
public int getOrderIndex() {
return orderIndex;
}
private String translationId;
TransferState(String key, int orderIndex) {
this.translationId = key;
this.orderIndex = orderIndex;
}
public String getTranslationId() {
return translationId;
}
}
public static class State implements Serializable, Comparable<State> {
private static final long serialVersionUID = 100L;
private TransferState state = TransferState.NONE;
private double progress = -1;
private Date completedDate;
public synchronized TransferState getState() {
return state;
}
public synchronized void setState(TransferState state) {
if (!this.state.equals(state)) {
progress = -1;
this.state = state;
if (state.equals(TransferState.DONE)) {
completedDate = new Date();
}
}
}
/**
* Sets the progress of the current state (0 till 1). Values < 0
* indicate that no measurement is possible
*
* @param progress
*/
public synchronized void setProgress(double progress) {
if (progress > 1) {
this.progress = 1;
} else {
this.progress = progress;
}
}
/**
* Gets the progress of the current state (0 till 1). Values < 0
* indicate that no measurement is possible
*
* @return the progress in percentage or a value < 0 if that's not
* possible
*/
public synchronized double getProgress() {
return progress;
}
/**
* @return the date the transfer completed.
*/
public Date getCompletedDate() {
return completedDate;
}
public int compareTo(State o) {
int comp = Integer.valueOf(state.orderIndex).compareTo(
o.state.orderIndex);
if (comp == 0) {
// Same state. Compare by progress
comp = Double.valueOf(progress).compareTo(o.progress);
}
return comp;
}
}
/** for Serialization */
public Transfer() {
}
/**
* Initializes a new Transfer
*
* @param transferManager
* @param file
* @param partner
*/
protected Transfer(TransferManager transferManager, FileInfo file,
Member partner)
{
Reject.ifNull(transferManager, "TransferManager is null");
Reject.ifNull(file, "FileInfo is null");
this.transferManager = transferManager;
this.file = file;
setPartner(partner);
this.initTime = new Date();
}
/**
* Re-initalized the Transfer with the TransferManager. Use this only if you
* are know what you are doing.
*
* @param aTransferManager
*/
void init(TransferManager aTransferManager) {
if (transferManager != null) {
logSevere("Unable to set TransferManager. Having already one. "
+ this);
return;
}
this.transferManager = aTransferManager;
if (this.partnerInfo != null) {
this.partner = this.partnerInfo.getNode(getController(), true);
} else {
this.partner = null;
}
this.startTime = null;
// FIX for #878
if (isCompleted()) {
state.setProgress(1);
}
}
/**
* @return the file.
*/
public FileInfo getFile() {
return file;
}
/**
* maybe null if loaded from serialized file
*
* @return the partner
*/
public final Member getPartner() {
if (partner == null && partnerInfo != null) {
return partnerInfo.getNode(getController(), true);
}
return partner;
}
/**
* Sets the parter for this download
*
* @param aPartner
*/
protected final void setPartner(Member aPartner) {
this.partner = aPartner;
if (partner != null) {
this.partnerInfo = aPartner.getInfo();
} else {
this.partnerInfo = null;
}
}
void shutdown() {
}
void setCompleted() {
// Set final state.
state.setState(TransferState.DONE);
state.setProgress(1);
}
/**
* @return the time of the transfer start
*/
public final Date getStartTime() {
return startTime;
}
/**
* Answers if this transfer has already started
*
* @return true if started
*/
public boolean isStarted() {
return startTime != null;
}
/**
* Sets this transfer as started
*/
protected synchronized void setStarted() {
boolean wasStarted = isStarted();
if (!wasStarted) {
// Start now
startTime = new Date();
getTransferManager().setStarted(this);
} else {
logWarning("Got already started transfer", new RuntimeException(
"from here"));
}
}
protected TransferManager getTransferManager() {
return transferManager;
}
protected Controller getController() {
return transferManager.getController();
}
/**
* @return the time when the transfer was initalized/constructed
*/
protected Date getInitTime() {
return initTime;
}
/**
* Sets the startoffset for this transfer
*
* @param startOffset
*/
protected void setStartOffset(long startOffset) {
this.startOffset = startOffset;
}
/**
* @return the start offset
*/
public long getStartOffset() {
return startOffset;
}
/**
* @return if this download is completed
*/
public boolean isCompleted() {
return state != null && state.getState() != null
&& state.getState().equals(TransferState.DONE);
}
public Date getCompletedDate() {
return state != null && state.getCompletedDate() != null ? state
.getCompletedDate() : null;
}
/**
* @return the transfer counter
*/
public TransferCounter getCounter() {
if (counter == null) {
counter = new TransferCounter(getStartOffset(), getFile().getSize());
}
return counter;
}
/**
* @return if this transfer is broken, override, but call super
*/
public boolean isBroken() {
if (isCompleted()) {
// The transfer is not broken
return false;
}
if (getPartner() == null) {
if (isFine()) {
logFine("Break cause: partner is null.");
}
return true;
}
if (!getPartner().isCompletelyConnected()) {
if (isFine()) {
logFine("Break cause: " + getPartner().getNick()
+ " not connected.");
}
return true;
}
boolean partnerOnFolder = stillPartnerOnFolder();
if (!partnerOnFolder) {
// broken if partner left folder
if (isFine()) {
logFine("Break cause: " + getPartner().getNick()
+ " not on folder.");
}
return true;
}
// Not done here, may cause too much CPU.
// #2532: Done in TransferManager.checkActiveTranfersForExcludes();
// Folder folder = getFile().getFolder(
// getController().getFolderRepository());
// if (folder != null) {
// if (folder.getDiskItemFilter().isExcluded(getFile())) {
// return true;
// }
// }
return false;
}
/**
* @return if this transfer is still queued at the remote side. if not this
* transfer should be set broken
*/
protected boolean stillQueuedAtPartner() {
if (getPartner() == null) {
return false;
}
return getPartner().isConnected();
}
/**
* @return if the partner still on the destination folder of file
*/
protected boolean stillPartnerOnFolder() {
Folder folder = getFile().getFolder(
getController().getFolderRepository());
if (folder == null) {
// folder not found
return false;
}
return folder.hasMember(getPartner());
}
/**
* Gets the latest transfer problem.
*
* @return the latest transfer problem
*/
public TransferProblem getTransferProblem() {
return transferProblem;
}
/**
* Sets the latest transfer problem.
*
* @param transferProblem
* the transfer problem
*/
public void setTransferProblem(TransferProblem transferProblem) {
this.transferProblem = transferProblem;
}
/**
* Gets additional information about the latest transfer problem.
*
* @return the latest transfer problem information
*/
public String getProblemInformation() {
return problemInformation;
}
/**
* Sets additional information about the latest transfer problem.
*
* @param problemInformation
* the latest transfer problem information
*/
public void setProblemInformation(String problemInformation) {
this.problemInformation = problemInformation;
}
public State getState() {
return state;
}
public TransferState getTransferState() {
return state == null ? null : state.getState();
}
public double getStateProgress() {
return state == null ? 0 : state.getProgress();
}
//
// // General
// ****************************************************************
//
// @Override
// public String getLoggerName() {
// return getClass().getSimpleName() + " '" +
// getRelativeFile().getFilenameOnly()
// + "'";
// }
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException
{
in.defaultReadObject();
this.partnerInfo = partnerInfo != null ? partnerInfo.intern() : null;
}
}