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.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.Main; 020import org.openstreetmap.josm.gui.ExceptionDialogUtil; 021import org.openstreetmap.josm.gui.Notification; 022import org.openstreetmap.josm.gui.util.GuiHelper; 023import org.openstreetmap.josm.tools.ExceptionUtil; 024import org.openstreetmap.josm.tools.Logging; 025import org.openstreetmap.josm.tools.Utils; 026 027/** 028 * The post-download handler notifies user of potential errors that occurred. 029 * @since 2322 030 */ 031public class PostDownloadHandler implements Runnable { 032 private final DownloadTask task; 033 private final Future<?> future; 034 private Consumer<Collection<Object>> errorReporter; 035 036 /** 037 * Creates a new {@link PostDownloadHandler} 038 * @param task the asynchronous download task 039 * @param future the future on which the completion of the download task can be synchronized 040 */ 041 public PostDownloadHandler(DownloadTask task, Future<?> future) { 042 this.task = task; 043 this.future = future; 044 } 045 046 /** 047 * Creates a new {@link PostDownloadHandler} using a custom error reporter 048 * @param task the asynchronous download task 049 * @param future the future on which the completion of the download task can be synchronized 050 * @param errorReporter a callback to inform about the number errors happened during the download 051 * task 052 */ 053 public PostDownloadHandler(DownloadTask task, Future<?> future, Consumer<Collection<Object>> errorReporter) { 054 this(task, future); 055 this.errorReporter = errorReporter; 056 } 057 058 @Override 059 public void run() { 060 // wait for downloads task to finish (by waiting for the future to return a value) 061 // 062 try { 063 future.get(); 064 } catch (InterruptedException | ExecutionException | CancellationException e) { 065 Logging.error(e); 066 return; 067 } 068 069 // make sure errors are reported only once 070 // 071 Set<Object> errors = new LinkedHashSet<>(task.getErrorObjects()); 072 073 if (this.errorReporter != null) { 074 GuiHelper.runInEDT(() -> errorReporter.accept(errors)); 075 } 076 077 if (errors.isEmpty()) { 078 return; 079 } 080 081 // just one error object? 082 // 083 if (errors.size() == 1) { 084 final Object error = errors.iterator().next(); 085 if (!GraphicsEnvironment.isHeadless()) { 086 SwingUtilities.invokeLater(() -> { 087 if (error instanceof Exception) { 088 ExceptionDialogUtil.explainException((Exception) error); 089 } else if (tr("No data found in this area.").equals(error)) { 090 new Notification(error.toString()).setIcon(JOptionPane.WARNING_MESSAGE).show(); 091 } else { 092 JOptionPane.showMessageDialog( 093 Main.parent, 094 error.toString(), 095 tr("Error during download"), 096 JOptionPane.ERROR_MESSAGE); 097 } 098 }); 099 } 100 return; 101 } 102 103 // multiple error object? prepare a HTML list 104 // 105 if (!errors.isEmpty()) { 106 final Collection<String> items = new ArrayList<>(); 107 for (Object error : errors) { 108 if (error instanceof String) { 109 items.add((String) error); 110 } else if (error instanceof Exception) { 111 items.add(ExceptionUtil.explainException((Exception) error)); 112 } 113 } 114 115 if (!GraphicsEnvironment.isHeadless()) { 116 SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog( 117 Main.parent, 118 "<html>"+Utils.joinAsHtmlUnorderedList(items)+"</html>", 119 tr("Errors during download"), 120 JOptionPane.ERROR_MESSAGE)); 121 } 122 return; 123 } 124 } 125}