source: josm/trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java

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

see #19898: Performance improvements for DatasetMerger
Avoid to avoid to materialise unneeded copies of SubclassFilteredCollection

  • Property svn:eol-style set to native
File size: 11.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.io.InputStream;
8import java.text.MessageFormat;
9import java.util.ArrayList;
10import java.util.Collection;
11
12import org.openstreetmap.josm.data.osm.DataSet;
13import org.openstreetmap.josm.data.osm.DataSetMerger;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
16import org.openstreetmap.josm.data.osm.Relation;
17import org.openstreetmap.josm.data.osm.Way;
18import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
19import org.openstreetmap.josm.gui.progress.ProgressMonitor;
20import org.openstreetmap.josm.tools.CheckParameterUtil;
21
22/**
23 * OsmServerBackreferenceReader fetches the primitives from the OSM server which
24 * refer to a specific primitive. For a {@link org.openstreetmap.josm.data.osm.Node Node}, ways and relations are retrieved
25 * which refer to the node. For a {@link Way} or a {@link Relation}, only relations are read.
26 *
27 * OsmServerBackreferenceReader uses the API calls <code>[node|way|relation]/#id/relations</code>
28 * and <code>node/#id/ways</code> to retrieve the referring primitives. The default behaviour
29 * of these calls is to reply incomplete primitives only.
30 *
31 * If you set {@link #setReadFull(boolean)} to true this reader uses a {@link MultiFetchServerObjectReader}
32 * to complete incomplete primitives.
33 *
34 * @since 1806
35 */
36public class OsmServerBackreferenceReader extends OsmServerReader {
37
38 /** the id of the primitive whose referrers are to be read */
39 private final long id;
40 /** the type of the primitive */
41 private final OsmPrimitiveType primitiveType;
42 /** true if this reader should complete incomplete primitives */
43 private boolean readFull;
44 /** true if this reader should allow incomplete parent ways */
45 private boolean allowIncompleteParentWays;
46
47 /**
48 * constructor
49 *
50 * @param primitive the primitive to be read. Must not be null. primitive.id &gt; 0 expected
51 *
52 * @throws IllegalArgumentException if primitive is null
53 * @throws IllegalArgumentException if primitive.id &lt;= 0
54 */
55 public OsmServerBackreferenceReader(OsmPrimitive primitive) {
56 CheckParameterUtil.ensureThat(primitive.getUniqueId() > 0, "id > 0");
57 this.id = primitive.getId();
58 this.primitiveType = OsmPrimitiveType.from(primitive);
59 this.readFull = false;
60 }
61
62 /**
63 * constructor
64 *
65 * @param id the id of the primitive. &gt; 0 expected
66 * @param type the type of the primitive. Must not be null.
67 *
68 * @throws IllegalArgumentException if id &lt;= 0
69 * @throws IllegalArgumentException if type is null
70 */
71 public OsmServerBackreferenceReader(long id, OsmPrimitiveType type) {
72 if (id <= 0)
73 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected. Got ''{1}''.", "id", id));
74 CheckParameterUtil.ensureParameterNotNull(type, "type");
75 this.id = id;
76 this.primitiveType = type;
77 this.readFull = false;
78 }
79
80 /**
81 * Creates a back reference reader for given primitive
82 *
83 * @param primitive the primitive
84 * @param readFull <code>true</code>, if referrers should be read fully (i.e. including their immediate children)
85 *
86 */
87 public OsmServerBackreferenceReader(OsmPrimitive primitive, boolean readFull) {
88 this(primitive);
89 this.readFull = readFull;
90 }
91
92 /**
93 * Creates a back reference reader for given primitive id
94 *
95 * @param id the id of the primitive whose referrers are to be read
96 * @param type the type of the primitive
97 * @param readFull true, if referrers should be read fully (i.e. including their immediate children)
98 *
99 * @throws IllegalArgumentException if id &lt;= 0
100 * @throws IllegalArgumentException if type is null
101 */
102 public OsmServerBackreferenceReader(long id, OsmPrimitiveType type, boolean readFull) {
103 this(id, type);
104 this.readFull = readFull;
105 }
106
107 /**
108 * Replies true if this reader also reads immediate children of referring primitives
109 *
110 * @return true if this reader also reads immediate children of referring primitives
111 */
112 public boolean isReadFull() {
113 return readFull;
114 }
115
116 /**
117 * Set true if this reader should reads immediate children of referring primitives too. False, otherwise.
118 *
119 * @param readFull true if this reader should reads immediate children of referring primitives too. False, otherwise.
120 * @return {@code this}, for easy chaining
121 * @since 15426
122 */
123 public OsmServerBackreferenceReader setReadFull(boolean readFull) {
124 this.readFull = readFull;
125 return this;
126 }
127
128 /**
129 * Determines if this reader allows to return incomplete parent ways of a node.
130 * @return {@code true} if this reader allows to return incomplete parent ways of a node
131 * @since 15426
132 */
133 public boolean isAllowIncompleteParentWays() {
134 return allowIncompleteParentWays;
135 }
136
137 /**
138 * Sets whether this reader allows to return incomplete parent ways of a node.
139 * @param allowIncompleteWays {@code true} if this reader allows to return incomplete parent ways of a node
140 * @return {@code this}, for easy chaining
141 * @since 15426
142 */
143 public OsmServerBackreferenceReader setAllowIncompleteParentWays(boolean allowIncompleteWays) {
144 this.allowIncompleteParentWays = allowIncompleteWays;
145 return this;
146 }
147
148 private DataSet getReferringPrimitives(ProgressMonitor progressMonitor, String type, String message) throws OsmTransferException {
149 progressMonitor.beginTask(null, 2);
150 try {
151 progressMonitor.subTask(tr("Contacting OSM Server..."));
152 StringBuilder sb = new StringBuilder();
153 sb.append(primitiveType.getAPIName()).append('/').append(id).append(type);
154
155 try (InputStream in = getInputStream(sb.toString(), progressMonitor.createSubTaskMonitor(1, true))) {
156 if (in == null)
157 return null;
158 progressMonitor.subTask(message);
159 return OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, true));
160 }
161 } catch (OsmTransferException e) {
162 throw e;
163 } catch (IOException | IllegalDataException e) {
164 if (cancel)
165 return null;
166 throw new OsmTransferException(e);
167 } finally {
168 progressMonitor.finishTask();
169 activeConnection = null;
170 }
171 }
172
173 /**
174 * Reads referring ways from the API server and replies them in a {@link DataSet}
175 *
176 * @param progressMonitor progress monitor
177 * @return the data set
178 * @throws OsmTransferException if any error occurs during dialog with OSM API
179 */
180 protected DataSet getReferringWays(ProgressMonitor progressMonitor) throws OsmTransferException {
181 return getReferringPrimitives(progressMonitor, "/ways", tr("Downloading referring ways ..."));
182 }
183
184 /**
185 * Reads referring relations from the API server and replies them in a {@link DataSet}
186 *
187 * @param progressMonitor the progress monitor
188 * @return the data set
189 * @throws OsmTransferException if any error occurs during dialog with OSM API
190 */
191 protected DataSet getReferringRelations(ProgressMonitor progressMonitor) throws OsmTransferException {
192 return getReferringPrimitives(progressMonitor, "/relations", tr("Downloading referring relations ..."));
193 }
194
195 /**
196 * Scans a dataset for incomplete primitives. Depending on the configuration of this reader
197 * incomplete primitives are read from the server with an individual <code>/api/0.6/[way,relation]/#id/full</code>
198 * request.
199 *
200 * <ul>
201 * <li>if this reader reads referrers for a {@link org.openstreetmap.josm.data.osm.Node}, referring ways are always
202 * read fully, unless {@link #setAllowIncompleteParentWays(boolean)} is set to true.</li>
203 * <li>if this reader reads referrers for an {@link Way} or a {@link Relation}, referring relations
204 * are only read fully if {@link #setReadFull(boolean)} is set to true.</li>
205 * </ul>
206 *
207 * The method replies the modified dataset.
208 *
209 * @param ds the original dataset
210 * @param progressMonitor the progress monitor
211 * @return the modified dataset
212 * @throws OsmTransferException if an exception occurs.
213 */
214 protected DataSet readIncompletePrimitives(DataSet ds, ProgressMonitor progressMonitor) throws OsmTransferException {
215 progressMonitor.beginTask(null, 2);
216 try {
217 if (isReadFull() || (primitiveType == OsmPrimitiveType.NODE && !isAllowIncompleteParentWays())) {
218 Collection<Way> waysToCheck = new ArrayList<>(ds.getWays());
219 for (Way way: waysToCheck) {
220 if (!way.isNew() && way.hasIncompleteNodes()) {
221 OsmServerObjectReader reader = new OsmServerObjectReader(way.getId(), OsmPrimitiveType.from(way), true /* read full */);
222 DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
223 DataSetMerger visitor = new DataSetMerger(ds, wayDs);
224 visitor.merge();
225 }
226 }
227 }
228 if (isReadFull()) {
229 Collection<Relation> relationsToCheck = new ArrayList<>(ds.getRelations());
230 for (Relation relation: relationsToCheck) {
231 if (!relation.isNew() && relation.hasIncompleteMembers()) {
232 OsmServerObjectReader reader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.from(relation), true);
233 DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
234 DataSetMerger visitor = new DataSetMerger(ds, wayDs);
235 visitor.merge();
236 }
237 }
238 }
239 return ds;
240 } finally {
241 progressMonitor.finishTask();
242 }
243 }
244
245 /**
246 * Reads the referring primitives from the OSM server, parses them and
247 * replies them as {@link DataSet}
248 *
249 * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null.
250 * @return the dataset with the referring primitives
251 * @throws OsmTransferException if an error occurs while communicating with the server
252 */
253 @Override
254 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
255 if (progressMonitor == null) {
256 progressMonitor = NullProgressMonitor.INSTANCE;
257 }
258 try {
259 progressMonitor.beginTask(null, 3);
260 DataSet ret = new DataSet();
261 if (primitiveType == OsmPrimitiveType.NODE) {
262 DataSet ds = getReferringWays(progressMonitor.createSubTaskMonitor(1, false));
263 DataSetMerger visitor = new DataSetMerger(ret, ds);
264 visitor.merge();
265 ret = visitor.getTargetDataSet();
266 }
267 DataSet ds = getReferringRelations(progressMonitor.createSubTaskMonitor(1, false));
268 DataSetMerger visitor = new DataSetMerger(ret, ds);
269 visitor.merge();
270 ret = visitor.getTargetDataSet();
271 if (ret != null) {
272 readIncompletePrimitives(ret, progressMonitor.createSubTaskMonitor(1, false));
273 ret.deleteInvisible();
274 }
275 return ret;
276 } finally {
277 progressMonitor.finishTask();
278 }
279 }
280}
Note: See TracBrowser for help on using the repository browser.