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.LinkedHashSet; 011import java.util.Set; 012import java.util.stream.Collectors; 013 014import javax.swing.JOptionPane; 015import javax.swing.SwingUtilities; 016 017import org.openstreetmap.josm.data.Bounds; 018import org.openstreetmap.josm.data.osm.DataSet; 019import org.openstreetmap.josm.data.osm.DataSetMerger; 020import org.openstreetmap.josm.data.osm.Node; 021import org.openstreetmap.josm.data.osm.OsmPrimitive; 022import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 023import org.openstreetmap.josm.data.osm.PrimitiveId; 024import org.openstreetmap.josm.data.osm.Way; 025import org.openstreetmap.josm.gui.MainApplication; 026import org.openstreetmap.josm.gui.MapFrame; 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.MultiFetchOverpassObjectReader; 031import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 032import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 033import org.openstreetmap.josm.io.OsmServerReader; 034import org.openstreetmap.josm.io.OsmTransferException; 035import org.openstreetmap.josm.io.OverpassDownloadReader; 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 Set<PrimitiveId> 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 * @since 15787 (modified interface) 061 */ 062 public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<? extends PrimitiveId> children) { 063 super("Download referrers", false /* don't ignore exception*/); 064 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 065 if (!targetLayer.isDownloadable()) { 066 throw new IllegalArgumentException("Non-downloadable layer: " + targetLayer); 067 } 068 canceled = false; 069 this.children = new LinkedHashSet<>(); 070 if (children != null) { 071 children.stream().filter(p -> !p.isNew()).forEach(this.children::add); 072 } 073 074 this.targetLayer = targetLayer; 075 parents = new DataSet(); 076 } 077 078 /** 079 * constructor 080 * 081 * @param targetLayer the target layer. Must not be null. 082 * @param primitiveId a PrimitiveId object. 083 * @param progressMonitor ProgressMonitor to use or null to create a new one. 084 * @throws IllegalArgumentException if id <= 0 085 * @throws IllegalArgumentException if targetLayer == null 086 */ 087 public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId, 088 ProgressMonitor progressMonitor) { 089 super("Download referrers", progressMonitor, false /* don't ignore exception*/); 090 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 091 if (primitiveId.isNew()) 092 throw new IllegalArgumentException(MessageFormat.format( 093 "Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId())); 094 canceled = false; 095 this.children = new LinkedHashSet<>(); 096 this.children.add(primitiveId); 097 this.targetLayer = targetLayer; 098 parents = new DataSet(); 099 } 100 101 @Override 102 protected void cancel() { 103 canceled = true; 104 synchronized (this) { 105 if (reader != null) { 106 reader.cancel(); 107 } 108 } 109 } 110 111 @Override 112 protected void finish() { 113 if (canceled) 114 return; 115 if (lastException != null) { 116 JOptionPane.showMessageDialog( 117 MainApplication.getMainFrame(), 118 ExceptionUtil.explainException(lastException), 119 tr("Error"), 120 JOptionPane.ERROR_MESSAGE); 121 return; 122 } 123 124 DataSetMerger visitor = new DataSetMerger(targetLayer.getDataSet(), parents); 125 visitor.merge(); 126 SwingUtilities.invokeLater(targetLayer::onPostDownloadFromServer); 127 if (visitor.getConflicts().isEmpty()) 128 return; 129 targetLayer.getConflicts().add(visitor.getConflicts()); 130 JOptionPane.showMessageDialog( 131 MainApplication.getMainFrame(), 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 MapFrame map = MainApplication.getMap(); 141 map.conflictDialog.unfurlDialog(); 142 map.repaint(); 143 } 144 145 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException { 146 reader = new OsmServerBackreferenceReader(id, type, false).setAllowIncompleteParentWays(true); 147 148 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 149 synchronized (this) { // avoid race condition in cancel() 150 reader = null; 151 if (canceled) 152 return; 153 } 154 new DataSetMerger(parents, ds).merge(); 155 } 156 157 @Override 158 protected void realRun() throws SAXException, IOException, OsmTransferException { 159 try { 160 if (Boolean.TRUE.equals(OverpassDownloadReader.FOR_MULTI_FETCH.get())) { 161 String request = MultiFetchOverpassObjectReader.genOverpassQuery(children, false, true, false); 162 reader = new OverpassDownloadReader(new Bounds(0, 0, 0, 0), 163 OverpassDownloadReader.OVERPASS_SERVER.get(), request); 164 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 165 new DataSetMerger(parents, ds).merge(); 166 } else { 167 progressMonitor.setTicksCount(children.size()); 168 int i = 1; 169 for (PrimitiveId p : children) { 170 if (canceled) 171 return; 172 String msg; 173 String id = Long.toString(p.getUniqueId()); 174 switch(p.getType()) { 175 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break; 176 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break; 177 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break; 178 default: throw new AssertionError(); 179 } 180 progressMonitor.subTask(msg); 181 downloadParents(p.getUniqueId(), p.getType(), progressMonitor); 182 i++; 183 } 184 Collection<Way> ways = parents.getWays(); 185 186 if (!ways.isEmpty()) { 187 // Collect incomplete nodes of parent ways 188 Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete)) 189 .collect(Collectors.toSet()); 190 if (!nodes.isEmpty()) { 191 reader = MultiFetchServerObjectReader.create(); 192 ((MultiFetchServerObjectReader) reader).append(nodes); 193 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 194 synchronized (this) { // avoid race condition in cancel() 195 reader = null; 196 } 197 new DataSetMerger(parents, wayNodes).merge(); 198 } 199 } 200 } 201 } catch (OsmTransferException e) { 202 if (canceled) 203 return; 204 lastException = e; 205 } 206 } 207 208}