001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions.downloadtasks;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.GraphicsEnvironment;
007import java.util.Collection;
008import java.util.HashSet;
009import java.util.LinkedHashSet;
010import java.util.Set;
011import java.util.concurrent.CancellationException;
012import java.util.concurrent.ExecutionException;
013import java.util.concurrent.Future;
014import java.util.function.Consumer;
015
016import javax.swing.JOptionPane;
017import javax.swing.SwingUtilities;
018
019import org.openstreetmap.josm.gui.ExceptionDialogUtil;
020import org.openstreetmap.josm.gui.MainApplication;
021import org.openstreetmap.josm.gui.Notification;
022import org.openstreetmap.josm.gui.util.GuiHelper;
023import org.openstreetmap.josm.tools.Logging;
024import org.openstreetmap.josm.tools.Utils;
025
026/**
027 * The post-download handler notifies user of potential errors that occurred.
028 * @since 2322
029 */
030public class PostDownloadHandler implements Runnable {
031    private final DownloadTask task;
032    private final Future<?> future;
033    private Consumer<Collection<Object>> errorReporter;
034
035    private static final Set<String> NO_DATA_ERROR_MESSAGES = new HashSet<>();
036
037    /**
038     * Creates a new {@link PostDownloadHandler}
039     * @param task the asynchronous download task
040     * @param future the future on which the completion of the download task can be synchronized
041     */
042    public PostDownloadHandler(DownloadTask task, Future<?> future) {
043        this.task = task;
044        this.future = future;
045    }
046
047    /**
048     * Creates a new {@link PostDownloadHandler} using a custom error reporter
049     * @param task the asynchronous download task
050     * @param future the future on which the completion of the download task can be synchronized
051     * @param errorReporter a callback to inform about the number errors happened during the download
052     *                      task
053     */
054    public PostDownloadHandler(DownloadTask task, Future<?> future, Consumer<Collection<Object>> errorReporter) {
055        this(task, future);
056        this.errorReporter = errorReporter;
057    }
058
059    /**
060     * Adds a new translated error message indicating that no data has been downloaded.
061     * @param message new translated error message indicating that no data has been downloaded.
062     * @return {@code true} if the message was not already known
063     * @since 15358
064     */
065    public static boolean addNoDataErrorMessage(String message) {
066        return NO_DATA_ERROR_MESSAGES.add(message);
067    }
068
069    /**
070     * Determines if a translated error message indicates that no data has been downloaded.
071     * @param message translated error message to check
072     * @return {@code true} if the message indicates that no data has been downloaded
073     * @since 15358
074     */
075    public static boolean isNoDataErrorMessage(Object message) {
076        return NO_DATA_ERROR_MESSAGES.contains(message);
077    }
078
079    @Override
080    public void run() {
081        // wait for downloads task to finish (by waiting for the future to return a value)
082        //
083        try {
084            future.get();
085        } catch (InterruptedException | ExecutionException | CancellationException e) {
086            Logging.error(e);
087            return;
088        }
089
090        // make sure errors are reported only once
091        //
092        Set<Object> errors = new LinkedHashSet<>(task.getErrorObjects());
093
094        if (this.errorReporter != null) {
095            GuiHelper.runInEDT(() -> errorReporter.accept(errors));
096        }
097
098        if (errors.isEmpty()) {
099            return;
100        }
101
102        // just one error object?
103        //
104        if (errors.size() == 1) {
105            final Object error = errors.iterator().next();
106            if (!GraphicsEnvironment.isHeadless()) {
107                SwingUtilities.invokeLater(() -> {
108                    if (error instanceof Exception) {
109                        ExceptionDialogUtil.explainException((Exception) error);
110                    } else if (isNoDataErrorMessage(error)) {
111                        new Notification(error.toString()).setIcon(JOptionPane.WARNING_MESSAGE).show();
112                    } else {
113                        JOptionPane.showMessageDialog(
114                                MainApplication.getMainFrame(),
115                                error.toString(),
116                                tr("Error during download"),
117                                JOptionPane.ERROR_MESSAGE);
118                    }
119                });
120            }
121            return;
122        }
123
124        // multiple error object? prepare a HTML list
125        //
126        final Collection<String> items = task.getErrorMessages();
127        if (!items.isEmpty() && !GraphicsEnvironment.isHeadless()) {
128            SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(
129                    MainApplication.getMainFrame(),
130                    "<html>"+Utils.joinAsHtmlUnorderedList(items)+"</html>",
131                    tr("Errors during download"),
132                    JOptionPane.ERROR_MESSAGE));
133        }
134    }
135}