source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java @ 11534

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

sonar - fb-contrib:FCBL_FIELD_COULD_BE_LOCAL - Correctness - Class defines fields that are used only as locals

  • Property svn:eol-style set to native
File size: 16.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.awt.Component;
9import java.awt.Dialog;
10import java.awt.FlowLayout;
11import java.awt.event.ActionEvent;
12import java.io.IOException;
13import java.net.HttpURLConnection;
14import java.util.HashSet;
15import java.util.Iterator;
16import java.util.List;
17import java.util.Set;
18import java.util.Stack;
19
20import javax.swing.AbstractAction;
21import javax.swing.JButton;
22import javax.swing.JOptionPane;
23import javax.swing.JPanel;
24import javax.swing.JScrollPane;
25import javax.swing.SwingUtilities;
26import javax.swing.event.TreeSelectionEvent;
27import javax.swing.event.TreeSelectionListener;
28import javax.swing.tree.TreePath;
29
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.data.osm.DataSet;
32import org.openstreetmap.josm.data.osm.DataSetMerger;
33import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
34import org.openstreetmap.josm.data.osm.Relation;
35import org.openstreetmap.josm.data.osm.RelationMember;
36import org.openstreetmap.josm.gui.DefaultNameFormatter;
37import org.openstreetmap.josm.gui.ExceptionDialogUtil;
38import org.openstreetmap.josm.gui.PleaseWaitRunnable;
39import org.openstreetmap.josm.gui.layer.OsmDataLayer;
40import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
41import org.openstreetmap.josm.gui.progress.ProgressMonitor;
42import org.openstreetmap.josm.io.OsmApi;
43import org.openstreetmap.josm.io.OsmApiException;
44import org.openstreetmap.josm.io.OsmServerObjectReader;
45import org.openstreetmap.josm.io.OsmTransferException;
46import org.openstreetmap.josm.tools.CheckParameterUtil;
47import org.openstreetmap.josm.tools.ImageProvider;
48import org.xml.sax.SAXException;
49
50/**
51 * ChildRelationBrowser is a UI component which provides a tree-like view on the hierarchical
52 * structure of relations.
53 *
54 * @since 1828
55 */
56public class ChildRelationBrowser extends JPanel {
57    /** the tree with relation children */
58    private RelationTree childTree;
59    /**  the tree model */
60    private transient RelationTreeModel model;
61
62    /** the osm data layer this browser is related to */
63    private transient OsmDataLayer layer;
64
65    /**
66     * Replies the {@link OsmDataLayer} this editor is related to
67     *
68     * @return the osm data layer
69     */
70    protected OsmDataLayer getLayer() {
71        return layer;
72    }
73
74    /**
75     * builds the UI
76     */
77    protected void build() {
78        setLayout(new BorderLayout());
79        childTree = new RelationTree(model);
80        JScrollPane pane = new JScrollPane(childTree);
81        add(pane, BorderLayout.CENTER);
82
83        add(buildButtonPanel(), BorderLayout.SOUTH);
84    }
85
86    /**
87     * builds the panel with the command buttons
88     *
89     * @return the button panel
90     */
91    protected JPanel buildButtonPanel() {
92        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
93
94        // ---
95        DownloadAllChildRelationsAction downloadAction = new DownloadAllChildRelationsAction();
96        pnl.add(new JButton(downloadAction));
97
98        // ---
99        DownloadSelectedAction downloadSelectedAction = new DownloadSelectedAction();
100        childTree.addTreeSelectionListener(downloadSelectedAction);
101        pnl.add(new JButton(downloadSelectedAction));
102
103        // ---
104        EditAction editAction = new EditAction();
105        childTree.addTreeSelectionListener(editAction);
106        pnl.add(new JButton(editAction));
107
108        return pnl;
109    }
110
111    /**
112     * constructor
113     *
114     * @param layer the {@link OsmDataLayer} this browser is related to. Must not be null.
115     * @throws IllegalArgumentException if layer is null
116     */
117    public ChildRelationBrowser(OsmDataLayer layer) {
118        CheckParameterUtil.ensureParameterNotNull(layer, "layer");
119        this.layer = layer;
120        model = new RelationTreeModel();
121        build();
122    }
123
124    /**
125     * constructor
126     *
127     * @param layer the {@link OsmDataLayer} this browser is related to. Must not be null.
128     * @param root the root relation
129     * @throws IllegalArgumentException if layer is null
130     */
131    public ChildRelationBrowser(OsmDataLayer layer, Relation root) {
132        this(layer);
133        populate(root);
134    }
135
136    /**
137     * populates the browser with a relation
138     *
139     * @param r the relation
140     */
141    public void populate(Relation r) {
142        model.populate(r);
143    }
144
145    /**
146     * populates the browser with a list of relation members
147     *
148     * @param members the list of relation members
149     */
150
151    public void populate(List<RelationMember> members) {
152        model.populate(members);
153    }
154
155    /**
156     * replies the parent dialog this browser is embedded in
157     *
158     * @return the parent dialog; null, if there is no {@link Dialog} as parent dialog
159     */
160    protected Dialog getParentDialog() {
161        Component c = this;
162        while (c != null && !(c instanceof Dialog)) {
163            c = c.getParent();
164        }
165        return (Dialog) c;
166    }
167
168    /**
169     * Action for editing the currently selected relation
170     *
171     *
172     */
173    class EditAction extends AbstractAction implements TreeSelectionListener {
174        EditAction() {
175            putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to."));
176            putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
177            putValue(NAME, tr("Edit"));
178            refreshEnabled();
179        }
180
181        protected void refreshEnabled() {
182            TreePath[] selection = childTree.getSelectionPaths();
183            setEnabled(selection != null && selection.length > 0);
184        }
185
186        public void run() {
187            TreePath[] selection = childTree.getSelectionPaths();
188            if (selection == null || selection.length == 0) return;
189            // do not launch more than 10 relation editors in parallel
190            //
191            for (int i = 0; i < Math.min(selection.length, 10); i++) {
192                Relation r = (Relation) selection[i].getLastPathComponent();
193                if (r.isIncomplete()) {
194                    continue;
195                }
196                RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
197                editor.setVisible(true);
198            }
199        }
200
201        @Override
202        public void actionPerformed(ActionEvent e) {
203            if (!isEnabled())
204                return;
205            run();
206        }
207
208        @Override
209        public void valueChanged(TreeSelectionEvent e) {
210            refreshEnabled();
211        }
212    }
213
214    /**
215     * Action for downloading all child relations for a given parent relation.
216     * Recursively.
217     */
218    class DownloadAllChildRelationsAction extends AbstractAction {
219        DownloadAllChildRelationsAction() {
220            putValue(SHORT_DESCRIPTION, tr("Download all child relations (recursively)"));
221            putValue(SMALL_ICON, ImageProvider.get("download"));
222            putValue(NAME, tr("Download All Children"));
223        }
224
225        public void run() {
226            Main.worker.submit(new DownloadAllChildrenTask(getParentDialog(), (Relation) model.getRoot()));
227        }
228
229        @Override
230        public void actionPerformed(ActionEvent e) {
231            if (!isEnabled())
232                return;
233            run();
234        }
235    }
236
237    /**
238     * Action for downloading all selected relations
239     */
240    class DownloadSelectedAction extends AbstractAction implements TreeSelectionListener {
241        DownloadSelectedAction() {
242            putValue(SHORT_DESCRIPTION, tr("Download selected relations"));
243            // FIXME: replace with better icon
244            //
245            putValue(SMALL_ICON, ImageProvider.get("download"));
246            putValue(NAME, tr("Download Selected Children"));
247            updateEnabledState();
248        }
249
250        protected void updateEnabledState() {
251            TreePath[] selection = childTree.getSelectionPaths();
252            setEnabled(selection != null && selection.length > 0);
253        }
254
255        public void run() {
256            TreePath[] selection = childTree.getSelectionPaths();
257            if (selection == null || selection.length == 0)
258                return;
259            Set<Relation> relations = new HashSet<>();
260            for (TreePath aSelection : selection) {
261                relations.add((Relation) aSelection.getLastPathComponent());
262            }
263            Main.worker.submit(new DownloadRelationSetTask(getParentDialog(), relations));
264        }
265
266        @Override
267        public void actionPerformed(ActionEvent e) {
268            if (!isEnabled())
269                return;
270            run();
271        }
272
273        @Override
274        public void valueChanged(TreeSelectionEvent e) {
275            updateEnabledState();
276        }
277    }
278
279    abstract class DownloadTask extends PleaseWaitRunnable {
280        protected boolean canceled;
281        protected int conflictsCount;
282        protected Exception lastException;
283
284        DownloadTask(String title, Dialog parent) {
285            super(title, new PleaseWaitProgressMonitor(parent), false);
286        }
287
288        @Override
289        protected void cancel() {
290            canceled = true;
291            OsmApi.getOsmApi().cancel();
292        }
293
294        protected void refreshView(Relation relation) {
295            for (int i = 0; i < childTree.getRowCount(); i++) {
296                Relation reference = (Relation) childTree.getPathForRow(i).getLastPathComponent();
297                if (reference == relation) {
298                    model.refreshNode(childTree.getPathForRow(i));
299                }
300            }
301        }
302
303        @Override
304        protected void finish() {
305            if (canceled)
306                return;
307            if (lastException != null) {
308                ExceptionDialogUtil.explainException(lastException);
309                return;
310            }
311
312            if (conflictsCount > 0) {
313                JOptionPane.showMessageDialog(
314                        Main.parent,
315                        trn("There was {0} conflict during import.",
316                                "There were {0} conflicts during import.",
317                                conflictsCount, conflictsCount),
318                                trn("Conflict in data", "Conflicts in data", conflictsCount),
319                                JOptionPane.WARNING_MESSAGE
320                );
321            }
322        }
323    }
324
325    /**
326     * The asynchronous task for downloading relation members.
327     */
328    class DownloadAllChildrenTask extends DownloadTask {
329        private final Stack<Relation> relationsToDownload;
330        private final Set<Long> downloadedRelationIds;
331
332        DownloadAllChildrenTask(Dialog parent, Relation r) {
333            super(tr("Download relation members"), parent);
334            relationsToDownload = new Stack<>();
335            downloadedRelationIds = new HashSet<>();
336            relationsToDownload.push(r);
337        }
338
339        /**
340         * warns the user if a relation couldn't be loaded because it was deleted on
341         * the server (the server replied a HTTP code 410)
342         *
343         * @param r the relation
344         */
345        protected void warnBecauseOfDeletedRelation(Relation r) {
346            String message = tr("<html>The child relation<br>"
347                    + "{0}<br>"
348                    + "is deleted on the server. It cannot be loaded</html>",
349                    r.getDisplayName(DefaultNameFormatter.getInstance())
350            );
351
352            JOptionPane.showMessageDialog(
353                    Main.parent,
354                    message,
355                    tr("Relation is deleted"),
356                    JOptionPane.WARNING_MESSAGE
357            );
358        }
359
360        /**
361         * Remembers the child relations to download
362         *
363         * @param parent the parent relation
364         */
365        protected void rememberChildRelationsToDownload(Relation parent) {
366            downloadedRelationIds.add(parent.getId());
367            for (RelationMember member: parent.getMembers()) {
368                if (member.isRelation()) {
369                    Relation child = member.getRelation();
370                    if (!downloadedRelationIds.contains(child.getId())) {
371                        relationsToDownload.push(child);
372                    }
373                }
374            }
375        }
376
377        /**
378         * Merges the primitives in <code>ds</code> to the dataset of the
379         * edit layer
380         *
381         * @param ds the data set
382         */
383        protected void mergeDataSet(DataSet ds) {
384            if (ds != null) {
385                final DataSetMerger visitor = new DataSetMerger(getLayer().data, ds);
386                visitor.merge();
387                if (!visitor.getConflicts().isEmpty()) {
388                    getLayer().getConflicts().add(visitor.getConflicts());
389                    conflictsCount += visitor.getConflicts().size();
390                }
391            }
392        }
393
394        @Override
395        protected void realRun() throws SAXException, IOException, OsmTransferException {
396            try {
397                while (!relationsToDownload.isEmpty() && !canceled) {
398                    Relation r = relationsToDownload.pop();
399                    if (r.isNew()) {
400                        continue;
401                    }
402                    rememberChildRelationsToDownload(r);
403                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
404                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
405                            true);
406                    DataSet dataSet = null;
407                    try {
408                        dataSet = reader.parseOsm(progressMonitor
409                                .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
410                    } catch (OsmApiException e) {
411                        if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) {
412                            warnBecauseOfDeletedRelation(r);
413                            continue;
414                        }
415                        throw e;
416                    }
417                    mergeDataSet(dataSet);
418                    refreshView(r);
419                }
420                SwingUtilities.invokeLater(Main.map::repaint);
421            } catch (OsmTransferException e) {
422                if (canceled) {
423                    Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
424                    return;
425                }
426                lastException = e;
427            }
428        }
429    }
430
431    /**
432     * The asynchronous task for downloading a set of relations
433     */
434    class DownloadRelationSetTask extends DownloadTask {
435        private final Set<Relation> relations;
436
437        DownloadRelationSetTask(Dialog parent, Set<Relation> relations) {
438            super(tr("Download relation members"), parent);
439            this.relations = relations;
440        }
441
442        protected void mergeDataSet(DataSet dataSet) {
443            if (dataSet != null) {
444                final DataSetMerger visitor = new DataSetMerger(getLayer().data, dataSet);
445                visitor.merge();
446                if (!visitor.getConflicts().isEmpty()) {
447                    getLayer().getConflicts().add(visitor.getConflicts());
448                    conflictsCount += visitor.getConflicts().size();
449                }
450            }
451        }
452
453        @Override
454        protected void realRun() throws SAXException, IOException, OsmTransferException {
455            try {
456                Iterator<Relation> it = relations.iterator();
457                while (it.hasNext() && !canceled) {
458                    Relation r = it.next();
459                    if (r.isNew()) {
460                        continue;
461                    }
462                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
463                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
464                            true);
465                    DataSet dataSet = reader.parseOsm(progressMonitor
466                            .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
467                    mergeDataSet(dataSet);
468                    refreshView(r);
469                }
470            } catch (OsmTransferException e) {
471                if (canceled) {
472                    Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
473                    return;
474                }
475                lastException = e;
476            }
477        }
478    }
479}
Note: See TracBrowser for help on using the repository browser.