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