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

Last change on this file since 6643 was 6643, checked in by Don-vip, 10 years ago

global replacement of e.printStackTrace() by Main.error(e)

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