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

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

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