/* * 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.FileOutputStream; import net.sourceforge.transfile.operations.messages.DataOfferMessage; import net.sourceforge.transfile.operations.messages.DataRequestMessage; import net.sourceforge.transfile.operations.messages.FileOfferMessage; import net.sourceforge.transfile.operations.messages.OperationMessage; /** * TODO doc * * @author codistmonk (creation 2010-06-05) * */ public class ReceiveOperation extends AbstractOperation { private final Controller controller; private final FileOfferMessage fileOffer; private final DestinationFileProvider destinationFileProvider; /** * * @param connection * <br>Should not be null * <br>Shared parameter * @param fileOffer * <br>Should not be null * <br>Shared parameter * @param destinationFileProvider * <br>Should not be null * <br>Shared parameter */ public ReceiveOperation(final Connection connection, final FileOfferMessage fileOffer, final DestinationFileProvider destinationFileProvider) { super(connection, fileOffer.getSourceFile().getName()); this.fileOffer = fileOffer; this.controller = this.new Controller(); this.destinationFileProvider = destinationFileProvider; } /** * * @return the file offer message used to create this operation * <br>A non-null value * <br>A shared value */ public final FileOfferMessage getFileOffer() { return this.fileOffer; } /** * {@inheritDoc} */ @Override public final Operation.Controller getController() { return this.controller; } /** * * @return * <br>A non-null value * <br>A shared value */ public final DestinationFileProvider getDestinationFileProvider() { return this.destinationFileProvider; } /** * * TODO doc * * @author codistmonk (2010-06-06) * */ private class Controller extends AbstractController { private long receivedByteCount; /** * Package-private default constructor to suppress visibility warnings. */ Controller() { ReceiveOperation.this.addOperationListener(new Listener() { @Override public void stateChanged() { if (Controller.this.canTransferData()) { Controller.this.requestData(); } } @Override public final void progressChanged() { // Do nothing } }); } @Override protected final boolean canStart() { final ReceiveOperation receiveOperation = ReceiveOperation.this; if (receiveOperation.getLocalFile() == null) { receiveOperation.setLocalFile( receiveOperation.getDestinationFileProvider().getDestinationFile(receiveOperation.getFileName())); } return receiveOperation.getLocalFile() != null; } @Override protected final void operationMessageReceived(final OperationMessage operationMessage) { if (operationMessage instanceof DataOfferMessage && this.canTransferData()) { if (ReceiveOperation.this.getLocalFile() == null) { throw new IllegalStateException("Destination file is null but the following message has been received: " + operationMessage); } try { FileOutputStream output = null; try { output = new FileOutputStream(ReceiveOperation.this.getLocalFile(), true); final byte[] data = ((DataOfferMessage) operationMessage).getBytes(); output.write(data); this.dataReceived(data.length, ReceiveOperation.this.getFileOffer().getSourceByteCount()); } finally { if (output != null) { output.close(); } } } catch (final Exception exception) { // TODO better error handling exception.printStackTrace(); } } if (!(operationMessage instanceof DataRequestMessage) && this.canTransferData()) { this.requestData(); } } @Override protected final File getSourceFile() { return ReceiveOperation.this.getFileOffer().getSourceFile(); } final void requestData() { if (this.canTransferData()) { ReceiveOperation.this.getConnection().sendMessage(new DataRequestMessage(this.getSourceFile(), this.receivedByteCount, PREFERRED_TRANSFERRED_BYTE_COUNT)); } } /** * TODO doc * * @param byteCount * <br>Range: {@code [0 .. totalByteCount - this.sentByteCount]} * @param totalByteCount * <br>Range: {@code [0 .. Long.MAX_VALUE]} */ private final void dataReceived(final int byteCount, final long totalByteCount) { this.receivedByteCount += byteCount; ReceiveOperation.this.setProgress((double) this.receivedByteCount / totalByteCount); if (this.receivedByteCount == totalByteCount) { ReceiveOperation.this.getConnection().sendMessage(new DataRequestMessage(this.getSourceFile(), this.receivedByteCount, PREFERRED_TRANSFERRED_BYTE_COUNT)); this.done(); } } } public static final int PREFERRED_TRANSFERRED_BYTE_COUNT = 1; /** * TODO doc * * @author codistmonk (creation 2010-06-06) * */ public static interface DestinationFileProvider { /** * TODO doc * * @param fileName * <br>Should not be null * @return * <br>A possibly null value */ public abstract File getDestinationFile(String fileName); } }