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 } 125 } 126 ); 127 if (visitor.getConflicts().isEmpty()) 128 return; 129 targetLayer.getConflicts().add(visitor.getConflicts()); 130 JOptionPane.showMessageDialog( 131 Main.parent, 132 trn("There was {0} conflict during import.", 133 "There were {0} conflicts during import.", 134 visitor.getConflicts().size(), 135 visitor.getConflicts().size() 136 ), 137 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()), 138 JOptionPane.WARNING_MESSAGE 139 ); 140 Main.map.conflictDialog.unfurlDialog(); 141 Main.map.repaint(); 142 } 143 144 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException { 145 reader = new OsmServerBackreferenceReader(id, type); 146 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 147 synchronized (this) { // avoid race condition in cancel() 148 reader = null; 149 } 150 Collection<Way> ways = ds.getWays(); 151 152 DataSetMerger merger; 153 if (!ways.isEmpty()) { 154 Set<Node> nodes = new HashSet<>(); 155 for (Way w: ways) { 156 // Ensure each node is only listed once 157 nodes.addAll(w.getNodes()); 158 } 159 // Don't retrieve any nodes we've already grabbed 160 nodes.removeAll(targetLayer.data.getNodes()); 161 if (!nodes.isEmpty()) { 162 reader = MultiFetchServerObjectReader.create(); 163 ((MultiFetchServerObjectReader) reader).append(nodes); 164 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 165 synchronized (this) { // avoid race condition in cancel() 166 reader = null; 167 } 168 merger = new DataSetMerger(ds, wayNodes); 169 merger.merge(); 170 } 171 } 172 merger = new DataSetMerger(parents, ds); 173 merger.merge(); 174 } 175 176 @Override 177 protected void realRun() throws SAXException, IOException, OsmTransferException { 178 try { 179 progressMonitor.setTicksCount(children.size()); 180 int i = 1; 181 for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) { 182 if (canceled) 183 return; 184 String msg; 185 switch(entry.getValue()) { 186 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1, children.size(), entry.getKey()); break; 187 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1, children.size(), entry.getKey()); break; 188 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1, children.size(), entry.getKey()); break; 189 default: throw new AssertionError(); 190 } 191 progressMonitor.subTask(msg); 192 downloadParents(entry.getKey(), entry.getValue(), progressMonitor); 193 i++; 194 } 195 } catch (OsmTransferException e) { 196 if (canceled) 197 return; 198 lastException = e; 199 } 200 } 201}