/*
* Copyright 2011-2012 Amazon Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://aws.amazon.com/apache2.0
*
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and
* limitations under the License.
*/
package com.amazonaws.eclipse.explorer.s3.dnd;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.navigator.CommonDropAdapter;
import org.eclipse.ui.navigator.CommonDropAdapterAssistant;
import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
/**
* Handles dropping an S3 key onto a supported resource in the project explorer.
*/
public class DownloadDropAssistant extends CommonDropAdapterAssistant {
public DownloadDropAssistant() {
}
/**
* All validation is left to the configuration in plugin.xml. Any match
* between possibleChildren and possibleDropTargets will be considered
* valid.
*/
@Override
public IStatus validateDrop(Object target, int operation, TransferData transferType) {
return validatePluginTransferDrop((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection(), target);
}
@Override
public IStatus validatePluginTransferDrop(IStructuredSelection aDragSelection, Object aDropTarget) {
if (aDropTarget instanceof IResource)
return Status.OK_STATUS;
else
return Status.CANCEL_STATUS;
}
@Override
public IStatus handlePluginTransferDrop(IStructuredSelection aDragSelection, Object aDropTarget) {
return doDrop(aDropTarget, aDragSelection);
}
@Override
public IStatus handleDrop(CommonDropAdapter aDropAdapter, final DropTargetEvent aDropTargetEvent, Object aTarget) {
if ( aDropTargetEvent.data instanceof StructuredSelection ) {
IStructuredSelection s3ObjectSelection = (StructuredSelection) aDropTargetEvent.data;
return doDrop(aTarget, s3ObjectSelection);
}
return Status.OK_STATUS;
}
protected IStatus doDrop(Object aTarget, IStructuredSelection s3ObjectSelection) {
if ( !(aTarget instanceof IResource) ) {
return Status.CANCEL_STATUS;
}
// Drop targets can be folders, projects, or files. In the case of
// files, we just want to identify the parent directory.
IResource resource = (IResource) aTarget;
if ( resource instanceof IFile ) {
resource = resource.getParent();
}
final IResource dropFolder = resource;
final S3ObjectSummary s3object = (S3ObjectSummary) s3ObjectSelection
.getFirstElement();
final File f = dropFolder.getLocation().toFile();
if ( !f.exists() )
return Status.CANCEL_STATUS;
String fileName = getOutputFileName(s3object, f);
if ( fileName == null || fileName.length() == 0 ) {
return Status.CANCEL_STATUS;
}
final File outputFile = new File(fileName);
new DownloadObjectJob("Downloading " + s3object.getKey(), s3object, dropFolder, outputFile).schedule();
return Status.OK_STATUS;
}
/**
* Opens a save-file dialog to prompt the user for a file name.
*/
private String getOutputFileName(final S3ObjectSummary s3object, final File f) {
FileDialog dialog = new FileDialog(Display.getDefault().getActiveShell(), SWT.SAVE);
dialog.setFilterPath(f.getAbsolutePath());
dialog.setFileName(s3object.getKey());
dialog.setOverwrite(true);
String fileName = dialog.open();
return fileName;
}
/**
* Async job to download an object from S3
*/
private final class DownloadObjectJob extends Job {
private final S3ObjectSummary s3object;
private final IResource dropFolder;
private final File outputFile;
private DownloadObjectJob(String name, S3ObjectSummary s3object, IResource dropFolder, File outputFile) {
super(name);
this.s3object = s3object;
this.dropFolder = dropFolder;
this.outputFile = outputFile;
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
FileOutputStream fos = null;
try {
AmazonS3 client = AwsToolkitCore.getClientFactory().getS3ClientForBucket(s3object.getBucketName());
S3Object object = client.getObject(s3object.getBucketName(), s3object.getKey());
// This number is used for reporting only; the download
// will appear to complete early if the file is bigger
// than 2GB.
long totalNumBytes = object.getObjectMetadata().getContentLength();
if ( totalNumBytes > Integer.MAX_VALUE )
totalNumBytes = Integer.MAX_VALUE;
monitor.beginTask("Downloading", (int) totalNumBytes);
// For a new file this is a no-op, but it truncates an
// existing file for overwrite.
RandomAccessFile raf = new RandomAccessFile(outputFile, "rw");
raf.setLength(0);
raf.close();
fos = new FileOutputStream(outputFile);
InputStream is = object.getObjectContent();
byte[] buffer = new byte[4096];
int bytesRead = 0;
while ( (bytesRead = is.read(buffer)) > 0 ) {
fos.write(buffer, 0, bytesRead);
monitor.worked(bytesRead);
}
} catch ( Exception e ) {
return new Status(Status.ERROR, AwsToolkitCore.PLUGIN_ID, "Error downloading file from S3", e);
} finally {
if ( fos != null ) {
try {
fos.close();
} catch ( Exception e ) {
AwsToolkitCore.getDefault().logException("Couldn't close file output stream", e);
}
}
monitor.done();
}
// Refresh the drop folder
// TODO: this won't work if they chose another folder in the
// file selection dialog.
Display.getDefault().asyncExec(new Runnable() {
public void run() {
try {
dropFolder.refreshLocal(1, monitor);
} catch ( CoreException e ) {
AwsToolkitCore.getDefault().logException("Couldn't refresh local files", e);
}
}
});
return Status.OK_STATUS;
}
}
}