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