001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions.downloadtasks;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.io.IOException;
008import java.text.MessageFormat;
009import java.util.ArrayList;
010import java.util.Collection;
011import java.util.Collections;
012import java.util.List;
013
014import org.openstreetmap.josm.Main;
015import org.openstreetmap.josm.data.osm.Changeset;
016import org.openstreetmap.josm.data.osm.ChangesetCache;
017import org.openstreetmap.josm.data.osm.ChangesetDataSet;
018import org.openstreetmap.josm.gui.ExceptionDialogUtil;
019import org.openstreetmap.josm.io.OsmTransferCanceledException;
020import org.openstreetmap.josm.io.OsmTransferException;
021import org.openstreetmap.josm.tools.Logging;
022import org.xml.sax.SAXException;
023
024/**
025 * This is an asynchronous task for downloading the changeset content of a collection of changesets.
026 * @since 2689
027 */
028public class ChangesetContentDownloadTask extends AbstractChangesetDownloadTask {
029
030    class DownloadTask extends RunnableDownloadTask {
031        /** the list of changeset ids to download */
032        private final List<Integer> toDownload = new ArrayList<>();
033
034        DownloadTask(Component parent, Collection<Integer> ids) {
035            super(parent, tr("Downloading changeset content"));
036            for (Integer id: ids != null ? ids : Collections.<Integer>emptyList()) {
037                if (id == null || id <= 0) {
038                    continue;
039                }
040                toDownload.add(id);
041            }
042        }
043
044        /**
045         * Downloads the changeset with id <code>changesetId</code> (only "header" information, no content)
046         *
047         * @param changesetId the changeset id
048         * @throws OsmTransferException if something went wrong
049         */
050        protected void downloadChangeset(int changesetId) throws OsmTransferException {
051            Changeset cs = reader.readChangeset(changesetId, false, getProgressMonitor().createSubTaskMonitor(0, false));
052            ChangesetCache.getInstance().update(cs);
053        }
054
055        @Override
056        protected void realRun() throws SAXException, IOException, OsmTransferException {
057            try {
058                getProgressMonitor().setTicksCount(toDownload.size());
059                int i = 0;
060                for (int id: toDownload) {
061                    i++;
062                    if (!isAvailableLocally(id)) {
063                        getProgressMonitor().setCustomText(tr("({0}/{1}) Downloading changeset {2}...", i, toDownload.size(), id));
064                        downloadChangeset(id);
065                    }
066                    if (isCanceled())
067                        return;
068                    getProgressMonitor().setCustomText(tr("({0}/{1}) Downloading content for changeset {2}...", i, toDownload.size(), id));
069                    ChangesetDataSet ds = reader.downloadChangeset(id, getProgressMonitor().createSubTaskMonitor(0, false));
070                    Changeset cs = ChangesetCache.getInstance().get(id);
071                    cs.setContent(ds);
072                    ChangesetCache.getInstance().update(cs);
073                    downloadedChangesets.add(cs);
074                    getProgressMonitor().worked(1);
075                }
076            } catch (OsmTransferCanceledException e) {
077                // the download was canceled by the user. This exception is caught if the user canceled the authentication dialog.
078                setCanceled(true);
079                Logging.trace(e);
080                return;
081            } catch (OsmTransferException e) {
082                if (isCanceled())
083                    return;
084                rememberLastException(e);
085            }
086        }
087
088        @Override
089        protected void finish() {
090            rememberDownloadedData(downloadedChangesets);
091            if (isCanceled())
092                return;
093            if (lastException != null) {
094                ExceptionDialogUtil.explainException(lastException);
095            }
096        }
097    }
098
099    /**
100     * Creates a download task for a single changeset
101     *
102     * @param changesetId the changeset id. &gt; 0 required.
103     * @throws IllegalArgumentException if changesetId &lt;= 0
104     */
105    public ChangesetContentDownloadTask(int changesetId) {
106        this(Main.parent, changesetId);
107    }
108
109    /**
110     * Creates a download task for a collection of changesets. null values and id &lt;=0 in
111     * the collection are silently discarded.
112     *
113     * @param changesetIds the changeset ids. Empty collection assumed, if null.
114     */
115    public ChangesetContentDownloadTask(Collection<Integer> changesetIds) {
116        this(Main.parent, changesetIds);
117    }
118
119    /**
120     * Creates a download task for a single changeset
121     *
122     * @param parent the parent component for the {@link org.openstreetmap.josm.gui.PleaseWaitDialog}. Must not be {@code null}.
123     * @param changesetId the changeset id. {@code >0} required.
124     * @throws IllegalArgumentException if {@code changesetId <= 0}
125     * @throws IllegalArgumentException if parent is {@code null}
126     */
127    public ChangesetContentDownloadTask(Component parent, int changesetId) {
128        if (changesetId <= 0)
129            throw new IllegalArgumentException(
130                    MessageFormat.format("Expected integer value > 0 for parameter ''{0}'', got ''{1}''", "changesetId", changesetId));
131        setDownloadTask(new DownloadTask(parent, Collections.singleton(changesetId)));
132    }
133
134    /**
135     * Creates a download task for a collection of changesets. null values and id &lt;=0 in
136     * the collection are sillently discarded.
137     *
138     * @param parent the parent component for the {@link org.openstreetmap.josm.gui.PleaseWaitDialog}. Must not be {@code null}.
139     * @param changesetIds the changeset ids. Empty collection assumed, if {@code null}.
140     * @throws IllegalArgumentException if parent is {@code null}
141     */
142    public ChangesetContentDownloadTask(Component parent, Collection<Integer> changesetIds) {
143        setDownloadTask(new DownloadTask(parent, changesetIds));
144    }
145
146    /**
147     * Replies true if the local {@link ChangesetCache} already includes the changeset with
148     * id <code>changesetId</code>.
149     *
150     * @param changesetId the changeset id
151     * @return true if the local {@link ChangesetCache} already includes the changeset with
152     * id <code>changesetId</code>
153     */
154    protected static boolean isAvailableLocally(int changesetId) {
155        return ChangesetCache.getInstance().get(changesetId) != null;
156    }
157}