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 }