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

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

refactoring - global simplification of use of setLayout method - simply pass layout to JPanel constructor

  • Property svn:eol-style set to native
File size: 16.6 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 Relation relation;
330 private final Stack<Relation> relationsToDownload;
331 private final Set<Long> downloadedRelationIds;
332
333 DownloadAllChildrenTask(Dialog parent, Relation r) {
334 super(tr("Download relation members"), parent);
335 this.relation = r;
336 relationsToDownload = new Stack<>();
337 downloadedRelationIds = new HashSet<>();
338 relationsToDownload.push(this.relation);
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 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(new Runnable() {
423 @Override
424 public void run() {
425 Main.map.repaint();
426 }
427 });
428 } catch (Exception e) {
429 if (canceled) {
430 Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
431 return;
432 }
433 lastException = e;
434 }
435 }
436 }
437
438 /**
439 * The asynchronous task for downloading a set of relations
440 */
441 class DownloadRelationSetTask extends DownloadTask {
442 private final Set<Relation> relations;
443
444 DownloadRelationSetTask(Dialog parent, Set<Relation> relations) {
445 super(tr("Download relation members"), parent);
446 this.relations = relations;
447 }
448
449 protected void mergeDataSet(DataSet dataSet) {
450 if (dataSet != null) {
451 final DataSetMerger visitor = new DataSetMerger(getLayer().data, dataSet);
452 visitor.merge();
453 if (!visitor.getConflicts().isEmpty()) {
454 getLayer().getConflicts().add(visitor.getConflicts());
455 conflictsCount += visitor.getConflicts().size();
456 }
457 }
458 }
459
460 @Override
461 protected void realRun() throws SAXException, IOException, OsmTransferException {
462 try {
463 Iterator<Relation> it = relations.iterator();
464 while (it.hasNext() && !canceled) {
465 Relation r = it.next();
466 if (r.isNew()) {
467 continue;
468 }
469 progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
470 OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
471 true);
472 DataSet dataSet = reader.parseOsm(progressMonitor
473 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
474 mergeDataSet(dataSet);
475 refreshView(r);
476 }
477 } catch (Exception e) {
478 if (canceled) {
479 Main.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
480 return;
481 }
482 lastException = e;
483 }
484 }
485 }
486}
Note: See TracBrowser for help on using the repository browser.