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

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

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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