source: josm/trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java

Last change on this file was 16211, checked in by GerdP, 4 years ago

fix javadoc

  • Property svn:eol-style set to native
File size: 10.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.history;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Component;
8import java.io.IOException;
9import java.net.HttpURLConnection;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.LinkedHashSet;
13import java.util.List;
14import java.util.Objects;
15import java.util.Set;
16
17import org.openstreetmap.josm.data.osm.Changeset;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.PrimitiveId;
20import org.openstreetmap.josm.data.osm.history.History;
21import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
22import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
23import org.openstreetmap.josm.gui.ExceptionDialogUtil;
24import org.openstreetmap.josm.gui.PleaseWaitRunnable;
25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
26import org.openstreetmap.josm.io.ChangesetQuery;
27import org.openstreetmap.josm.io.OsmApiException;
28import org.openstreetmap.josm.io.OsmServerChangesetReader;
29import org.openstreetmap.josm.io.OsmServerHistoryReader;
30import org.openstreetmap.josm.io.OsmTransferException;
31import org.openstreetmap.josm.tools.CheckParameterUtil;
32import org.xml.sax.SAXException;
33
34/**
35 * Loads the object history of a collection of objects from the server.
36 *
37 * It provides a fluent API for configuration.
38 *
39 * Sample usage:
40 *
41 * <pre>
42 * HistoryLoadTask task = new HistoryLoadTask()
43 * .add(node)
44 * .add(way)
45 * .add(relation)
46 * .add(aHistoryItem);
47 *
48 * MainApplication.worker.execute(task);
49 * </pre>
50 */
51public class HistoryLoadTask extends PleaseWaitRunnable {
52
53 private boolean canceled;
54 private Exception lastException;
55 private final Set<PrimitiveId> toLoad = new LinkedHashSet<>();
56 private HistoryDataSet loadedData;
57 private OsmServerHistoryReader reader;
58 private boolean getChangesetData = true;
59 private boolean collectMissing;
60 private final Set<PrimitiveId> missingPrimitives = new LinkedHashSet<>();
61
62 /**
63 * Constructs a new {@code HistoryLoadTask}.
64 */
65 public HistoryLoadTask() {
66 super(tr("Load history"), true);
67 }
68
69 /**
70 * Constructs a new {@code HistoryLoadTask}.
71 *
72 * @param parent the component to be used as reference to find the
73 * parent for {@link org.openstreetmap.josm.gui.PleaseWaitDialog}.
74 * Must not be <code>null</code>.
75 * @throws NullPointerException if parent is <code>null</code>
76 */
77 public HistoryLoadTask(Component parent) {
78 super(Objects.requireNonNull(parent, "parent"), tr("Load history"), true);
79 }
80
81 /**
82 * Adds an object whose history is to be loaded.
83 *
84 * @param pid the primitive id. Must not be null. Id &gt; 0 required.
85 * @return this task
86 */
87 public HistoryLoadTask add(PrimitiveId pid) {
88 CheckParameterUtil.ensureThat(pid.getUniqueId() > 0, "id > 0");
89 toLoad.add(pid);
90 return this;
91 }
92
93 /**
94 * Adds an object to be loaded, the object is specified by a history item.
95 *
96 * @param primitive the history item
97 * @return this task
98 * @throws NullPointerException if primitive is null
99 */
100 public HistoryLoadTask add(HistoryOsmPrimitive primitive) {
101 return add(primitive.getPrimitiveId());
102 }
103
104 /**
105 * Adds an object to be loaded, the object is specified by an already loaded object history.
106 *
107 * @param history the history. Must not be null.
108 * @return this task
109 * @throws NullPointerException if history is null
110 */
111 public HistoryLoadTask add(History history) {
112 return add(history.getPrimitiveId());
113 }
114
115 /**
116 * Adds an object to be loaded, the object is specified by an OSM primitive.
117 *
118 * @param primitive the OSM primitive. Must not be null. primitive.getOsmId() &gt; 0 required.
119 * @return this task
120 * @throws NullPointerException if the primitive is null
121 * @throws IllegalArgumentException if primitive.getOsmId() &lt;= 0
122 */
123 public HistoryLoadTask add(OsmPrimitive primitive) {
124 CheckParameterUtil.ensureThat(primitive.getOsmId() > 0, "id > 0");
125 return add(primitive.getOsmPrimitiveId());
126 }
127
128 /**
129 * Adds a collection of objects to loaded, specified by a collection of OSM primitives.
130 *
131 * @param primitives the OSM primitives. Must not be <code>null</code>.
132 * <code>primitive.getId() &gt; 0</code> required.
133 * @return this task
134 * @throws NullPointerException if primitives is null
135 * @throws IllegalArgumentException if one of the ids in the collection &lt;= 0
136 * @since 16123
137 */
138 public HistoryLoadTask addPrimitiveIds(Collection<? extends PrimitiveId> primitives) {
139 primitives.forEach(this::add);
140 return this;
141 }
142
143 /**
144 * Adds a collection of objects to loaded, specified by a collection of OSM primitives.
145 *
146 * @param primitives the OSM primitives. Must not be <code>null</code>.
147 * <code>primitive.getId() &gt; 0</code> required.
148 * @return this task
149 * @throws NullPointerException if primitives is null
150 * @throws IllegalArgumentException if one of the ids in the collection &lt;= 0
151 * @since 16123
152 */
153 public HistoryLoadTask addOsmPrimitives(Collection<? extends OsmPrimitive> primitives) {
154 primitives.forEach(this::add);
155 return this;
156 }
157
158 @Override
159 protected void cancel() {
160 if (reader != null) {
161 reader.cancel();
162 }
163 canceled = true;
164 }
165
166 @Override
167 protected void finish() {
168 if (isCanceled())
169 return;
170 if (lastException != null) {
171 ExceptionDialogUtil.explainException(lastException);
172 return;
173 }
174 HistoryDataSet.getInstance().mergeInto(loadedData);
175 }
176
177 @Override
178 protected void realRun() throws SAXException, IOException, OsmTransferException {
179 loadedData = new HistoryDataSet();
180 int ticks = toLoad.size();
181 if (getChangesetData)
182 ticks *= 2;
183 try {
184 progressMonitor.setTicksCount(ticks);
185 for (PrimitiveId pid: toLoad) {
186 if (canceled) {
187 break;
188 }
189 loadHistory(pid);
190 }
191 } catch (OsmTransferException e) {
192 lastException = e;
193 }
194 }
195
196 private void loadHistory(PrimitiveId pid) throws OsmTransferException {
197 String msg = getLoadingMessage(pid);
198 progressMonitor.indeterminateSubTask(tr(msg, Long.toString(pid.getUniqueId())));
199 reader = null;
200 HistoryDataSet ds = null;
201 try {
202 reader = new OsmServerHistoryReader(pid.getType(), pid.getUniqueId());
203 if (getChangesetData) {
204 ds = loadHistory(reader, progressMonitor);
205 } else {
206 ds = reader.parseHistory(progressMonitor.createSubTaskMonitor(1, false));
207 }
208 } catch (OsmApiException e) {
209 if (canceled)
210 return;
211 if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND && collectMissing) {
212 missingPrimitives.add(pid);
213 } else {
214 throw e;
215 }
216 } catch (OsmTransferException e) {
217 if (canceled)
218 return;
219 throw e;
220 }
221 if (ds != null) {
222 loadedData.mergeInto(ds);
223 }
224 }
225
226 protected static HistoryDataSet loadHistory(OsmServerHistoryReader reader, ProgressMonitor progressMonitor) throws OsmTransferException {
227 HistoryDataSet ds = reader.parseHistory(progressMonitor.createSubTaskMonitor(1, false));
228 if (ds != null) {
229 // load corresponding changesets (mostly for changeset comment)
230 OsmServerChangesetReader changesetReader = new OsmServerChangesetReader();
231 List<Long> changesetIds = new ArrayList<>(ds.getChangesetIds());
232
233 // query changesets 100 by 100 (OSM API limit)
234 int n = ChangesetQuery.MAX_CHANGESETS_NUMBER;
235 for (int i = 0; i < changesetIds.size(); i += n) {
236 for (Changeset c : changesetReader.queryChangesets(
237 new ChangesetQuery().forChangesetIds(changesetIds.subList(i, Math.min(i + n, changesetIds.size()))),
238 progressMonitor.createSubTaskMonitor(1, false))) {
239 ds.putChangeset(c);
240 }
241 }
242 }
243 return ds;
244 }
245
246 protected static String getLoadingMessage(PrimitiveId pid) {
247 switch (pid.getType()) {
248 case NODE:
249 return marktr("Loading history for node {0}");
250 case WAY:
251 return marktr("Loading history for way {0}");
252 case RELATION:
253 return marktr("Loading history for relation {0}");
254 default:
255 return "";
256 }
257 }
258
259 /**
260 * Determines if this task has ben canceled.
261 * @return {@code true} if this task has ben canceled
262 */
263 public boolean isCanceled() {
264 return canceled;
265 }
266
267 /**
268 * Returns the last exception that occurred during loading, if any.
269 * @return the last exception that occurred during loading, or {@code null}
270 */
271 public Exception getLastException() {
272 return lastException;
273 }
274
275 /**
276 * Determine if changeset information is needed. By default it is retrieved.
277 * @param b false means don't retrieve changeset data.
278 * @since 14763
279 */
280 public void setChangesetDataNeeded(boolean b) {
281 getChangesetData = b;
282 }
283
284 /**
285 * Determine if missing primitives should be collected. By default they are not collected
286 * and the first missing object terminates the task.
287 * @param b true means collect missing data and continue.
288 * @since 16205
289 */
290 public void setCollectMissing(boolean b) {
291 collectMissing = b;
292 }
293
294 /**
295 * replies the set of ids of all primitives for which a fetch request to the
296 * server was submitted but which are not available from the server (the server
297 * replied a return code of 404)
298 * @return the set of ids of missing primitives
299 * @since 16205
300 */
301 public Set<PrimitiveId> getMissingPrimitives() {
302 return missingPrimitives;
303 }
304
305}
Note: See TracBrowser for help on using the repository browser.