001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.dialogs.relation; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.awt.Component; 007 import java.awt.Dialog; 008 import java.io.IOException; 009 010 import javax.swing.JTree; 011 import javax.swing.SwingUtilities; 012 import javax.swing.event.TreeExpansionEvent; 013 import javax.swing.event.TreeWillExpandListener; 014 import javax.swing.tree.ExpandVetoException; 015 import javax.swing.tree.TreePath; 016 017 import org.openstreetmap.josm.Main; 018 import org.openstreetmap.josm.data.osm.DataSet; 019 import org.openstreetmap.josm.data.osm.DataSetMerger; 020 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 021 import org.openstreetmap.josm.data.osm.Relation; 022 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 023 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; 024 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 025 import org.openstreetmap.josm.io.OsmApi; 026 import org.openstreetmap.josm.io.OsmServerObjectReader; 027 import org.openstreetmap.josm.io.OsmTransferException; 028 import org.xml.sax.SAXException; 029 030 /** 031 * This is a {@link JTree} rendering the hierarchical structure of {@link Relation}s. 032 * 033 * @see RelationTreeModel 034 */ 035 public class RelationTree extends JTree { 036 /** 037 * builds the UI 038 */ 039 protected void build() { 040 setRootVisible(false); 041 setCellRenderer(new RelationTreeCellRenderer()); 042 addTreeWillExpandListener(new LazyRelationLoader()); 043 } 044 045 /** 046 * constructor 047 */ 048 public RelationTree(){ 049 super(); 050 build(); 051 } 052 053 /** 054 * constructor 055 * @param model the tree model 056 */ 057 public RelationTree(RelationTreeModel model) { 058 super(model); 059 build(); 060 } 061 062 /** 063 * replies the parent dialog this tree is embedded in. 064 * 065 * @return the parent dialog; null, if there is no parent dialog 066 */ 067 protected Dialog getParentDialog() { 068 Component c = RelationTree.this; 069 while(c != null && ! (c instanceof Dialog)) { 070 c = c.getParent(); 071 } 072 return (Dialog)c; 073 } 074 075 /** 076 * An adapter for TreeWillExpand-events. If a node is to be expanded which is 077 * not loaded yet this will trigger asynchronous loading of the respective 078 * relation. 079 * 080 */ 081 class LazyRelationLoader implements TreeWillExpandListener { 082 083 public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { 084 // do nothing 085 } 086 087 public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { 088 TreePath path = event.getPath(); 089 Relation parent = (Relation)event.getPath().getLastPathComponent(); 090 if (! parent.isIncomplete() || parent.isNew()) 091 // we don't load complete or new relations 092 return; 093 // launch the download task 094 // 095 Main.worker.submit(new RelationLoader(getParentDialog(),parent, path)); 096 } 097 } 098 099 /** 100 * Asynchronous download task for a specific relation 101 * 102 */ 103 class RelationLoader extends PleaseWaitRunnable { 104 private boolean canceled; 105 private Exception lastException; 106 private Relation relation; 107 private DataSet ds; 108 private TreePath path; 109 110 public RelationLoader(Dialog dialog, Relation relation, TreePath path) { 111 super( 112 tr("Load relation"), 113 new PleaseWaitProgressMonitor( 114 dialog 115 ), 116 false /* don't ignore exceptions */ 117 ); 118 this.relation = relation; 119 this.path = path; 120 } 121 @Override 122 protected void cancel() { 123 OsmApi.getOsmApi().cancel(); 124 this.canceled = true; 125 } 126 127 @Override 128 protected void finish() { 129 if (canceled) 130 return; 131 if (lastException != null) { 132 lastException.printStackTrace(); 133 return; 134 } 135 DataSetMerger visitor = new DataSetMerger(Main.main.getEditLayer().data, ds); 136 visitor.merge(); 137 if (! visitor.getConflicts().isEmpty()) { 138 Main.main.getEditLayer().getConflicts().add(visitor.getConflicts()); 139 } 140 final RelationTreeModel model = (RelationTreeModel)getModel(); 141 SwingUtilities.invokeLater( 142 new Runnable() { 143 public void run() { 144 model.refreshNode(path); 145 } 146 } 147 ); 148 } 149 150 @Override 151 protected void realRun() throws SAXException, IOException, OsmTransferException { 152 try { 153 OsmServerObjectReader reader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.from(relation), true /* full load */); 154 ds = reader.parseOsm(progressMonitor 155 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 156 } catch(Exception e) { 157 if (canceled) { 158 System.out.println(tr("Warning: Ignoring exception because task was canceled. Exception: {0}", e.toString())); 159 return; 160 } 161 this.lastException = e; 162 } 163 } 164 } 165 }