// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions.downloadtasks;
import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Future;
import javax.swing.SwingUtilities;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.osm.Changeset;
import org.openstreetmap.josm.data.osm.ChangesetCache;
import org.openstreetmap.josm.gui.PleaseWaitRunnable;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.OsmServerChangesetReader;
import org.openstreetmap.josm.tools.ExceptionUtil;
import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
/**
* Common abstract implementation of other changeset download tasks.
* @since 10124
*/
public abstract class AbstractChangesetDownloadTask extends AbstractDownloadTask<Set<Changeset>> {
abstract class RunnableDownloadTask extends PleaseWaitRunnable {
/** the reader object used to read changesets from the API */
protected final OsmServerChangesetReader reader = new OsmServerChangesetReader();
/** the set of downloaded changesets */
protected final Set<Changeset> downloadedChangesets = new HashSet<>();
/** keeps the last exception thrown in the task, if any */
protected Exception lastException;
RunnableDownloadTask(Component parent, String title) {
super(parent, title, false /* don't ignore exceptions */);
}
@Override
protected void cancel() {
setCanceled(true);
synchronized (this) {
if (reader != null) {
reader.cancel();
}
}
}
protected final void rememberLastException(Exception e) {
lastException = e;
setFailed(true);
}
protected final void updateChangesets() {
// update the global changeset cache with the downloaded changesets.
// this will trigger change events which views are listening to. They
// will update their views accordingly.
//
// Run on the EDT because UI updates are triggered.
//
Runnable r = () -> ChangesetCache.getInstance().update(downloadedChangesets);
if (SwingUtilities.isEventDispatchThread()) {
r.run();
} else {
try {
SwingUtilities.invokeAndWait(r);
} catch (InterruptedException e) {
Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache");
Thread.currentThread().interrupt();
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof RuntimeException) {
BugReportExceptionHandler.handleException(t);
} else if (t instanceof Exception) {
ExceptionUtil.explainException(e);
} else {
BugReportExceptionHandler.handleException(t);
}
}
}
}
}
private RunnableDownloadTask downloadTaskRunnable;
protected final void setDownloadTask(RunnableDownloadTask downloadTask) {
this.downloadTaskRunnable = downloadTask;
}
@Override
public final Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
return download();
}
/**
* Asynchronously launches the changeset download task. This is equivalent to {@code download(false, null, null)}.
*
* You can wait for the asynchronous download task to finish by synchronizing on the returned
* {@link Future}, but make sure not to freeze up JOSM. Example:
* <pre>
* Future<?> future = task.download();
* // DON'T run this on the Swing EDT or JOSM will freeze
* future.get(); // waits for the dowload task to complete
* </pre>
*
* The following example uses a pattern which is better suited if a task is launched from the Swing EDT:
* <pre>
* final Future<?> future = task.download();
* Runnable runAfterTask = new Runnable() {
* public void run() {
* // this is not strictly necessary because of the type of executor service
* // Main.worker is initialized with, but it doesn't harm either
* //
* future.get(); // wait for the download task to complete
* doSomethingAfterTheTaskCompleted();
* }
* }
* Main.worker.submit(runAfterTask);
* </pre>
*
* @return the future representing the asynchronous task
*/
public final Future<?> download() {
return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null;
}
@Override
public final Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null;
}
@Override
public final void cancel() {
if (downloadTaskRunnable != null) {
downloadTaskRunnable.cancel();
}
}
@Override
public String getConfirmationMessage(URL url) {
return null;
}
}