001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions.downloadtasks; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.io.IOException; 008import java.text.MessageFormat; 009import java.util.Collection; 010import java.util.HashMap; 011import java.util.HashSet; 012import java.util.Map; 013import java.util.Map.Entry; 014import java.util.Set; 015 016import javax.swing.JOptionPane; 017import javax.swing.SwingUtilities; 018 019import org.openstreetmap.josm.Main; 020import org.openstreetmap.josm.data.osm.DataSet; 021import org.openstreetmap.josm.data.osm.DataSetMerger; 022import org.openstreetmap.josm.data.osm.Node; 023import org.openstreetmap.josm.data.osm.OsmPrimitive; 024import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 025import org.openstreetmap.josm.data.osm.PrimitiveId; 026import org.openstreetmap.josm.data.osm.Way; 027import org.openstreetmap.josm.gui.PleaseWaitRunnable; 028import org.openstreetmap.josm.gui.layer.OsmDataLayer; 029import org.openstreetmap.josm.gui.progress.ProgressMonitor; 030import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 031import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 032import org.openstreetmap.josm.io.OsmServerReader; 033import org.openstreetmap.josm.io.OsmTransferException; 034import org.openstreetmap.josm.tools.CheckParameterUtil; 035import org.openstreetmap.josm.tools.ExceptionUtil; 036import org.xml.sax.SAXException; 037 038/** 039 * The asynchronous task for downloading referring primitives 040 * 041 */ 042public class DownloadReferrersTask extends PleaseWaitRunnable { 043 private boolean canceled; 044 private Exception lastException; 045 private OsmServerReader reader; 046 /** the target layer */ 047 private OsmDataLayer targetLayer; 048 /** the collection of child primitives */ 049 private Map<Long, OsmPrimitiveType> children; 050 /** the parents */ 051 private DataSet parents; 052 053 /** 054 * constructor 055 * 056 * @param targetLayer the target layer for the downloaded primitives. Must not be null. 057 * @param children the collection of child primitives for which parents are to be downloaded 058 * 059 */ 060 public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) { 061 super("Download referrers", false /* don't ignore exception*/); 062 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 063 canceled = false; 064 this.children = new HashMap<>(); 065 if (children != null) { 066 for (OsmPrimitive p: children) { 067 if (! p.isNew()) { 068 this.children.put(p.getId(), OsmPrimitiveType.from(p)); 069 } 070 } 071 } 072 this.targetLayer = targetLayer; 073 parents = new DataSet(); 074 } 075 076 /** 077 * constructor 078 * 079 * @param targetLayer the target layer for the downloaded primitives. Must not be null. 080 * @param children the collection of children for which parents are to be downloaded. Children 081 * are specified by their id and their type. 082 */ 083 public DownloadReferrersTask(OsmDataLayer targetLayer, Map<Long, OsmPrimitiveType> children) { 084 super("Download referrers", false /* don't ignore exception*/); 085 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 086 canceled = false; 087 this.children = new HashMap<>(); 088 if (children != null) { 089 for (Entry<Long, OsmPrimitiveType> entry : children.entrySet()) { 090 if (entry.getKey() > 0 && entry.getValue() != null) { 091 children.put(entry.getKey(), entry.getValue()); 092 } 093 } 094 } 095 this.targetLayer = targetLayer; 096 parents = new DataSet(); 097 } 098 099 /** 100 * constructor 101 * 102 * @param targetLayer the target layer. Must not be null. 103 * @param id the primitive id. id > 0 required. 104 * @param type the primitive type. type != null required 105 * @exception IllegalArgumentException thrown if id <= 0 106 * @exception IllegalArgumentException thrown if type == null 107 * @exception IllegalArgumentException thrown if targetLayer == null 108 * 109 */ 110 public DownloadReferrersTask(OsmDataLayer targetLayer, long id, OsmPrimitiveType type) throws IllegalArgumentException { 111 super("Download referrers", false /* don't ignore exception*/); 112 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 113 if (id <= 0) 114 throw new IllegalArgumentException(MessageFormat.format("Id > 0 required, got {0}", id)); 115 CheckParameterUtil.ensureParameterNotNull(type, "type"); 116 canceled = false; 117 this.children = new HashMap<>(); 118 this.children.put(id, type); 119 this.targetLayer = targetLayer; 120 parents = new DataSet(); 121 } 122 123 /** 124 * constructor 125 * 126 * @param targetLayer the target layer. Must not be null. 127 * @param primitiveId a PrimitiveId object. 128 * @exception IllegalArgumentException thrown if id <= 0 129 * @exception IllegalArgumentException thrown if targetLayer == null 130 * 131 */ 132 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId) throws IllegalArgumentException { 133 this(targetLayer, primitiveId, null); 134 } 135 136 /** 137 * constructor 138 * 139 * @param targetLayer the target layer. Must not be null. 140 * @param primitiveId a PrimitiveId object. 141 * @param progressMonitor ProgressMonitor to use or null to create a new one. 142 * @exception IllegalArgumentException thrown if id <= 0 143 * @exception IllegalArgumentException thrown if targetLayer == null 144 * 145 */ 146 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId, 147 ProgressMonitor progressMonitor) throws IllegalArgumentException { 148 super("Download referrers", progressMonitor, false /* don't ignore exception*/); 149 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 150 if (primitiveId.isNew()) 151 throw new IllegalArgumentException(MessageFormat.format("Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId())); 152 canceled = false; 153 this.children = new HashMap<>(); 154 this.children.put(primitiveId.getUniqueId(), primitiveId.getType()); 155 this.targetLayer = targetLayer; 156 parents = new DataSet(); 157 } 158 159 @Override 160 protected void cancel() { 161 canceled = true; 162 synchronized(this) { 163 if (reader != null) { 164 reader.cancel(); 165 } 166 } 167 } 168 169 @Override 170 protected void finish() { 171 if (canceled) 172 return; 173 if (lastException != null) { 174 ExceptionUtil.explainException(lastException); 175 return; 176 } 177 178 DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents); 179 visitor.merge(); 180 SwingUtilities.invokeLater( 181 new Runnable() { 182 @Override 183 public void run() { 184 targetLayer.onPostDownloadFromServer(); 185 if(Main.map != null) 186 Main.map.mapView.repaint(); 187 } 188 } 189 ); 190 if (visitor.getConflicts().isEmpty()) 191 return; 192 targetLayer.getConflicts().add(visitor.getConflicts()); 193 JOptionPane.showMessageDialog( 194 Main.parent, 195 trn("There was {0} conflict during import.", 196 "There were {0} conflicts during import.", 197 visitor.getConflicts().size(), 198 visitor.getConflicts().size() 199 ), 200 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()), 201 JOptionPane.WARNING_MESSAGE 202 ); 203 Main.map.conflictDialog.unfurlDialog(); 204 Main.map.repaint(); 205 } 206 207 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException{ 208 reader = new OsmServerBackreferenceReader(id, type); 209 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 210 synchronized(this) { // avoid race condition in cancel() 211 reader = null; 212 } 213 Collection<Way> ways = ds.getWays(); 214 215 DataSetMerger merger; 216 if (!ways.isEmpty()) { 217 Set<Node> nodes = new HashSet<>(); 218 for (Way w: ways) { 219 // Ensure each node is only listed once 220 nodes.addAll(w.getNodes()); 221 } 222 // Don't retrieve any nodes we've already grabbed 223 nodes.removeAll(targetLayer.data.getNodes()); 224 if (!nodes.isEmpty()) { 225 reader = new MultiFetchServerObjectReader(); 226 ((MultiFetchServerObjectReader)reader).append(nodes); 227 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 228 synchronized(this) { // avoid race condition in cancel() 229 reader = null; 230 } 231 merger = new DataSetMerger(ds, wayNodes); 232 merger.merge(); 233 } 234 } 235 merger = new DataSetMerger(parents, ds); 236 merger.merge(); 237 } 238 239 @Override 240 protected void realRun() throws SAXException, IOException, OsmTransferException { 241 try { 242 progressMonitor.setTicksCount(children.size()); 243 int i=1; 244 for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) { 245 if (canceled) 246 return; 247 String msg = ""; 248 switch(entry.getValue()) { 249 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1,children.size(), entry.getKey()); break; 250 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1,children.size(), entry.getKey()); break; 251 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1,children.size(), entry.getKey()); break; 252 } 253 progressMonitor.subTask(msg); 254 downloadParents(entry.getKey(), entry.getValue(), progressMonitor); 255 i++; 256 } 257 } catch(Exception e) { 258 if (canceled) 259 return; 260 lastException = e; 261 } 262 } 263}