package com.limegroup.gnutella.gui.download;
import java.awt.Component;
import java.awt.Dialog;
import java.io.File;
import java.text.MessageFormat;
import javax.swing.JOptionPane;
import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.FileDesc;
import com.limegroup.gnutella.IncompleteFileDesc;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.SaveLocationException;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.browser.MagnetOptions;
import com.limegroup.gnutella.gui.FileChooserHandler;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.MessageService;
import com.limegroup.gnutella.settings.QuestionsHandler;
import com.limegroup.gnutella.util.CommonUtils;
/**
* Static helper class that kicks of downloads handling all the necessary
* consistency check and showing appropriate error/warning dialogs.
*/
public class DownloaderUtils {
/**
* Tries to create a downloader for a factory performing the following
* consistency checks beforehand:
* <ul>
* <li>{@link #isAlreadyDownloading(DownloaderFactory)}
* <li>{@link #isSaveLocationTaken(DownloaderFactory)}
* <li>if the proposed save location is not taken
* {@link #continueWithOrWithoutHashConflict(DownloaderFactory)} is
* performed
* </ul>
*
* @param factory
* @return <code>null</code> if there is another download for the same
* hash, or incomplete file, or the user cancelled the download at
* some point.
*/
public static Downloader createDownloader(DownloaderFactory factory) {
// check for already downloading conflicts
if (isAlreadyDownloading(factory)) {
return null;
}
// check for file name conflicts
if (!isSaveLocationTaken(factory)) {
// check for hash conflicts
if (!continueWithOrWithoutHashConflict(factory)) {
return null;
}
}
// try to start download
return createDownloader(factory, false);
}
/**
* Tries to create a downloader for a factory asking the user for a save
* location for the download.
* <p>
* Performs the following consistency checks:
* <ul>
* <li>{@link #isAlreadyDownloading(DownloaderFactory)}
* <li>{@link #continueWithOrWithoutHashConflict(DownloaderFactory)}
* </ul>
*
* @param factory
* @return <code>null</code> if there is another download for the same
* hash or incomplete file, or the user cancelled somewhere along
* the way.
*/
public static Downloader createDownloaderAs(DownloaderFactory factory) {
// check for already downloading conflicts
if (isAlreadyDownloading(factory)) {
return null;
}
// check for hash conflicts
if (!continueWithOrWithoutHashConflict(factory)) {
return null;
}
File file = showFileChooser(factory, MessageService
.getParentComponent());
if (file == null) {
return null;
}
factory.setSaveFile(file);
// OSX's FileDialog box already prompts the user that they're
// going to be overwriting a file, so we don't need to do that
// particular check again.
return createDownloader(factory, CommonUtils.isAnyMac());
}
/**
* Tries to create a downloader for a magnet.
* <p>
* The magnet may also be {@link MagnetOptions#isDownloadable() not valid
* for downloading}, then a warning is displayed.
* @param magnet
* @return <code>null</code> if the magnet is is invalid or the user
* cancelled the process
*/
public static Downloader createDownloader(MagnetOptions magnet) {
String msg = magnet.getErrorMessage();
if (!magnet.isDownloadable()) {
if (msg == null) {
msg = magnet.toString();
}
// show warning and return
GUIMediator.showError("ERROR_BAD_MAGNET_LINK", msg);
return null;
}
if (msg != null) {
// show warning but proceed
GUIMediator.showWarning("ERROR_INVALID_URLS_IN_MAGNET");
}
MagnetDownloaderFactory factory = new MagnetDownloaderFactory(magnet);
if (magnet.getDisplayName() == null) {
Downloader dl = createDownloaderAs(factory);
if (dl != null && magnet.isHashOnly()) {
GUIMediator.showError("DOWNLOAD_HASH_ONLY_MAGNET");
}
return dl;
}
else {
return createDownloader(factory);
}
}
/**
* Tries to create a downloader from a factory.
* <p>
* If the {@link DownloaderFactory#createDownloader(boolean)} throws an
* exception, {@link DownloaderDialog#handle(DownloaderFactory,
* SaveLocationException)} is called to handle it.
* <p>
* If the process was successful, the final file is shared individually if
* it's not in a shared directory.
*
* @param factory
* @param overwrite
* @return <code>null</null> if the user cancelled at some point
*/
public static Downloader createDownloader(DownloaderFactory factory,
boolean overwrite) {
try {
return factory.createDownloader(overwrite);
} catch (SaveLocationException sle) {
return DownloaderDialog.handle(factory, sle);
}
}
/**
* Checks if there is a conflicting download already running with the same
* hash or incomplete file name, shows a notification dialog and returns
* true.
*
* @param factory
* @return
*/
public static boolean isAlreadyDownloading(DownloaderFactory factory) {
if (RouterService.getDownloadManager().conflicts(factory.getURN(),
factory.getSaveFile().getName(), factory.getFileSize())) {
showIsAlreadyDownloadingWarning(factory);
return true;
}
return false;
}
public static void showIsAlreadyDownloadingWarning(DownloaderFactory factory) {
GUIMediator.showFormattedError(
"FORMATTED_ERROR_ALREADY_DOWNLOADING",
new Object[] { factory.getSaveFile() },
QuestionsHandler.ALREADY_DOWNLOADING);
}
/**
* Returns a non-incomplete FileDesc for the urn or null.
*
* @param urn
* @return
*/
public static FileDesc getFromLibrary(URN urn) {
if (urn == null) {
return null;
}
FileDesc desc = RouterService.getFileManager().getFileDescForUrn(urn);
return (desc instanceof IncompleteFileDesc) ? null : desc;
}
/**
* Checks if there is already a file in the library with the same urn and
* shows a dialog to user if (s)he wants to continue anyway.
*
* @param factory
* @return <code>true></code> if there is no conflict or the user wants to
* continue anyway
*/
public static boolean continueWithOrWithoutHashConflict(
DownloaderFactory factory) {
FileDesc desc = getFromLibrary(factory.getURN());
if (desc != null) {
return showHashConflict(desc);
}
return true;
}
private static boolean showHashConflict(FileDesc desc) {
String message = MessageFormat.format(GUIMediator
.getStringResource("DOWNLOADER_UTILS_HASH_CONFLICT_MESSAGE"),
new Object[] { desc.getFile() });
String question = GUIMediator
.getStringResource("DOWNLOADER_UTILS_HASH_CONFLICT_QUESTION");
String continueLabel = GUIMediator
.getStringResource("DOWNLOADER_UTILS_HASH_CONFLICT_CONTINUE_LABEL");
String[] content = new String[] { message, question };
JOptionPane pane = new JOptionPane(
content,
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION,
null,
new String[] {
continueLabel,
GUIMediator.getStringResource("GENERAL_CANCEL_BUTTON_LABEL") }
);
Dialog dialog = pane.createDialog(MessageService.getParentComponent(),
GUIMediator.getStringResource("DOWNLOADER_UTILS_ALREADY_IN_LIBRARY"));
dialog.setVisible(true);
return continueLabel.equals(pane.getValue());
}
/**
* Shows a filechooser for selecting a save location for a downloader.
*
* @param factory
* @param c
* @return
*/
public static File showFileChooser(DownloaderFactory factory, Component c) {
return FileChooserHandler.getSaveAsFile(c, "DOWNLOADER_UTILS_FILECHOOSER_TITLE",
factory.getSaveFile());
}
/**
* Checks if the final save location already exists or is taken by another
* download.
*
* @param factory
* @return
*/
private static boolean isSaveLocationTaken(DownloaderFactory factory) {
return factory.getSaveFile().exists()
|| RouterService.getDownloadManager().isSaveLocationTaken(
factory.getSaveFile());
}
}