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