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 * @since 2923 041 */ 042public class DownloadReferrersTask extends PleaseWaitRunnable { 043 private boolean canceled; 044 private Exception lastException; 045 private OsmServerReader reader; 046 /** the target layer */ 047 private final OsmDataLayer targetLayer; 048 /** the collection of child primitives */ 049 private final Map<Long, OsmPrimitiveType> children; 050 /** the parents */ 051 private final 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 public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) { 060 super("Download referrers", false /* don't ignore exception*/); 061 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 062 canceled = false; 063 this.children = new HashMap<>(); 064 if (children != null) { 065 for (OsmPrimitive p: children) { 066 if (!p.isNew()) { 067 this.children.put(p.getId(), OsmPrimitiveType.from(p)); 068 } 069 } 070 } 071 this.targetLayer = targetLayer; 072 parents = new DataSet(); 073 } 074 075 /** 076 * constructor 077 * 078 * @param targetLayer the target layer. Must not be null. 079 * @param primitiveId a PrimitiveId object. 080 * @param progressMonitor ProgressMonitor to use or null to create a new one. 081 * @throws IllegalArgumentException if id <= 0 082 * @throws IllegalArgumentException if targetLayer == null 083 */ 084 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId, 085 ProgressMonitor progressMonitor) { 086 super("Download referrers", progressMonitor, false /* don't ignore exception*/); 087 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 088 if (primitiveId.isNew()) 089 throw new IllegalArgumentException(MessageFormat.format( 090 "Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId())); 091 canceled = false; 092 this.children = new HashMap<>(); 093 this.children.put(primitiveId.getUniqueId(), primitiveId.getType()); 094 this.targetLayer = targetLayer; 095 parents = new DataSet(); 096 } 097 098 @Override 099 protected void cancel() { 100 canceled = true; 101 synchronized (this) { 102 if (reader != null) { 103 reader.cancel(); 104 } 105 } 106 } 107 108 @Override 109 protected void finish() { 110 if (canceled) 111 return; 112 if (lastException != null) { 113 ExceptionUtil.explainException(lastException); 114 return; 115 } 116 117 DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents); 118 visitor.merge(); 119 SwingUtilities.invokeLater( 120 new Runnable() { 121 @Override 122 public void run() { 123 targetLayer.onPostDownloadFromServer(); 124 if (Main.map != null) 125 Main.map.mapView.repaint(); 126 } 127 } 128 ); 129 if (visitor.getConflicts().isEmpty()) 130 return; 131 targetLayer.getConflicts().add(visitor.getConflicts()); 132 JOptionPane.showMessageDialog( 133 Main.parent, 134 trn("There was {0} conflict during import.", 135 "There were {0} conflicts during import.", 136 visitor.getConflicts().size(), 137 visitor.getConflicts().size() 138 ), 139 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()), 140 JOptionPane.WARNING_MESSAGE 141 ); 142 Main.map.conflictDialog.unfurlDialog(); 143 Main.map.repaint(); 144 } 145 146 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException { 147 reader = new OsmServerBackreferenceReader(id, type); 148 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 149 synchronized (this) { // avoid race condition in cancel() 150 reader = null; 151 } 152 Collection<Way> ways = ds.getWays(); 153 154 DataSetMerger merger; 155 if (!ways.isEmpty()) { 156 Set<Node> nodes = new HashSet<>(); 157 for (Way w: ways) { 158 // Ensure each node is only listed once 159 nodes.addAll(w.getNodes()); 160 } 161 // Don't retrieve any nodes we've already grabbed 162 nodes.removeAll(targetLayer.data.getNodes()); 163 if (!nodes.isEmpty()) { 164 reader = MultiFetchServerObjectReader.create(); 165 ((MultiFetchServerObjectReader) reader).append(nodes); 166 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 167 synchronized (this) { // avoid race condition in cancel() 168 reader = null; 169 } 170 merger = new DataSetMerger(ds, wayNodes); 171 merger.merge(); 172 } 173 } 174 merger = new DataSetMerger(parents, ds); 175 merger.merge(); 176 } 177 178 @Override 179 protected void realRun() throws SAXException, IOException, OsmTransferException { 180 try { 181 progressMonitor.setTicksCount(children.size()); 182 int i = 1; 183 for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) { 184 if (canceled) 185 return; 186 String msg; 187 switch(entry.getValue()) { 188 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1, children.size(), entry.getKey()); break; 189 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1, children.size(), entry.getKey()); break; 190 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1, children.size(), entry.getKey()); break; 191 default: throw new AssertionError(); 192 } 193 progressMonitor.subTask(msg); 194 downloadParents(entry.getKey(), entry.getValue(), progressMonitor); 195 i++; 196 } 197 } catch (OsmTransferException e) { 198 if (canceled) 199 return; 200 lastException = e; 201 } 202 } 203}