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.osm.DataSet; 018import org.openstreetmap.josm.data.osm.DataSetMerger; 019import org.openstreetmap.josm.data.osm.Node; 020import org.openstreetmap.josm.data.osm.OsmPrimitive; 021import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 022import org.openstreetmap.josm.data.osm.PrimitiveId; 023import org.openstreetmap.josm.data.osm.Way; 024import org.openstreetmap.josm.gui.MainApplication; 025import org.openstreetmap.josm.gui.MapFrame; 026import org.openstreetmap.josm.gui.PleaseWaitRunnable; 027import org.openstreetmap.josm.gui.layer.OsmDataLayer; 028import org.openstreetmap.josm.gui.progress.ProgressMonitor; 029import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 030import org.openstreetmap.josm.io.OsmServerBackreferenceReader; 031import org.openstreetmap.josm.io.OsmServerReader; 032import org.openstreetmap.josm.io.OsmTransferException; 033import org.openstreetmap.josm.tools.CheckParameterUtil; 034import org.openstreetmap.josm.tools.ExceptionUtil; 035import org.xml.sax.SAXException; 036 037/** 038 * The asynchronous task for downloading referring primitives 039 * @since 2923 040 */ 041public class DownloadReferrersTask extends PleaseWaitRunnable { 042 private boolean canceled; 043 private Exception lastException; 044 private OsmServerReader reader; 045 /** the target layer */ 046 private final OsmDataLayer targetLayer; 047 /** the collection of child primitives */ 048 private final Set<PrimitiveId> children; 049 /** the parents */ 050 private final DataSet parents; 051 052 /** 053 * constructor 054 * 055 * @param targetLayer the target layer for the downloaded primitives. Must not be null. 056 * @param children the collection of child primitives for which parents are to be downloaded 057 * @since 15787 (modified interface) 058 */ 059 public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<? extends PrimitiveId> children) { 060 super("Download referrers", false /* don't ignore exception*/); 061 CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer"); 062 if (!targetLayer.isDownloadable()) { 063 throw new IllegalArgumentException("Non-downloadable layer: " + targetLayer); 064 } 065 canceled = false; 066 this.children = new LinkedHashSet<>(); 067 if (children != null) { 068 children.stream().filter(p -> !p.isNew()).forEach(this.children::add); 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 LinkedHashSet<>(); 093 this.children.add(primitiveId); 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.getDataSet(), parents); 118 visitor.merge(); 119 SwingUtilities.invokeLater(targetLayer::onPostDownloadFromServer); 120 if (visitor.getConflicts().isEmpty()) 121 return; 122 targetLayer.getConflicts().add(visitor.getConflicts()); 123 JOptionPane.showMessageDialog( 124 MainApplication.getMainFrame(), 125 trn("There was {0} conflict during import.", 126 "There were {0} conflicts during import.", 127 visitor.getConflicts().size(), 128 visitor.getConflicts().size() 129 ), 130 trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()), 131 JOptionPane.WARNING_MESSAGE 132 ); 133 MapFrame map = MainApplication.getMap(); 134 map.conflictDialog.unfurlDialog(); 135 map.repaint(); 136 } 137 138 protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException { 139 reader = new OsmServerBackreferenceReader(id, type, false).setAllowIncompleteParentWays(true); 140 141 DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 142 synchronized (this) { // avoid race condition in cancel() 143 reader = null; 144 if (canceled) 145 return; 146 } 147 new DataSetMerger(parents, ds).merge(); 148 } 149 150 @Override 151 protected void realRun() throws SAXException, IOException, OsmTransferException { 152 try { 153 progressMonitor.setTicksCount(children.size()); 154 int i = 1; 155 for (PrimitiveId p : children) { 156 if (canceled) 157 return; 158 String msg; 159 String id = Long.toString(p.getUniqueId()); 160 switch(p.getType()) { 161 case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i, children.size(), id); break; 162 case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i, children.size(), id); break; 163 case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i, children.size(), id); break; 164 default: throw new AssertionError(); 165 } 166 progressMonitor.subTask(msg); 167 downloadParents(p.getUniqueId(), p.getType(), progressMonitor); 168 i++; 169 } 170 Collection<Way> ways = parents.getWays(); 171 172 if (!ways.isEmpty()) { 173 // Collect incomplete nodes of parent ways 174 Set<Node> nodes = ways.stream().flatMap(w -> w.getNodes().stream().filter(OsmPrimitive::isIncomplete)) 175 .collect(Collectors.toSet()); 176 if (!nodes.isEmpty()) { 177 reader = MultiFetchServerObjectReader.create(); 178 ((MultiFetchServerObjectReader) reader).append(nodes); 179 DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 180 synchronized (this) { // avoid race condition in cancel() 181 reader = null; 182 } 183 new DataSetMerger(parents, wayNodes).merge(); 184 } 185 } 186 } catch (OsmTransferException e) { 187 if (canceled) 188 return; 189 lastException = e; 190 } 191 } 192}