/*
* Copyright © 2010 Martin Riedel
*
* This file is part of TransFile.
*
* TransFile 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, either version 3 of the License, or
* (at your option) any later version.
*
* TransFile 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 TransFile. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.transfile.operations;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import net.sourceforge.transfile.operations.messages.DataOfferMessage;
import net.sourceforge.transfile.operations.messages.DataRequestMessage;
import net.sourceforge.transfile.operations.messages.OperationMessage;
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-05)
*
*/
public class SendOperation extends AbstractOperation {
private final Controller controller;
/**
*
* @param connection
* <br>Should not be null
* <br>Shared parameter
* @param sourceFile
* <br>Should not be null
* <br>Shared parameter
*/
public SendOperation(final Connection connection, final File sourceFile) {
super(connection, sourceFile.getName());
this.controller = this.new Controller();
this.setLocalFile(sourceFile);
}
/**
* {@inheritDoc}
*/
@Override
public final Operation.Controller getController() {
return this.controller;
}
/**
* TODO doc
*
* @author codistmonk (creation 2010-06-05)
*
*/
private class Controller extends AbstractController {
/**
* Package-private default constructor to suppress visibility warnings.
*/
Controller() {
// Do nothing
}
@Override
protected final void operationMessageReceived(final OperationMessage operationMessage) {
if (operationMessage instanceof DataRequestMessage && this.canTransferData()) {
final DataRequestMessage request = (DataRequestMessage) operationMessage;
this.dataReceived(request.getFirstByteOffset(), this.getSourceFile().length());
if (request.getRequestedByteCount() > 0) {
try {
this.reply(request);
} catch (final Exception exception) {
// TODO better error handling
exception.printStackTrace();
}
}
}
}
/**
* TODO doc
*
* @param request
* <br>Should not be null
* @throws IOException if the source file doesn't exist or cannot be read
*/
private final void reply(final DataRequestMessage request) throws IOException {
final byte[] buffer = new byte[request.getRequestedByteCount()];
final int readByteCount = this.readBytes(request.getFirstByteOffset(), buffer);
if (readByteCount > 0) {
SendOperation.this.getConnection().sendMessage(new DataOfferMessage(
this.getSourceFile(),
request.getFirstByteOffset(),
Arrays.copyOfRange(buffer, 0, readByteCount)));
}
}
/**
* TODO doc
*
* @param firstByteOffset
* <br>Range: {@code [0L .. Long.MAX_VALUE]}
* @param buffer
* <br>Should not be null
* <br>Input-output parameter
* @return the total number of bytes read into the buffer, or {@code -1} if there is no more data because the end of the file has been reached
* <br>Range: {@code [-1 .. buffer.length]}
* @throws IOException if the source file doesn't exist or cannot be read
*/
private final int readBytes(final long firstByteOffset, final byte[] buffer) throws IOException {
FileInputStream input = null;
try {
input = new FileInputStream(this.getSourceFile());
input.skip(firstByteOffset);
return input.read(buffer);
} finally {
if (input != null) {
input.close();
}
}
}
/**
* TODO doc
*
* @param byteCount
* <br>Range: {@code [0L .. totalByteCount]}
* @param totalByteCount
* <br>Range: {@code [0L .. Long.MAX_VALUE]}
*/
private final void dataReceived(final long byteCount, final long totalByteCount) {
SendOperation.this.setProgress((double) byteCount / totalByteCount);
if (byteCount == totalByteCount) {
this.done();
}
}
}
}