// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.actions.downloadtasks;
import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.DataSetMerger;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
import org.openstreetmap.josm.data.osm.PrimitiveId;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.PleaseWaitRunnable;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
import org.openstreetmap.josm.io.OsmServerReader;
import org.openstreetmap.josm.io.OsmTransferException;
import org.openstreetmap.josm.tools.CheckParameterUtil;
import org.openstreetmap.josm.tools.ExceptionUtil;
import org.xml.sax.SAXException;
/**
* The asynchronous task for downloading referring primitives
* @since 2923
*/
public class DownloadReferrersTask extends PleaseWaitRunnable {
private boolean canceled;
private Exception lastException;
private OsmServerReader reader;
/** the target layer */
private final OsmDataLayer targetLayer;
/** the collection of child primitives */
private final Map<Long, OsmPrimitiveType> children;
/** the parents */
private final DataSet parents;
/**
* constructor
*
* @param targetLayer the target layer for the downloaded primitives. Must not be null.
* @param children the collection of child primitives for which parents are to be downloaded
*/
public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) {
super("Download referrers", false /* don't ignore exception*/);
CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
canceled = false;
this.children = new HashMap<>();
if (children != null) {
for (OsmPrimitive p: children) {
if (!p.isNew()) {
this.children.put(p.getId(), OsmPrimitiveType.from(p));
}
}
}
this.targetLayer = targetLayer;
parents = new DataSet();
}
/**
* constructor
*
* @param targetLayer the target layer. Must not be null.
* @param primitiveId a PrimitiveId object.
* @param progressMonitor ProgressMonitor to use or null to create a new one.
* @throws IllegalArgumentException if id <= 0
* @throws IllegalArgumentException if targetLayer == null
*/
public DownloadReferrersTask(OsmDataLayer targetLayer, PrimitiveId primitiveId,
ProgressMonitor progressMonitor) {
super("Download referrers", progressMonitor, false /* don't ignore exception*/);
CheckParameterUtil.ensureParameterNotNull(targetLayer, "targetLayer");
if (primitiveId.isNew())
throw new IllegalArgumentException(MessageFormat.format(
"Cannot download referrers for new primitives (ID {0})", primitiveId.getUniqueId()));
canceled = false;
this.children = new HashMap<>();
this.children.put(primitiveId.getUniqueId(), primitiveId.getType());
this.targetLayer = targetLayer;
parents = new DataSet();
}
@Override
protected void cancel() {
canceled = true;
synchronized (this) {
if (reader != null) {
reader.cancel();
}
}
}
@Override
protected void finish() {
if (canceled)
return;
if (lastException != null) {
ExceptionUtil.explainException(lastException);
return;
}
DataSetMerger visitor = new DataSetMerger(targetLayer.data, parents);
visitor.merge();
SwingUtilities.invokeLater(targetLayer::onPostDownloadFromServer);
if (visitor.getConflicts().isEmpty())
return;
targetLayer.getConflicts().add(visitor.getConflicts());
JOptionPane.showMessageDialog(
Main.parent,
trn("There was {0} conflict during import.",
"There were {0} conflicts during import.",
visitor.getConflicts().size(),
visitor.getConflicts().size()
),
trn("Conflict during download", "Conflicts during download", visitor.getConflicts().size()),
JOptionPane.WARNING_MESSAGE
);
Main.map.conflictDialog.unfurlDialog();
Main.map.repaint();
}
protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException {
reader = new OsmServerBackreferenceReader(id, type);
DataSet ds = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
synchronized (this) { // avoid race condition in cancel()
reader = null;
}
Collection<Way> ways = ds.getWays();
DataSetMerger merger;
if (!ways.isEmpty()) {
Set<Node> nodes = new HashSet<>();
for (Way w: ways) {
// Ensure each node is only listed once
nodes.addAll(w.getNodes());
}
// Don't retrieve any nodes we've already grabbed
nodes.removeAll(targetLayer.data.getNodes());
if (!nodes.isEmpty()) {
reader = MultiFetchServerObjectReader.create();
((MultiFetchServerObjectReader) reader).append(nodes);
DataSet wayNodes = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
synchronized (this) { // avoid race condition in cancel()
reader = null;
}
merger = new DataSetMerger(ds, wayNodes);
merger.merge();
}
}
merger = new DataSetMerger(parents, ds);
merger.merge();
}
@Override
protected void realRun() throws SAXException, IOException, OsmTransferException {
try {
progressMonitor.setTicksCount(children.size());
int i = 1;
for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) {
if (canceled)
return;
String msg;
switch(entry.getValue()) {
case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1, children.size(), entry.getKey()); break;
case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1, children.size(), entry.getKey()); break;
case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1, children.size(), entry.getKey()); break;
default: throw new AssertionError();
}
progressMonitor.subTask(msg);
downloadParents(entry.getKey(), entry.getValue(), progressMonitor);
i++;
}
} catch (OsmTransferException e) {
if (canceled)
return;
lastException = e;
}
}
}