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.Set; 011import java.util.HashSet; 012import java.util.HashMap; 013import java.util.Map; 014import java.util.Map.Entry; 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<Long, OsmPrimitiveType>(); 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<Long, OsmPrimitiveType>(); 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<Long, OsmPrimitiveType>(); 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 super("Download referrers", false /* don't ignore exception*/); 134 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 135 if (primitiveId.isNew()) 136 throw new IllegalArgumentException(MessageFormat.format("Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId())); 137 canceled = false; 138 this.children = new HashMap<Long, OsmPrimitiveType>(); 139 this.children.put(primitiveId.getUniqueId(), primitiveId.getType()); 140 this.targetLayer = targetLayer; 141 parents = new DataSet(); 142 } 143 144 @Override 145 protected void cancel() { 146 canceled = true; 147 synchronized(this) { 148 if (reader != null) { 149 reader.cancel(); 150 } 151 } 152 } 153 154 @Override 155 protected void finish() { 156 if (canceled) 157 return; 158 if (lastException != null) { 159 ExceptionUtil.explainException(lastException); 160 return; 161 } 162 163 DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents); 164 visitor.merge(); 165 SwingUtilities.invokeLater( 166 new Runnable() { 167 @Override 168 public void run() { 169 targetLayer.onPostDownloadFromServer(); 170 Main.map.mapView.repaint(); 171 } 172 } 173 ); 174 if (visitor.getConflicts().isEmpty()) 175 return; 176 targetLayer.getConflicts().add(visitor.getConflicts()); 177 JOptionPane.showMessageDialog( 178 Main.parent, 179 trn("There was {0} conflict during import.", 180 "There were {0} conflicts during import.", 181 visitor.getConflicts().size(), 182 visitor.getConflicts().size() 183 ), 184 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()), 185 JOptionPane.WARNING_MESSAGE 186 ); 187 Main.map.conflictDialog.unfurlDialog(); 188 Main.map.repaint(); 189 } 190 191 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException{ 192 reader = new OsmServerBackreferenceReader(id, type); 193 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 194 synchronized(this) { // avoid race condition in cancel() 195 reader = null; 196 } 197 Collection<Way> ways = ds.getWays(); 198 199 DataSetMerger merger; 200 if (!ways.isEmpty()) { 201 Set<Node> nodes = new HashSet<Node>(); 202 for (Way w: ways) { 203 // Ensure each node is only listed once 204 nodes.addAll(w.getNodes()); 205 } 206 // Don't retrieve any nodes we've already grabbed 207 nodes.removeAll(targetLayer.data.getNodes()); 208 if (!nodes.isEmpty()) { 209 reader = new MultiFetchServerObjectReader(); 210 ((MultiFetchServerObjectReader)reader).append(nodes); 211 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 212 synchronized(this) { // avoid race condition in cancel() 213 reader = null; 214 } 215 merger = new DataSetMerger(ds, wayNodes); 216 merger.merge(); 217 } 218 } 219 merger = new DataSetMerger(parents, ds); 220 merger.merge(); 221 } 222 223 @Override 224 protected void realRun() throws SAXException, IOException, OsmTransferException { 225 try { 226 progressMonitor.setTicksCount(children.size()); 227 int i=1; 228 for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) { 229 if (canceled) 230 return; 231 String msg = ""; 232 switch(entry.getValue()) { 233 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1,children.size(), entry.getKey()); break; 234 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1,children.size(), entry.getKey()); break; 235 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1,children.size(), entry.getKey()); break; 236 } 237 progressMonitor.subTask(msg); 238 downloadParents(entry.getKey(), entry.getValue(), progressMonitor); 239 i++; 240 } 241 } catch(Exception e) { 242 if (canceled) 243 return; 244 lastException = e; 245 } 246 } 247}