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

Last change on this file since 2847 was 2847, checked in by mjulius, 14 years ago

fix messages for gui/dialogs

File size: 18.7 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 *
55 */
56public class ChildRelationBrowser extends JPanel {
57 //static private final Logger logger = Logger.getLogger(ChildRelationBrowser.class.getName());
58
59 /** the tree with relation children */
60 private RelationTree childTree;
61 /** the tree model */
62 private RelationTreeModel model;
63
64 /** the osm data layer this browser is related to */
65 private OsmDataLayer layer;
66
67 /**
68 * Replies the {@see 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();
95 pnl.setLayout(new FlowLayout(FlowLayout.LEFT));
96
97 // ---
98 DownloadAllChildRelationsAction downloadAction= new DownloadAllChildRelationsAction();
99 pnl.add(new JButton(downloadAction));
100
101 // ---
102 DownloadSelectedAction downloadSelectedAction= new DownloadSelectedAction();
103 childTree.addTreeSelectionListener(downloadSelectedAction);
104 pnl.add(new JButton(downloadSelectedAction));
105
106 // ---
107 EditAction editAction = new EditAction();
108 childTree.addTreeSelectionListener(editAction);
109 pnl.add(new JButton(editAction));
110
111 return pnl;
112 }
113
114 /**
115 * constructor
116 *
117 * @param layer the {@see OsmDataLayer} this browser is related to. Must not be null.
118 * @exception IllegalArgumentException thrown, if layer is null
119 */
120 public ChildRelationBrowser(OsmDataLayer layer) throws IllegalArgumentException {
121 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
122 this.layer = layer;
123 model = new RelationTreeModel();
124 build();
125 }
126
127 /**
128 * constructor
129 *
130 * @param layer the {@see OsmDataLayer} this browser is related to. Must not be null.
131 * @param root the root relation
132 * @exception IllegalArgumentException thrown, if layer is null
133 */
134 public ChildRelationBrowser(OsmDataLayer layer, Relation root) throws IllegalArgumentException {
135 this(layer);
136 populate(root);
137 }
138
139 /**
140 * populates the browser with a relation
141 *
142 * @param r the relation
143 */
144 public void populate(Relation r) {
145 model.populate(r);
146 }
147
148 /**
149 * populates the browser with a list of relation members
150 *
151 * @param members the list of relation members
152 */
153
154 public void populate(List<RelationMember> members) {
155 model.populate(members);
156 }
157
158 /**
159 * replies the parent dialog this browser is embedded in
160 *
161 * @return the parent dialog; null, if there is no {@see Dialog} as parent dialog
162 */
163 protected Dialog getParentDialog() {
164 Component c = this;
165 while(c != null && ! (c instanceof Dialog)) {
166 c = c.getParent();
167 }
168 return (Dialog)c;
169 }
170
171 /**
172 * Action for editing the currently selected relation
173 *
174 *
175 */
176 class EditAction extends AbstractAction implements TreeSelectionListener {
177 public EditAction() {
178 putValue(SHORT_DESCRIPTION, tr("Edit the relation the currently selected relation member refers to."));
179 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
180 putValue(NAME, tr("Edit"));
181 refreshEnabled();
182 }
183
184 protected void refreshEnabled() {
185 TreePath[] selection = childTree.getSelectionPaths();
186 setEnabled(selection != null && selection.length > 0);
187 }
188
189 public void run() {
190 TreePath [] selection = childTree.getSelectionPaths();
191 if (selection == null || selection.length == 0) return;
192 // do not launch more than 10 relation editors in parallel
193 //
194 for (int i=0; i < Math.min(selection.length,10);i++) {
195 Relation r = (Relation)selection[i].getLastPathComponent();
196 if (r.isIncomplete()) {
197 continue;
198 }
199 RelationEditor editor = RelationEditor.getEditor(getLayer(), r, null);
200 editor.setVisible(true);
201 }
202 }
203
204 public void actionPerformed(ActionEvent e) {
205 if (!isEnabled())
206 return;
207 run();
208 }
209
210 public void valueChanged(TreeSelectionEvent e) {
211 refreshEnabled();
212 }
213 }
214
215 /**
216 * Action for downloading all child relations for a given parent relation.
217 * Recursively.
218 */
219 class DownloadAllChildRelationsAction extends AbstractAction{
220 public DownloadAllChildRelationsAction() {
221 putValue(SHORT_DESCRIPTION, tr("Download all child relations (recursively)"));
222 putValue(SMALL_ICON, ImageProvider.get("download"));
223 putValue(NAME, tr("Download All Children"));
224 }
225
226 public void run() {
227 Main.worker.submit(new DownloadAllChildrenTask(getParentDialog(), (Relation)model.getRoot()));
228 }
229
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 public 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 HashSet<Relation> relations = new HashSet<Relation>();
260 for (TreePath aSelection : selection) {
261 relations.add((Relation) aSelection.getLastPathComponent());
262 }
263 Main.worker.submit(new DownloadRelationSetTask(getParentDialog(),relations));
264 }
265
266 public void actionPerformed(ActionEvent e) {
267 if (!isEnabled())
268 return;
269 run();
270 }
271
272 public void valueChanged(TreeSelectionEvent e) {
273 updateEnabledState();
274 }
275 }
276
277 /**
278 * The asynchronous task for downloading relation members.
279 *
280 *
281 */
282 class DownloadAllChildrenTask extends PleaseWaitRunnable {
283 private boolean cancelled;
284 private int conflictsCount;
285 private Exception lastException;
286 private Relation relation;
287 private Stack<Relation> relationsToDownload;
288 private Set<Long> downloadedRelationIds;
289
290 public DownloadAllChildrenTask(Dialog parent, Relation r) {
291 super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /*
292 * don't
293 * ignore
294 * exception
295 */);
296 this.relation = r;
297 relationsToDownload = new Stack<Relation>();
298 downloadedRelationIds = new HashSet<Long>();
299 relationsToDownload.push(this.relation);
300 }
301
302 @Override
303 protected void cancel() {
304 cancelled = true;
305 OsmApi.getOsmApi().cancel();
306 }
307
308 protected void refreshView(Relation relation){
309 for (int i=0; i < childTree.getRowCount(); i++) {
310 Relation reference = (Relation)childTree.getPathForRow(i).getLastPathComponent();
311 if (reference == relation) {
312 model.refreshNode(childTree.getPathForRow(i));
313 }
314 }
315 }
316
317 @Override
318 protected void finish() {
319 if (cancelled)
320 return;
321 if (lastException != null) {
322 ExceptionDialogUtil.explainException(lastException);
323 return;
324 }
325
326 if (conflictsCount > 0) {
327 JOptionPane.showMessageDialog(
328 Main.parent,
329 trn("There was {0} conflict during import.",
330 "There were {0} conflicts during import.",
331 conflictsCount, conflictsCount),
332 trn("Conflict in data", "Conflicts in data", conflictsCount),
333 JOptionPane.WARNING_MESSAGE
334 );
335 }
336 }
337
338 /**
339 * warns the user if a relation couldn't be loaded because it was deleted on
340 * the server (the server replied a HTTP code 410)
341 *
342 * @param r the relation
343 */
344 protected void warnBecauseOfDeletedRelation(Relation r) {
345 String message = tr("<html>The child relation<br>"
346 + "{0}<br>"
347 + "is deleted on the server. It cannot be loaded",
348 r.getDisplayName(DefaultNameFormatter.getInstance())
349 );
350
351 JOptionPane.showMessageDialog(
352 Main.parent,
353 message,
354 tr("Relation is deleted"),
355 JOptionPane.WARNING_MESSAGE
356 );
357 }
358
359 /**
360 * Remembers the child relations to download
361 *
362 * @param parent the parent relation
363 */
364 protected void rememberChildRelationsToDownload(Relation parent) {
365 downloadedRelationIds.add(parent.getId());
366 for (RelationMember member: parent.getMembers()) {
367 if (member.isRelation()) {
368 Relation child = member.getRelation();
369 if (!downloadedRelationIds.contains(child.getId())) {
370 relationsToDownload.push(child);
371 }
372 }
373 }
374 }
375
376 /**
377 * Merges the primitives in <code>ds</code> to the dataset of the
378 * edit layer
379 *
380 * @param ds the data set
381 */
382 protected void mergeDataSet(DataSet ds) {
383 if (ds != null) {
384 final DataSetMerger visitor = new DataSetMerger(getLayer().data, ds);
385 visitor.merge();
386 // FIXME: this is necessary because there are dialogs listening
387 // for DataChangeEvents which manipulate Swing components on this
388 // thread.
389 //
390 SwingUtilities.invokeLater(new Runnable() {
391 public void run() {
392 getLayer().fireDataChange();
393 }
394 });
395 if (!visitor.getConflicts().isEmpty()) {
396 getLayer().getConflicts().add(visitor.getConflicts());
397 conflictsCount += visitor.getConflicts().size();
398 }
399 }
400 }
401
402 @Override
403 protected void realRun() throws SAXException, IOException, OsmTransferException {
404 try {
405 while(! relationsToDownload.isEmpty() && !cancelled) {
406 Relation r = relationsToDownload.pop();
407 if (r.isNew()) {
408 continue;
409 }
410 rememberChildRelationsToDownload(r);
411 progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
412 OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
413 true);
414 DataSet dataSet = null;
415 try {
416 dataSet = reader.parseOsm(progressMonitor
417 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
418 } catch(OsmApiException e) {
419 if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) {
420 warnBecauseOfDeletedRelation(r);
421 continue;
422 }
423 throw e;
424 }
425 mergeDataSet(dataSet);
426 refreshView(r);
427 }
428 } catch (Exception e) {
429 if (cancelled) {
430 System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
431 .toString()));
432 return;
433 }
434 lastException = e;
435 }
436 }
437 }
438
439 /**
440 * The asynchronous task for downloading a set of relations
441 */
442 class DownloadRelationSetTask extends PleaseWaitRunnable {
443 private boolean cancelled;
444 private int conflictsCount;
445 private Exception lastException;
446 private Set<Relation> relations;
447
448 public DownloadRelationSetTask(Dialog parent, Set<Relation> relations) {
449 super(tr("Download relation members"), new PleaseWaitProgressMonitor(parent), false /*
450 * don't
451 * ignore
452 * exception
453 */);
454 this.relations = relations;
455 }
456
457 @Override
458 protected void cancel() {
459 cancelled = true;
460 OsmApi.getOsmApi().cancel();
461 }
462
463 protected void refreshView(Relation relation){
464 for (int i=0; i < childTree.getRowCount(); i++) {
465 Relation reference = (Relation)childTree.getPathForRow(i).getLastPathComponent();
466 if (reference == relation) {
467 model.refreshNode(childTree.getPathForRow(i));
468 }
469 }
470 }
471
472 @Override
473 protected void finish() {
474 if (cancelled)
475 return;
476 if (lastException != null) {
477 ExceptionDialogUtil.explainException(lastException);
478 return;
479 }
480
481 if (conflictsCount > 0) {
482 JOptionPane.showMessageDialog(
483 Main.parent,
484 trn("There was {0} conflict during import.",
485 "There were {0} conflicts during import.",
486 conflictsCount, conflictsCount),
487 trn("Conflict in data", "Conflicts in data", conflictsCount),
488 JOptionPane.WARNING_MESSAGE
489 );
490 }
491 }
492
493 protected void mergeDataSet(DataSet dataSet) {
494 if (dataSet != null) {
495 final DataSetMerger visitor = new DataSetMerger(getLayer().data, dataSet);
496 visitor.merge();
497 // FIXME: this is necessary because there are dialogs listening
498 // for DataChangeEvents which manipulate Swing components on this
499 // thread.
500 //
501 SwingUtilities.invokeLater(new Runnable() {
502 public void run() {
503 getLayer().fireDataChange();
504 }
505 });
506 if (!visitor.getConflicts().isEmpty()) {
507 getLayer().getConflicts().add(visitor.getConflicts());
508 conflictsCount += visitor.getConflicts().size();
509 }
510 }
511 }
512
513 @Override
514 protected void realRun() throws SAXException, IOException, OsmTransferException {
515 try {
516 Iterator<Relation> it = relations.iterator();
517 while(it.hasNext() && !cancelled) {
518 Relation r = it.next();
519 if (r.isNew()) {
520 continue;
521 }
522 progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
523 OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
524 true);
525 DataSet dataSet = reader.parseOsm(progressMonitor
526 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
527 mergeDataSet(dataSet);
528 refreshView(r);
529 }
530 } catch (Exception e) {
531 if (cancelled) {
532 System.out.println(tr("Warning: ignoring exception because task is cancelled. Exception: {0}", e
533 .toString()));
534 return;
535 }
536 lastException = e;
537 }
538 }
539 }
540}
Note: See TracBrowser for help on using the repository browser.