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

Last change on this file since 6069 was 6069, checked in by stoecker, 11 years ago

see #8853 remove tabs, trailing spaces, windows line ends, strange characters

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