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 thrown 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 HashSet<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 thrown if dialogParent is null 127 */ 128 public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids) throws IllegalArgumentException{ 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 thrown if dialogParent is null 142 * @since 7704 143 */ 144 public ChangesetHeaderDownloadTask(Component dialogParent, Collection<Integer> ids, boolean includeDiscussion) 145 throws IllegalArgumentException { 146 super(dialogParent, tr("Download changesets"), false /* don't ignore exceptions */); 147 init(ids); 148 this.includeDiscussion = includeDiscussion; 149 } 150 151 @Override 152 protected void cancel() { 153 canceled = true; 154 synchronized (this) { 155 if (reader != null) { 156 reader.cancel(); 157 } 158 } 159 } 160 161 @Override 162 protected void finish() { 163 if (canceled) 164 return; 165 if (lastException != null) { 166 ExceptionDialogUtil.explainException(lastException); 167 } 168 Runnable r = new Runnable() { 169 @Override 170 public void run() { 171 ChangesetCache.getInstance().update(downloadedChangesets); 172 } 173 }; 174 175 if (SwingUtilities.isEventDispatchThread()) { 176 r.run(); 177 } else { 178 try { 179 SwingUtilities.invokeAndWait(r); 180 } catch(InterruptedException e) { 181 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); 182 } catch(InvocationTargetException e) { 183 Throwable t = e.getTargetException(); 184 if (t instanceof RuntimeException) { 185 BugReportExceptionHandler.handleException(t); 186 } else if (t instanceof Exception){ 187 ExceptionUtil.explainException(e); 188 } else { 189 BugReportExceptionHandler.handleException(t); 190 } 191 } 192 } 193 } 194 195 @Override 196 protected void realRun() throws SAXException, IOException, OsmTransferException { 197 try { 198 synchronized (this) { 199 reader = new OsmServerChangesetReader(); 200 } 201 downloadedChangesets = new HashSet<>(); 202 downloadedChangesets.addAll(reader.readChangesets(idsToDownload, includeDiscussion, 203 getProgressMonitor().createSubTaskMonitor(0, false))); 204 } catch(OsmTransferException e) { 205 if (canceled) 206 // ignore exception if canceled 207 return; 208 // remember other exceptions 209 lastException = e; 210 } 211 } 212 213 /* ------------------------------------------------------------------------------- */ 214 /* interface ChangesetDownloadTask */ 215 /* ------------------------------------------------------------------------------- */ 216 @Override 217 public Set<Changeset> getDownloadedChangesets() { 218 return downloadedChangesets; 219 } 220 221 @Override 222 public boolean isCanceled() { 223 return canceled; 224 } 225 226 @Override 227 public boolean isFailed() { 228 return lastException != null; 229 } 230}