001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.relation; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.awt.Dialog; 008import java.io.IOException; 009import java.util.Collection; 010import java.util.HashSet; 011import java.util.Objects; 012import java.util.Set; 013 014import javax.swing.SwingUtilities; 015 016import org.openstreetmap.josm.data.osm.DataSet; 017import org.openstreetmap.josm.data.osm.DefaultNameFormatter; 018import org.openstreetmap.josm.data.osm.OsmPrimitive; 019import org.openstreetmap.josm.data.osm.Relation; 020import org.openstreetmap.josm.gui.ExceptionDialogUtil; 021import org.openstreetmap.josm.gui.MainApplication; 022import org.openstreetmap.josm.gui.PleaseWaitRunnable; 023import org.openstreetmap.josm.gui.layer.OsmDataLayer; 024import org.openstreetmap.josm.gui.progress.ProgressMonitor; 025import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 026import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 027import org.openstreetmap.josm.io.OsmTransferException; 028import org.openstreetmap.josm.tools.Logging; 029import org.xml.sax.SAXException; 030 031/** 032 * The asynchronous task for downloading relation members. 033 * @since 2563 034 */ 035public class DownloadRelationMemberTask extends PleaseWaitRunnable { 036 private boolean canceled; 037 private Exception lastException; 038 private final Set<Relation> parents = new HashSet<>(); 039 private final Collection<OsmPrimitive> children; 040 private final OsmDataLayer curLayer; 041 private MultiFetchServerObjectReader objectReader; 042 043 public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer, Dialog dialog) { 044 super(tr("Download relation members"), new PleaseWaitProgressMonitor(dialog), false /* don't ignore exception */); 045 if (parent != null) 046 this.parents.add(parent); 047 this.children = Objects.requireNonNull(children); 048 this.curLayer = Objects.requireNonNull(curLayer); 049 checkLayer(); 050 } 051 052 public DownloadRelationMemberTask(Relation parent, Collection<OsmPrimitive> children, OsmDataLayer curLayer) { 053 super(tr("Download relation members"), false /* don't ignore exception */); 054 if (parent != null) 055 this.parents.add(parent); 056 this.children = Objects.requireNonNull(children); 057 this.curLayer = Objects.requireNonNull(curLayer); 058 checkLayer(); 059 } 060 061 /** 062 * Creates a download task for downloading the child primitives {@code children} for all parent 063 * relations in {@code parents}. 064 * 065 * @param parents the collection of parent relations 066 * @param children the collection of child primitives to download 067 * @param curLayer the current OSM layer 068 */ 069 public DownloadRelationMemberTask(Collection<Relation> parents, Collection<OsmPrimitive> children, OsmDataLayer curLayer) { 070 super(tr("Download relation members"), false /* don't ignore exception */); 071 this.parents.addAll(parents); 072 this.children = Objects.requireNonNull(children); 073 this.curLayer = Objects.requireNonNull(curLayer); 074 checkLayer(); 075 } 076 077 private void checkLayer() { 078 if (!curLayer.isDownloadable()) { 079 throw new IllegalArgumentException("Non-downloadable layer: " + curLayer); 080 } 081 } 082 083 @Override 084 protected void cancel() { 085 canceled = true; 086 synchronized (this) { 087 if (objectReader != null) { 088 objectReader.cancel(); 089 } 090 } 091 } 092 093 @Override 094 protected void finish() { 095 MainApplication.getMap().repaint(); 096 if (canceled) 097 return; 098 if (lastException != null) { 099 ExceptionDialogUtil.explainException(lastException); 100 } 101 } 102 103 protected String buildDownloadFeedbackMessage() { 104 if (parents.isEmpty()) { 105 return trn("Downloading {0} incomplete object", 106 "Downloading {0} incomplete objects", 107 children.size(), 108 children.size()); 109 } else if (parents.size() == 1) { 110 Relation parent = parents.iterator().next(); 111 return trn("Downloading {0} incomplete child of relation ''{1}''", 112 "Downloading {0} incomplete children of relation ''{1}''", 113 children.size(), 114 children.size(), 115 parent.getDisplayName(DefaultNameFormatter.getInstance())); 116 } else { 117 return trn("Downloading {0} incomplete child of {1} parent relations", 118 "Downloading {0} incomplete children of {1} parent relations", 119 children.size(), 120 children.size(), 121 parents.size()); 122 } 123 } 124 125 @Override 126 protected void realRun() throws SAXException, IOException, OsmTransferException { 127 try { 128 synchronized (this) { 129 if (canceled) return; 130 objectReader = MultiFetchServerObjectReader.create(); 131 } 132 objectReader.setRecurseDownAppended(false).setRecurseDownRelations(false); 133 objectReader.append(children); 134 progressMonitor.indeterminateSubTask( 135 buildDownloadFeedbackMessage() 136 ); 137 final DataSet dataSet = objectReader.parseOsm(progressMonitor 138 .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 139 if (dataSet == null) 140 return; 141 dataSet.deleteInvisible(); 142 synchronized (this) { 143 if (canceled) return; 144 objectReader = null; 145 } 146 147 SwingUtilities.invokeLater(() -> { 148 curLayer.mergeFrom(dataSet); 149 curLayer.onPostDownloadFromServer(); 150 }); 151 } catch (OsmTransferException e) { 152 if (canceled) { 153 Logging.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString())); 154 return; 155 } 156 lastException = e; 157 } 158 } 159}