001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006
007import java.io.IOException;
008import java.io.InputStream;
009import java.text.MessageFormat;
010import java.util.ArrayList;
011import java.util.Collection;
012import java.util.Collections;
013import java.util.List;
014
015import org.openstreetmap.josm.Main;
016import org.openstreetmap.josm.data.osm.Changeset;
017import org.openstreetmap.josm.data.osm.ChangesetDataSet;
018import org.openstreetmap.josm.data.osm.DataSet;
019import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
020import org.openstreetmap.josm.gui.progress.ProgressMonitor;
021import org.openstreetmap.josm.tools.CheckParameterUtil;
022import org.openstreetmap.josm.tools.XmlParsingException;
023
024/**
025 * Reads the history of an {@link org.openstreetmap.josm.data.osm.OsmPrimitive} from the OSM API server.
026 *
027 */
028public class OsmServerChangesetReader extends OsmServerReader {
029
030    /**
031     * Constructs a new {@code OsmServerChangesetReader}.
032     */
033    public OsmServerChangesetReader() {
034        setDoAuthenticate(false);
035    }
036
037    /**
038     * don't use - not implemented!
039     */
040    @Override
041    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
042        return null;
043    }
044
045    protected final InputStream getChangesetInputStream(long id, boolean includeDiscussion, ProgressMonitor monitor)
046            throws OsmTransferException {
047        StringBuilder sb = new StringBuilder();
048        sb.append("changeset/").append(id);
049        if (includeDiscussion) {
050            sb.append("?include_discussion=true");
051        }
052        return getInputStream(sb.toString(), monitor.createSubTaskMonitor(1, true));
053    }
054
055    /**
056     * Queries a list
057     * @param query  the query specification. Must not be null.
058     * @param monitor a progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
059     * @return the list of changesets read from the server
060     * @throws IllegalArgumentException if query is null
061     * @throws OsmTransferException if something goes wrong w
062     */
063    public List<Changeset> queryChangesets(ChangesetQuery query, ProgressMonitor monitor) throws OsmTransferException {
064        CheckParameterUtil.ensureParameterNotNull(query, "query");
065        List<Changeset> result = null;
066        if (monitor == null) {
067            monitor = NullProgressMonitor.INSTANCE;
068        }
069        try {
070            monitor.beginTask(tr("Reading changesets..."));
071            StringBuilder sb = new StringBuilder();
072            sb.append("changesets?").append(query.getQueryString());
073            try (InputStream in = getInputStream(sb.toString(), monitor.createSubTaskMonitor(1, true))) {
074                if (in == null)
075                    return null;
076                monitor.indeterminateSubTask(tr("Downloading changesets ..."));
077                result = OsmChangesetParser.parse(in, monitor.createSubTaskMonitor(1, true));
078            } catch (IOException e) {
079                Main.warn(e);
080            }
081        } catch (OsmTransferException e) {
082            throw e;
083        } catch (IllegalDataException e) {
084            throw new OsmTransferException(e);
085        } finally {
086            monitor.finishTask();
087        }
088        return result;
089    }
090
091    /**
092     * Reads the changeset with id <code>id</code> from the server.
093     *
094     * @param id the changeset id. id &gt; 0 required.
095     * @param includeDiscussion determines if discussion comments must be downloaded or not
096     * @param monitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
097     * @return the changeset read
098     * @throws OsmTransferException if something goes wrong
099     * @throws IllegalArgumentException if id &lt;= 0
100     * @since 7704
101     */
102    public Changeset readChangeset(long id, boolean includeDiscussion, ProgressMonitor monitor) throws OsmTransferException {
103        if (id <= 0)
104            throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected. Got ''{1}''.", "id", id));
105        if (monitor == null) {
106            monitor = NullProgressMonitor.INSTANCE;
107        }
108        Changeset result = null;
109        try {
110            monitor.beginTask(tr("Reading changeset {0} ...", id));
111            try (InputStream in = getChangesetInputStream(id, includeDiscussion, monitor)) {
112                if (in == null)
113                    return null;
114                monitor.indeterminateSubTask(tr("Downloading changeset {0} ...", id));
115                List<Changeset> changesets = OsmChangesetParser.parse(in, monitor.createSubTaskMonitor(1, true));
116                if (changesets == null || changesets.isEmpty())
117                    return null;
118                result = changesets.get(0);
119            } catch (IOException e) {
120                Main.warn(e);
121            }
122        } catch (OsmTransferException e) {
123            throw e;
124        } catch (IllegalDataException e) {
125            throw new OsmTransferException(e);
126        } finally {
127            monitor.finishTask();
128        }
129        return result;
130    }
131
132    /**
133     * Reads the changesets with id <code>ids</code> from the server.
134     *
135     * @param ids the list of ids. Ignored if null. Only load changesets for ids &gt; 0.
136     * @param includeDiscussion determines if discussion comments must be downloaded or not
137     * @param monitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
138     * @return the changeset read
139     * @throws OsmTransferException if something goes wrong
140     * @throws IllegalArgumentException if id &lt;= 0
141     * @since 7704
142     */
143    public List<Changeset> readChangesets(Collection<Integer> ids, boolean includeDiscussion, ProgressMonitor monitor)
144            throws OsmTransferException {
145        if (ids == null)
146            return Collections.emptyList();
147        if (monitor == null) {
148            monitor = NullProgressMonitor.INSTANCE;
149        }
150        try {
151            monitor.beginTask(trn("Downloading {0} changeset ...", "Downloading {0} changesets ...", ids.size(), ids.size()));
152            monitor.setTicksCount(ids.size());
153            List<Changeset> ret = new ArrayList<>();
154            int i = 0;
155            for (int id : ids) {
156                if (id <= 0) {
157                    continue;
158                }
159                i++;
160                try (InputStream in = getChangesetInputStream(id, includeDiscussion, monitor)) {
161                    if (in == null)
162                        return null;
163                    monitor.indeterminateSubTask(tr("({0}/{1}) Downloading changeset {2} ...", i, ids.size(), id));
164                    List<Changeset> changesets = OsmChangesetParser.parse(in, monitor.createSubTaskMonitor(1, true));
165                    if (changesets == null || changesets.isEmpty()) {
166                        continue;
167                    }
168                    ret.addAll(changesets);
169                } catch (IOException e) {
170                    Main.warn(e);
171                }
172                monitor.worked(1);
173            }
174            return ret;
175        } catch (OsmTransferException e) {
176            throw e;
177        } catch (IllegalDataException e) {
178            throw new OsmTransferException(e);
179        } finally {
180            monitor.finishTask();
181        }
182    }
183
184    /**
185     * Downloads the content of a changeset
186     *
187     * @param id the changeset id. &gt; 0 required.
188     * @param monitor the progress monitor. {@link NullProgressMonitor#INSTANCE} assumed if null.
189     * @return the changeset content
190     * @throws IllegalArgumentException if id &lt;= 0
191     * @throws OsmTransferException if something went wrong
192     */
193    public ChangesetDataSet downloadChangeset(int id, ProgressMonitor monitor) throws OsmTransferException {
194        if (id <= 0)
195            throw new IllegalArgumentException(
196                    MessageFormat.format("Expected value of type integer > 0 for parameter ''{0}'', got {1}", "id", id));
197        if (monitor == null) {
198            monitor = NullProgressMonitor.INSTANCE;
199        }
200        ChangesetDataSet result = null;
201        try {
202            monitor.beginTask(tr("Downloading changeset content"));
203            StringBuilder sb = new StringBuilder();
204            sb.append("changeset/").append(id).append("/download");
205            try (InputStream in = getInputStream(sb.toString(), monitor.createSubTaskMonitor(1, true))) {
206                if (in == null)
207                    return null;
208                monitor.setCustomText(tr("Downloading content for changeset {0} ...", id));
209                OsmChangesetContentParser parser = new OsmChangesetContentParser(in);
210                result = parser.parse(monitor.createSubTaskMonitor(1, true));
211            } catch (IOException e) {
212                Main.warn(e);
213            }
214        } catch (XmlParsingException e) {
215            throw new OsmTransferException(e);
216        } finally {
217            monitor.finishTask();
218        }
219        return result;
220    }
221}