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