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.append(children);
133            progressMonitor.indeterminateSubTask(
134                    buildDownloadFeedbackMessage()
135            );
136            final DataSet dataSet = objectReader.parseOsm(progressMonitor
137                    .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
138            if (dataSet == null)
139                return;
140            dataSet.deleteInvisible();
141            synchronized (this) {
142                if (canceled) return;
143                objectReader = null;
144            }
145
146            SwingUtilities.invokeLater(() -> {
147                curLayer.mergeFrom(dataSet);
148                curLayer.onPostDownloadFromServer();
149            });
150        } catch (OsmTransferException e) {
151            if (canceled) {
152                Logging.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
153                return;
154            }
155            lastException = e;
156        }
157    }
158}