001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.actions.downloadtasks; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 import static org.openstreetmap.josm.tools.I18n.trn; 006 007 import java.io.IOException; 008 import java.text.MessageFormat; 009 import java.util.Collection; 010 import java.util.Set; 011 import java.util.HashSet; 012 import java.util.HashMap; 013 import java.util.Map; 014 import java.util.Map.Entry; 015 016 import javax.swing.JOptionPane; 017 import javax.swing.SwingUtilities; 018 019 import org.openstreetmap.josm.Main; 020 import org.openstreetmap.josm.data.osm.DataSet; 021 import org.openstreetmap.josm.data.osm.DataSetMerger; 022 import org.openstreetmap.josm.data.osm.Node; 023 import org.openstreetmap.josm.data.osm.OsmPrimitive; 024 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 025 import org.openstreetmap.josm.data.osm.PrimitiveId; 026 import org.openstreetmap.josm.data.osm.Way; 027 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 028 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 029 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 030 import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 031 import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 032 import org.openstreetmap.josm.io.OsmServerReader; 033 import org.openstreetmap.josm.io.OsmTransferException; 034 import org.openstreetmap.josm.tools.CheckParameterUtil; 035 import org.openstreetmap.josm.tools.ExceptionUtil; 036 import org.xml.sax.SAXException; 037 038 /** 039 * The asynchronous task for downloading referring primitives 040 * 041 */ 042 public 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 primitives the collection of children for which parents are to be downloaded. Children 081 * are specified by their id and their type. 082 * 083 */ 084 public DownloadReferrersTask(OsmDataLayer targetLayer, Map<Long, OsmPrimitiveType> children) { 085 super("Download referrers", false /* don't ignore exception*/); 086 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 087 canceled = false; 088 this.children = new HashMap<Long, OsmPrimitiveType>(); 089 if (children != null) { 090 for (Entry<Long, OsmPrimitiveType> entry : children.entrySet()) { 091 if (entry.getKey() > 0 && entry.getValue() != null) { 092 children.put(entry.getKey(), entry.getValue()); 093 } 094 } 095 } 096 this.targetLayer = targetLayer; 097 parents = new DataSet(); 098 } 099 100 /** 101 * constructor 102 * 103 * @param targetLayer the target layer. Must not be null. 104 * @param id the primitive id. id > 0 required. 105 * @param type the primitive type. type != null required 106 * @exception IllegalArgumentException thrown if id <= 0 107 * @exception IllegalArgumentException thrown if type == null 108 * @exception IllegalArgumentException thrown if targetLayer == null 109 * 110 */ 111 public DownloadReferrersTask(OsmDataLayer targetLayer, long id, OsmPrimitiveType type) throws IllegalArgumentException { 112 super("Download referrers", false /* don't ignore exception*/); 113 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 114 if (id <= 0) 115 throw new IllegalArgumentException(MessageFormat.format("Id > 0 required, got {0}", id)); 116 CheckParameterUtil.ensureParameterNotNull(type, "type"); 117 canceled = false; 118 this.children = new HashMap<Long, OsmPrimitiveType>(); 119 this.children.put(id, type); 120 this.targetLayer = targetLayer; 121 parents = new DataSet(); 122 } 123 124 /** 125 * constructor 126 * 127 * @param targetLayer the target layer. Must not be null. 128 * @param primitiveId a PrimitiveId object. 129 * @exception IllegalArgumentException thrown if id <= 0 130 * @exception IllegalArgumentException thrown if targetLayer == null 131 * 132 */ 133 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId) throws IllegalArgumentException { 134 super("Download referrers", false /* don't ignore exception*/); 135 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 136 if (primitiveId.isNew()) 137 throw new IllegalArgumentException(MessageFormat.format("Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId())); 138 canceled = false; 139 this.children = new HashMap<Long, OsmPrimitiveType>(); 140 this.children.put(primitiveId.getUniqueId(), primitiveId.getType()); 141 this.targetLayer = targetLayer; 142 parents = new DataSet(); 143 } 144 145 @Override 146 protected void cancel() { 147 canceled = true; 148 synchronized(this) { 149 if (reader != null) { 150 reader.cancel(); 151 } 152 } 153 } 154 155 @Override 156 protected void finish() { 157 if (canceled) 158 return; 159 if (lastException != null) { 160 ExceptionUtil.explainException(lastException); 161 return; 162 } 163 164 DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents); 165 visitor.merge(); 166 SwingUtilities.invokeLater( 167 new Runnable() { 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 }