source: josm/trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskList.java@ 4310

Last change on this file since 4310 was 3313, checked in by stoecker, 14 years ago

fixed typo

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