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