source: josm/trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java@ 13632

Last change on this file since 13632 was 13612, checked in by Don-vip, 6 years ago

use OsmDataLayer.getDataSet() in download tasks

  • Property svn:eol-style set to native
File size: 12.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions.downloadtasks;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.EventQueue;
9import java.awt.geom.Area;
10import java.awt.geom.Rectangle2D;
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.HashSet;
14import java.util.LinkedHashSet;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Set;
18import java.util.concurrent.CancellationException;
19import java.util.concurrent.ExecutionException;
20import java.util.concurrent.Future;
21
22import javax.swing.JOptionPane;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.actions.UpdateSelectionAction;
26import org.openstreetmap.josm.data.Bounds;
27import org.openstreetmap.josm.data.osm.DataSet;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.gui.HelpAwareOptionPane;
30import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
31import org.openstreetmap.josm.gui.MainApplication;
32import org.openstreetmap.josm.gui.Notification;
33import org.openstreetmap.josm.gui.layer.Layer;
34import org.openstreetmap.josm.gui.layer.OsmDataLayer;
35import org.openstreetmap.josm.gui.progress.ProgressMonitor;
36import org.openstreetmap.josm.gui.util.GuiHelper;
37import org.openstreetmap.josm.tools.ExceptionUtil;
38import org.openstreetmap.josm.tools.ImageProvider;
39import org.openstreetmap.josm.tools.Logging;
40import org.openstreetmap.josm.tools.Utils;
41
42/**
43 * This class encapsulates the downloading of several bounding boxes that would otherwise be too
44 * large to download in one go. Error messages will be collected for all downloads and displayed as
45 * a list in the end.
46 * @author xeen
47 * @since 6053
48 */
49public class DownloadTaskList {
50 private final List<DownloadTask> tasks = new LinkedList<>();
51 private final List<Future<?>> taskFutures = new LinkedList<>();
52 private ProgressMonitor progressMonitor;
53
54 private void addDownloadTask(ProgressMonitor progressMonitor, DownloadTask dt, Rectangle2D td, int i, int n) {
55 ProgressMonitor childProgress = progressMonitor.createSubTaskMonitor(1, false);
56 childProgress.setCustomText(tr("Download {0} of {1} ({2} left)", i, n, n - i));
57 Future<?> future = dt.download(false, new Bounds(td), childProgress);
58 taskFutures.add(future);
59 tasks.add(dt);
60 }
61
62 /**
63 * Downloads a list of areas from the OSM Server
64 * @param newLayer Set to true if all areas should be put into a single new layer
65 * @param rects The List of Rectangle2D to download
66 * @param osmData Set to true if OSM data should be downloaded
67 * @param gpxData Set to true if GPX data should be downloaded
68 * @param progressMonitor The progress monitor
69 * @return The Future representing the asynchronous download task
70 */
71 public Future<?> download(boolean newLayer, List<Rectangle2D> rects, boolean osmData, boolean gpxData, ProgressMonitor progressMonitor) {
72 this.progressMonitor = progressMonitor;
73 if (newLayer) {
74 Layer l = new OsmDataLayer(new DataSet(), OsmDataLayer.createNewName(), null);
75 MainApplication.getLayerManager().addLayer(l);
76 MainApplication.getLayerManager().setActiveLayer(l);
77 }
78
79 int n = (osmData && gpxData ? 2 : 1)*rects.size();
80 progressMonitor.beginTask(null, n);
81 int i = 0;
82 for (Rectangle2D td : rects) {
83 i++;
84 if (osmData) {
85 addDownloadTask(progressMonitor, new DownloadOsmTask(), td, i, n);
86 }
87 if (gpxData) {
88 addDownloadTask(progressMonitor, new DownloadGpsTask(), td, i, n);
89 }
90 }
91 progressMonitor.addCancelListener(() -> {
92 for (DownloadTask dt : tasks) {
93 dt.cancel();
94 }
95 });
96 return MainApplication.worker.submit(new PostDownloadProcessor(osmData));
97 }
98
99 /**
100 * Downloads a list of areas from the OSM Server
101 * @param newLayer Set to true if all areas should be put into a single new layer
102 * @param areas The Collection of Areas to download
103 * @param osmData Set to true if OSM data should be downloaded
104 * @param gpxData Set to true if GPX data should be downloaded
105 * @param progressMonitor The progress monitor
106 * @return The Future representing the asynchronous download task
107 */
108 public Future<?> download(boolean newLayer, Collection<Area> areas, boolean osmData, boolean gpxData, ProgressMonitor progressMonitor) {
109 progressMonitor.beginTask(tr("Updating data"));
110 try {
111 List<Rectangle2D> rects = new ArrayList<>(areas.size());
112 for (Area a : areas) {
113 rects.add(a.getBounds2D());
114 }
115
116 return download(newLayer, rects, osmData, gpxData, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
117 } finally {
118 progressMonitor.finishTask();
119 }
120 }
121
122 /**
123 * Replies the set of ids of all complete, non-new primitives (i.e. those with !primitive.incomplete)
124 * @param ds data set
125 *
126 * @return the set of ids of all complete, non-new primitives
127 */
128 protected Set<OsmPrimitive> getCompletePrimitives(DataSet ds) {
129 Set<OsmPrimitive> ret = new HashSet<>();
130 for (OsmPrimitive primitive : ds.allPrimitives()) {
131 if (!primitive.isIncomplete() && !primitive.isNew()) {
132 ret.add(primitive);
133 }
134 }
135 return ret;
136 }
137
138 /**
139 * Updates the local state of a set of primitives (given by a set of primitive ids) with the
140 * state currently held on the server.
141 *
142 * @param potentiallyDeleted a set of ids to check update from the server
143 */
144 protected void updatePotentiallyDeletedPrimitives(Set<OsmPrimitive> potentiallyDeleted) {
145 final List<OsmPrimitive> toSelect = new ArrayList<>();
146 for (OsmPrimitive primitive : potentiallyDeleted) {
147 if (primitive != null) {
148 toSelect.add(primitive);
149 }
150 }
151 EventQueue.invokeLater(() -> UpdateSelectionAction.updatePrimitives(toSelect));
152 }
153
154 /**
155 * Processes a set of primitives (given by a set of their ids) which might be deleted on the
156 * server. First prompts the user whether he wants to check the current state on the server. If
157 * yes, retrieves the current state on the server and checks whether the primitives are indeed
158 * deleted on the server.
159 *
160 * @param potentiallyDeleted a set of primitives (given by their ids)
161 */
162 protected void handlePotentiallyDeletedPrimitives(Set<OsmPrimitive> potentiallyDeleted) {
163 ButtonSpec[] options = new ButtonSpec[] {
164 new ButtonSpec(
165 tr("Check on the server"),
166 ImageProvider.get("ok"),
167 tr("Click to check whether objects in your local dataset are deleted on the server"),
168 null /* no specific help topic */
169 ),
170 new ButtonSpec(
171 tr("Ignore"),
172 ImageProvider.get("cancel"),
173 tr("Click to abort and to resume editing"),
174 null /* no specific help topic */
175 ),
176 };
177
178 String message = "<html>" + trn(
179 "There is {0} object in your local dataset which "
180 + "might be deleted on the server.<br>If you later try to delete or "
181 + "update this the server is likely to report a conflict.",
182 "There are {0} objects in your local dataset which "
183 + "might be deleted on the server.<br>If you later try to delete or "
184 + "update them the server is likely to report a conflict.",
185 potentiallyDeleted.size(), potentiallyDeleted.size())
186 + "<br>"
187 + trn("Click <strong>{0}</strong> to check the state of this object on the server.",
188 "Click <strong>{0}</strong> to check the state of these objects on the server.",
189 potentiallyDeleted.size(),
190 options[0].text) + "<br>"
191 + tr("Click <strong>{0}</strong> to ignore." + "</html>", options[1].text);
192
193 int ret = HelpAwareOptionPane.showOptionDialog(
194 Main.parent,
195 message,
196 tr("Deleted or moved objects"),
197 JOptionPane.WARNING_MESSAGE,
198 null,
199 options,
200 options[0],
201 ht("/Action/UpdateData#SyncPotentiallyDeletedObjects")
202 );
203 if (ret != 0 /* OK */)
204 return;
205
206 updatePotentiallyDeletedPrimitives(potentiallyDeleted);
207 }
208
209 /**
210 * Replies the set of primitive ids which have been downloaded by this task list
211 *
212 * @return the set of primitive ids which have been downloaded by this task list
213 */
214 public Set<OsmPrimitive> getDownloadedPrimitives() {
215 Set<OsmPrimitive> ret = new HashSet<>();
216 for (DownloadTask task : tasks) {
217 if (task instanceof DownloadOsmTask) {
218 DataSet ds = ((DownloadOsmTask) task).getDownloadedData();
219 if (ds != null) {
220 ret.addAll(ds.allPrimitives());
221 }
222 }
223 }
224 return ret;
225 }
226
227 class PostDownloadProcessor implements Runnable {
228
229 private final boolean osmData;
230
231 PostDownloadProcessor(boolean osmData) {
232 this.osmData = osmData;
233 }
234
235 /**
236 * Grabs and displays the error messages after all download threads have finished.
237 */
238 @Override
239 public void run() {
240 progressMonitor.finishTask();
241
242 // wait for all download tasks to finish
243 //
244 for (Future<?> future : taskFutures) {
245 try {
246 future.get();
247 } catch (InterruptedException | ExecutionException | CancellationException e) {
248 Logging.error(e);
249 return;
250 }
251 }
252 Set<Object> errors = new LinkedHashSet<>();
253 for (DownloadTask dt : tasks) {
254 errors.addAll(dt.getErrorObjects());
255 }
256 if (!errors.isEmpty()) {
257 final Collection<String> items = new ArrayList<>();
258 for (Object error : errors) {
259 if (error instanceof String) {
260 items.add((String) error);
261 } else if (error instanceof Exception) {
262 items.add(ExceptionUtil.explainException((Exception) error));
263 }
264 }
265
266 GuiHelper.runInEDT(() -> {
267 if (items.size() == 1 && tr("No data found in this area.").equals(items.iterator().next())) {
268 new Notification(items.iterator().next()).setIcon(JOptionPane.WARNING_MESSAGE).show();
269 } else {
270 JOptionPane.showMessageDialog(Main.parent, "<html>"
271 + tr("The following errors occurred during mass download: {0}",
272 Utils.joinAsHtmlUnorderedList(items)) + "</html>",
273 tr("Errors during download"), JOptionPane.ERROR_MESSAGE);
274 }
275 });
276
277 return;
278 }
279
280 // FIXME: this is a hack. We assume that the user canceled the whole download if at
281 // least one task was canceled or if it failed
282 //
283 for (DownloadTask task : tasks) {
284 if (task instanceof AbstractDownloadTask) {
285 AbstractDownloadTask<?> absTask = (AbstractDownloadTask<?>) task;
286 if (absTask.isCanceled() || absTask.isFailed())
287 return;
288 }
289 }
290 final OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer();
291 if (editLayer != null && osmData) {
292 final Set<OsmPrimitive> myPrimitives = getCompletePrimitives(editLayer.getDataSet());
293 for (DownloadTask task : tasks) {
294 if (task instanceof DownloadOsmTask) {
295 DataSet ds = ((DownloadOsmTask) task).getDownloadedData();
296 if (ds != null) {
297 // myPrimitives.removeAll(ds.allPrimitives()) will do the same job but much slower
298 for (OsmPrimitive primitive: ds.allPrimitives()) {
299 myPrimitives.remove(primitive);
300 }
301 }
302 }
303 }
304 if (!myPrimitives.isEmpty()) {
305 GuiHelper.runInEDT(() -> handlePotentiallyDeletedPrimitives(myPrimitives));
306 }
307 }
308 }
309 }
310}
Note: See TracBrowser for help on using the repository browser.