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

Revision 5059, 11.7 KB checked in by simon04, 2 months ago (diff)

fix #6561 - fix several overflowing dialog texts

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