001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.io.IOException; 008import java.lang.reflect.InvocationTargetException; 009import java.util.Collection; 010import java.util.Collections; 011import java.util.HashSet; 012import java.util.Set; 013 014import javax.swing.SwingUtilities; 015 016import org.openstreetmap.josm.Main; 017import org.openstreetmap.josm.data.osm.Changeset; 018import org.openstreetmap.josm.data.osm.ChangesetCache; 019import org.openstreetmap.josm.gui.ExceptionDialogUtil; 020import org.openstreetmap.josm.gui.PleaseWaitRunnable; 021import org.openstreetmap.josm.io.OsmServerChangesetReader; 022import org.openstreetmap.josm.io.OsmTransferException; 023import org.openstreetmap.josm.tools.BugReportExceptionHandler; 024import org.openstreetmap.josm.tools.CheckParameterUtil; 025import org.openstreetmap.josm.tools.ExceptionUtil; 026import org.xml.sax.SAXException; 027 028/** 029 * This is an asynchronous task for downloading a collection of changests from the OSM 030 * server. 031 * 032 * The task only downloads the changeset properties without the changeset content. It 033 * updates the global {@link ChangesetCache}. 034 * 035 */ 036public class ChangesetHeaderDownloadTask extends PleaseWaitRunnable implements ChangesetDownloadTask { 037 038 /** 039 * Builds a download task from for a collection of changesets. 040 * 041 * Ignores null values and changesets with {@link Changeset#isNew()} == true. 042 * 043 * @param changesets the collection of changesets. Assumes an empty collection if null. 044 * @return the download task 045 */ 046 public static ChangesetHeaderDownloadTask buildTaskForChangesets(Collection<Changeset> changesets) { 047 return buildTaskForChangesets(Main.parent, changesets); 048 } 049 050 /** 051 * Builds a download task from for a collection of changesets. 052 * 053 * Ignores null values and changesets with {@link Changeset#isNew()} == true. 054 * 055 * @param parent the parent component relative to which the {@link org.openstreetmap.josm.gui.PleaseWaitDialog} is displayed. 056 * Must not be null. 057 * @param changesets the collection of changesets. Assumes an empty collection if null. 058 * @return the download task 059 * @throws IllegalArgumentException if parent is null 060 */ 061 public static ChangesetHeaderDownloadTask buildTaskForChangesets(Component parent, Collection<Changeset> changesets) { 062 CheckParameterUtil.ensureParameterNotNull(parent, "parent"); 063 if (changesets == null) { 064 changesets = Collections.emptyList(); 065 } 066 067 Set<Integer> ids = new HashSet<>(); 068 for (Changeset cs: changesets) { 069 if (cs == null || cs.isNew()) { 070 continue; 071 } 072 ids.add(cs.getId()); 073 } 074 if (parent == null) 075 return new ChangesetHeaderDownloadTask(ids); 076 else 077 return new ChangesetHeaderDownloadTask(parent, ids); 078 079 } 080 081 private Set<Integer> idsToDownload; 082 private OsmServerChangesetReader reader; 083 private boolean canceled; 084 private Exception lastException; 085 private Set<Changeset> downloadedChangesets; 086 private final boolean includeDiscussion; 087 088 protected void init(Collection<Integer> ids) { 089 if (ids == null) { 090 ids = Collections.emptyList(); 091 } 092 idsToDownload = new HashSet<>(); 093 if (ids == null || ids.isEmpty()) 094 return; 095 for (int id: ids) { 096 if (id <= 0) { 097 continue; 098 } 099 idsToDownload.add(id); 100 } 101 } 102 103 /** 104 * Creates the download task for a collection of changeset ids. Uses a {@link org.openstreetmap.josm.gui.PleaseWaitDialog} 105 * whose parent is {@link Main#parent}. 106 * 107 * Null ids or or ids <= 0 in the id collection are ignored. 108 * 109 * @param ids the collection of ids. Empty collection assumed if null. 110 */ 111 public ChangesetHeaderDownloadTask(Collection<Integer> ids) { 112 // parent for dialog is Main.parent 113 super(tr("Download changesets"), false /* don't ignore exceptions */); 114 init(ids); 115 this.includeDiscussion = false; 116 } 117 118 /** 119 * Creates the download task for a collection of changeset ids. Uses a {@link org.openstreetmap.josm.gui.PleaseWaitDialog} 120 * whose parent is the parent window of <code>dialogParent</code>. 121 * 122 * Null ids or or ids <= 0 in the id collection are ignored. 123 * 124 * @param dialogParent the parent reference component for the {@link org.openstreetmap.josm.gui.PleaseWaitDialog}. Must not be null. 125 * @param ids the collection of ids. Empty collection assumed if null. 126 * @throws IllegalArgumentException if dialogParent is null 127 */ 128 public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids) { 129 this(dialogParent, ids, false); 130 } 131 132 /** 133 * Creates the download task for a collection of changeset ids, with possibility to download changeset discussion. 134 * Uses a {@link org.openstreetmap.josm.gui.PleaseWaitDialog} whose parent is the parent window of <code>dialogParent</code>. 135 * 136 * Null ids or or ids <= 0 in the id collection are ignored. 137 * 138 * @param dialogParent the parent reference component for the {@link org.openstreetmap.josm.gui.PleaseWaitDialog}. Must not be null. 139 * @param ids the collection of ids. Empty collection assumed if null. 140 * @param includeDiscussion determines if discussion comments must be downloaded or not 141 * @throws IllegalArgumentException if dialogParent is null 142 * @since 7704 143 */ 144 public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids, boolean includeDiscussion) { 145 super(dialogParent, tr("Download changesets"), false /* don't ignore exceptions */); 146 init(ids); 147 this.includeDiscussion = includeDiscussion; 148 } 149 150 @Override 151 protected void cancel() { 152 canceled = true; 153 synchronized (this) { 154 if (reader != null) { 155 reader.cancel(); 156 } 157 } 158 } 159 160 @Override 161 protected void finish() { 162 if (canceled) 163 return; 164 if (lastException != null) { 165 ExceptionDialogUtil.explainException(lastException); 166 } 167 Runnable r = new Runnable() { 168 @Override 169 public void run() { 170 ChangesetCache.getInstance().update(downloadedChangesets); 171 } 172 }; 173 174 if (SwingUtilities.isEventDispatchThread()) { 175 r.run(); 176 } else { 177 try { 178 SwingUtilities.invokeAndWait(r); 179 } catch (InterruptedException e) { 180 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); 181 } catch (InvocationTargetException e) { 182 Throwable t = e.getTargetException(); 183 if (t instanceof RuntimeException) { 184 BugReportExceptionHandler.handleException(t); 185 } else if (t instanceof Exception) { 186 ExceptionUtil.explainException(e); 187 } else { 188 BugReportExceptionHandler.handleException(t); 189 } 190 } 191 } 192 } 193 194 @Override 195 protected void realRun() throws SAXException, IOException, OsmTransferException { 196 try { 197 synchronized (this) { 198 reader = new OsmServerChangesetReader(); 199 } 200 downloadedChangesets = new HashSet<>(); 201 downloadedChangesets.addAll(reader.readChangesets(idsToDownload, includeDiscussion, 202 getProgressMonitor().createSubTaskMonitor(0, false))); 203 } catch (OsmTransferException e) { 204 if (canceled) 205 // ignore exception if canceled 206 return; 207 // remember other exceptions 208 lastException = e; 209 } 210 } 211 212 /* ------------------------------------------------------------------------------- */ 213 /* interface ChangesetDownloadTask */ 214 /* ------------------------------------------------------------------------------- */ 215 @Override 216 public Set<Changeset> getDownloadedChangesets() { 217 return downloadedChangesets; 218 } 219 220 @Override 221 public boolean isCanceled() { 222 return canceled; 223 } 224 225 @Override 226 public boolean isFailed() { 227 return lastException != null; 228 } 229}