package net.sourceforge.transfile.operations;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import net.sourceforge.transfile.operations.messages.Message;
import net.sourceforge.transfile.operations.messages.OperationMessage;
import net.sourceforge.transfile.operations.messages.StateMessage;
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-05)
*
*/
public abstract class AbstractOperation implements Operation {
private final Collection<Listener> listeners;
private final Connection connection;
private final String fileName;
private State state;
private double progress;
private File localFile;
/**
*
* @param connection
* <br>Should not be null
* <br>Shared parameter
* @param fileName
* <br>Should not be null
* <br>Shared parameter
*/
public AbstractOperation(final Connection connection, final String fileName) {
this.listeners = new ArrayList<Listener>();
this.connection = connection;
this.fileName = fileName;
this.state = State.QUEUED;
}
@Override
public final synchronized Connection getConnection() {
return this.connection;
}
/**
*
* @return
* <br>A non-null value
* <br>A new value
*/
public final synchronized Listener[] getListeners() {
return this.listeners.toArray(new Listener[this.listeners.size()]);
}
@Override
public final synchronized void addOperationListener(final Listener listener) {
this.listeners.add(listener);
}
@Override
public final synchronized void removeOperationListener(final Listener listener) {
this.listeners.remove(listener);
}
@Override
public final synchronized String getFileName() {
return this.fileName;
}
@Override
public final synchronized File getLocalFile() {
return this.localFile;
}
@Override
public final synchronized double getProgress() {
return this.progress;
}
@Override
public final synchronized State getState() {
return this.state;
}
@Override
public final synchronized void setLocalFile(final File localFile) {
this.localFile = localFile;
}
/**
*
* @param progress
* <br>Range: {@code [0.0 .. 1.0]}
*/
public final void setProgress(final double progress) {
if (this.getProgress() != progress) {
synchronized (this) {
this.progress = progress;
}
for (final Listener listener : this.getListeners()) {
listener.progressChanged();
}
}
}
/**
*
* @param state
* <br>Should not be null
* <br>Shared parameter
*/
public final void setState(final State state) {
if (this.getState() != state) {
synchronized (this) {
this.state = state;
}
for (final Listener listener : this.getListeners()) {
listener.stateChanged();
}
}
}
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-06)
*
*/
protected abstract class AbstractController implements Controller {
private final Connection.Listener messageHandler;
private State remoteState;
public AbstractController() {
this.messageHandler = this.new MessageHandler();
AbstractOperation.this.addOperationListener(this.new StateMessageSender());
}
/**
* {@inheritDoc}
*/
@Override
public final void cancel() {
AbstractOperation.this.setState(State.CANCELED);
}
/**
* {@inheritDoc}
*/
@Override
public final void done() {
AbstractOperation.this.setState(State.DONE);
}
/**
* {@inheritDoc}
*/
@Override
public final void pause() {
AbstractOperation.this.setState(State.PAUSED);
}
/**
* {@inheritDoc}
*/
@Override
public final void remove() {
AbstractOperation.this.setState(State.REMOVED);
AbstractOperation.this.getConnection().removeConnectionListener(this.messageHandler);
}
/**
* {@inheritDoc}
*/
@Override
public final void start() {
if (this.canStart()) {
AbstractOperation.this.setState(State.PROGRESSING);
}
}
/**
*
* @return
* <br>A possibly null value
* <br>A shared value
*/
public final synchronized State getRemoteState() {
return this.remoteState;
}
public final boolean canTransferData() {
return AbstractOperation.this.getState() == this.getRemoteState() && AbstractOperation.this.getState() == State.PROGRESSING;
}
/**
*
* @param remoteState
* <br>Can be null
* <br>Shared parameter
*/
final synchronized void setRemoteState(final State remoteState) {
this.remoteState = remoteState;
if (this.getRemoteState() == State.PAUSED || this.getRemoteState() == State.CANCELED) {
AbstractOperation.this.setState(this.getRemoteState());
}
}
protected boolean canStart() {
return true;
}
/**
* TODO doc
*
* @param operationMessage
* <br>Should not be null
*/
protected void operationMessageReceived(final OperationMessage operationMessage) {
// Default implementation, do nothing
}
/**
* TODO doc
*
* @return
* <br>Should not be null
* <br>A shared value
*/
protected File getSourceFile() {
return AbstractOperation.this.getLocalFile();
}
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-07)
*
*/
private class StateMessageSender implements Listener {
/**
* Package-private default constructor to suppress visibility warnings.
*/
StateMessageSender() {
// Do nothing
}
@Override
public final void stateChanged() {
final Operation operation = AbstractOperation.this;
operation.getConnection().sendMessage(
new StateMessage(AbstractController.this.getSourceFile(), operation.getState()));
}
@Override
public final void progressChanged() {
// Do nothing
}
}
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-06)
*
*/
private class MessageHandler extends Connection.AbstractListener {
MessageHandler() {
AbstractOperation.this.getConnection().addConnectionListener(this);
}
@Override
protected final void doMessageReceived(final Message message) {
if (message instanceof OperationMessage && ((OperationMessage) message).getSourceFile().equals(AbstractController.this.getSourceFile())) {
if (message instanceof StateMessage) {
AbstractController.this.setRemoteState(((StateMessage) message).getState());
}
AbstractController.this.operationMessageReceived((OperationMessage) message);
}
}
}
}
}