001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.dialogs.changeset; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Component; 007 import java.io.IOException; 008 import java.text.MessageFormat; 009 import java.util.ArrayList; 010 import java.util.Collection; 011 import java.util.Collections; 012 import java.util.HashSet; 013 import java.util.List; 014 import java.util.Set; 015 016 import org.openstreetmap.josm.data.osm.Changeset; 017 import org.openstreetmap.josm.data.osm.ChangesetCache; 018 import org.openstreetmap.josm.data.osm.ChangesetDataSet; 019 import org.openstreetmap.josm.gui.ExceptionDialogUtil; 020 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 021 import org.openstreetmap.josm.io.OsmServerChangesetReader; 022 import org.openstreetmap.josm.io.OsmTransferCanceledException; 023 import org.openstreetmap.josm.io.OsmTransferException; 024 import org.xml.sax.SAXException; 025 026 /** 027 * This is an asynchronous task for downloading the changeset content of a collection of 028 * changesets. 029 * 030 */ 031 public class ChangesetContentDownloadTask extends PleaseWaitRunnable implements ChangesetDownloadTask{ 032 033 /** the list of changeset ids to download */ 034 private final List<Integer> toDownload = new ArrayList<Integer>(); 035 /** true if the task was canceled */ 036 private boolean canceled; 037 /** keeps the last exception thrown in the task, if any */ 038 private Exception lastException; 039 /** the reader object used to read changesets from the API */ 040 private OsmServerChangesetReader reader; 041 /** the set of downloaded changesets */ 042 private Set<Changeset> downloadedChangesets; 043 044 /** 045 * Initialize the task with a collection of changeset ids to download 046 * 047 * @param ids the collection of ids. May be null. 048 */ 049 protected void init(Collection<Integer> ids) { 050 if (ids == null) { 051 ids = Collections.emptyList(); 052 } 053 for (Integer id: ids) { 054 if (id == null || id <= 0) { 055 continue; 056 } 057 toDownload.add(id); 058 } 059 downloadedChangesets = new HashSet<Changeset>(); 060 } 061 062 /** 063 * Creates a download task for a single changeset 064 * 065 * @param changesetId the changeset id. >0 required. 066 * @throws IllegalArgumentException thrown if changesetId <= 0 067 */ 068 public ChangesetContentDownloadTask(int changesetId) throws IllegalArgumentException{ 069 super(tr("Downloading changeset content"), false /* don't ignore exceptions */); 070 if (changesetId <= 0) 071 throw new IllegalArgumentException(MessageFormat.format("Expected integer value > 0 for parameter ''{0}'', got ''{1}''", "changesetId", changesetId)); 072 init(Collections.singleton(changesetId)); 073 } 074 075 /** 076 * Creates a download task for a collection of changesets. null values and id <=0 in 077 * the collection are sillently discarded. 078 * 079 * @param changesetIds the changeset ids. Empty collection assumed, if null. 080 */ 081 public ChangesetContentDownloadTask(Collection<Integer> changesetIds) { 082 super(tr("Downloading changeset content"), false /* don't ignore exceptions */); 083 init(changesetIds); 084 } 085 086 /** 087 * Creates a download task for a single changeset 088 * 089 * @param parent the parent component for the {@link PleaseWaitDialog}. Must not be null. 090 * @param changesetId the changeset id. >0 required. 091 * @throws IllegalArgumentException thrown if changesetId <= 0 092 * @throws IllegalArgumentException thrown if parent is null 093 */ 094 public ChangesetContentDownloadTask(Component parent, int changesetId) throws IllegalArgumentException{ 095 super(parent, tr("Downloading changeset content"), false /* don't ignore exceptions */); 096 if (changesetId <= 0) 097 throw new IllegalArgumentException(MessageFormat.format("Expected integer value > 0 for parameter ''{0}'', got ''{1}''", "changesetId", changesetId)); 098 init(Collections.singleton(changesetId)); 099 } 100 101 /** 102 * Creates a download task for a collection of changesets. null values and id <=0 in 103 * the collection are sillently discarded. 104 * 105 * @param parent the parent component for the {@link PleaseWaitDialog}. Must not be null. 106 * @param changesetIds the changeset ids. Empty collection assumed, if null. 107 * @throws IllegalArgumentException thrown if parent is null 108 */ 109 public ChangesetContentDownloadTask(Component parent, Collection<Integer> changesetIds) throws IllegalArgumentException { 110 super(parent, tr("Downloading changeset content"), false /* don't ignore exceptions */); 111 init(changesetIds); 112 } 113 114 /** 115 * Replies true if the local {@link ChangesetCache} already includes the changeset with 116 * id <code>changesetId</code>. 117 * 118 * @param changesetId the changeset id 119 * @return true if the local {@link ChangesetCache} already includes the changeset with 120 * id <code>changesetId</code> 121 */ 122 protected boolean isAvailableLocally(int changesetId) { 123 return ChangesetCache.getInstance().get(changesetId) != null; 124 } 125 126 /** 127 * Downloads the changeset with id <code>changesetId</code> (only "header" 128 * information, no content) 129 * 130 * @param changesetId the changeset id 131 * @throws OsmTransferException thrown if something went wrong 132 */ 133 protected void downloadChangeset(int changesetId) throws OsmTransferException { 134 synchronized(this) { 135 reader = new OsmServerChangesetReader(); 136 } 137 Changeset cs = reader.readChangeset(changesetId, getProgressMonitor().createSubTaskMonitor(0, false)); 138 synchronized(this) { 139 reader = null; 140 } 141 ChangesetCache.getInstance().update(cs); 142 } 143 144 @Override 145 protected void cancel() { 146 canceled = true; 147 synchronized (this) { 148 if (reader != null) { 149 reader.cancel(); 150 } 151 } 152 } 153 154 @Override 155 protected void finish() { 156 if (canceled) return; 157 if (lastException != null) { 158 ExceptionDialogUtil.explainException(lastException); 159 } 160 } 161 162 @Override 163 protected void realRun() throws SAXException, IOException, OsmTransferException { 164 try { 165 getProgressMonitor().setTicksCount(toDownload.size()); 166 int i=0; 167 for (int id: toDownload) { 168 i++; 169 if (!isAvailableLocally(id)) { 170 getProgressMonitor().setCustomText(tr("({0}/{1}) Downloading changeset {2}...", i, toDownload.size(), id)); 171 downloadChangeset(id); 172 } 173 if (canceled) return; 174 synchronized(this) { 175 reader = new OsmServerChangesetReader(); 176 } 177 getProgressMonitor().setCustomText(tr("({0}/{1}) Downloading content for changeset {2}...", i, toDownload.size(), id)); 178 ChangesetDataSet ds = reader.downloadChangeset(id, getProgressMonitor().createSubTaskMonitor(0, false)); 179 synchronized(this) { 180 reader = null; 181 } 182 Changeset cs = ChangesetCache.getInstance().get(id); 183 cs.setContent(ds); 184 ChangesetCache.getInstance().update(cs); 185 downloadedChangesets.add(cs); 186 getProgressMonitor().worked(1); 187 } 188 } catch(OsmTransferCanceledException e) { 189 // the download was canceled by the user. This exception is caught if the 190 // user canceled the authentication dialog. 191 // 192 canceled = true; 193 return; 194 } catch(OsmTransferException e) { 195 if (canceled) 196 return; 197 lastException = e; 198 } catch(RuntimeException e) { 199 throw e; 200 } 201 } 202 203 /* ------------------------------------------------------------------------------- */ 204 /* interface ChangesetDownloadTask */ 205 /* ------------------------------------------------------------------------------- */ 206 public Set<Changeset> getDownloadedChangesets() { 207 return downloadedChangesets; 208 } 209 210 public boolean isCanceled() { 211 return canceled; 212 } 213 214 public boolean isFailed() { 215 return lastException != null; 216 } 217 }