source: josm/trunk/src/org/openstreetmap/josm/io/AbstractReader.java@ 14716

Last change on this file since 14716 was 14716, checked in by GerdP, 5 years ago

see #17201: Improve progress monitor: Let OsmImporter decide what task is following the parser

  • Property svn:eol-style set to native
File size: 29.8 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.io.InputStreamReader;
9import java.text.MessageFormat;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.HashMap;
13import java.util.List;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.OptionalLong;
17import java.util.function.Consumer;
18
19import org.openstreetmap.josm.data.Bounds;
20import org.openstreetmap.josm.data.DataSource;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.data.osm.AbstractPrimitive;
23import org.openstreetmap.josm.data.osm.Changeset;
24import org.openstreetmap.josm.data.osm.DataSet;
25import org.openstreetmap.josm.data.osm.DownloadPolicy;
26import org.openstreetmap.josm.data.osm.Node;
27import org.openstreetmap.josm.data.osm.NodeData;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
30import org.openstreetmap.josm.data.osm.PrimitiveData;
31import org.openstreetmap.josm.data.osm.PrimitiveId;
32import org.openstreetmap.josm.data.osm.Relation;
33import org.openstreetmap.josm.data.osm.RelationData;
34import org.openstreetmap.josm.data.osm.RelationMember;
35import org.openstreetmap.josm.data.osm.RelationMemberData;
36import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
37import org.openstreetmap.josm.data.osm.Tagged;
38import org.openstreetmap.josm.data.osm.UploadPolicy;
39import org.openstreetmap.josm.data.osm.User;
40import org.openstreetmap.josm.data.osm.Way;
41import org.openstreetmap.josm.data.osm.WayData;
42import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
43import org.openstreetmap.josm.gui.progress.ProgressMonitor;
44import org.openstreetmap.josm.tools.CheckParameterUtil;
45import org.openstreetmap.josm.tools.Logging;
46import org.openstreetmap.josm.tools.Utils;
47import org.openstreetmap.josm.tools.date.DateUtils;
48
49/**
50 * Abstract Reader, allowing other implementations than OsmReader (PbfReader in PBF plugin for example)
51 * @author Vincent
52 * @since 4490
53 */
54public abstract class AbstractReader {
55
56 /** Used by plugins to register themselves as data postprocessors. */
57 private static volatile List<OsmServerReadPostprocessor> postprocessors;
58
59 protected boolean cancel;
60
61 /**
62 * Register a new postprocessor.
63 * @param pp postprocessor
64 * @see #deregisterPostprocessor
65 * @since 14119 (moved from OsmReader)
66 */
67 public static void registerPostprocessor(OsmServerReadPostprocessor pp) {
68 if (postprocessors == null) {
69 postprocessors = new ArrayList<>();
70 }
71 postprocessors.add(pp);
72 }
73
74 /**
75 * Deregister a postprocessor previously registered with {@link #registerPostprocessor}.
76 * @param pp postprocessor
77 * @see #registerPostprocessor
78 * @since 14119 (moved from OsmReader)
79 */
80 public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) {
81 if (postprocessors != null) {
82 postprocessors.remove(pp);
83 }
84 }
85
86 /**
87 * The dataset to add parsed objects to.
88 */
89 protected DataSet ds = new DataSet();
90
91 protected Changeset uploadChangeset;
92
93 /** the map from external ids to read OsmPrimitives. External ids are
94 * longs too, but in contrast to internal ids negative values are used
95 * to identify primitives unknown to the OSM server
96 */
97 protected final Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<>();
98
99 /**
100 * Data structure for the remaining way objects
101 */
102 protected final Map<Long, Collection<Long>> ways = new HashMap<>();
103
104 /**
105 * Data structure for relation objects
106 */
107 protected final Map<Long, Collection<RelationMemberData>> relations = new HashMap<>();
108
109 /**
110 * Replies the parsed data set
111 *
112 * @return the parsed data set
113 */
114 public DataSet getDataSet() {
115 return ds;
116 }
117
118 /**
119 * Iterate over registered postprocessors and give them each a chance to modify the dataset we have just loaded.
120 * @param progressMonitor Progress monitor
121 */
122 protected void callPostProcessors(ProgressMonitor progressMonitor) {
123 if (postprocessors != null) {
124 for (OsmServerReadPostprocessor pp : postprocessors) {
125 pp.postprocessDataSet(getDataSet(), progressMonitor);
126 }
127 }
128 }
129
130 /**
131 * Processes the parsed nodes after parsing. Just adds them to
132 * the dataset
133 *
134 */
135 protected void processNodesAfterParsing() {
136 for (OsmPrimitive primitive: externalIdMap.values()) {
137 if (primitive instanceof Node) {
138 this.ds.addPrimitive(primitive);
139 }
140 }
141 }
142
143 /**
144 * Processes the ways after parsing. Rebuilds the list of nodes of each way and
145 * adds the way to the dataset
146 *
147 * @throws IllegalDataException if a data integrity problem is detected
148 */
149 protected void processWaysAfterParsing() throws IllegalDataException {
150 for (Entry<Long, Collection<Long>> entry : ways.entrySet()) {
151 Long externalWayId = entry.getKey();
152 Way w = (Way) externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY));
153 List<Node> wayNodes = new ArrayList<>();
154 for (long id : entry.getValue()) {
155 Node n = (Node) externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE));
156 if (n == null) {
157 if (id <= 0)
158 throw new IllegalDataException(
159 tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.",
160 Long.toString(externalWayId),
161 Long.toString(id)));
162 // create an incomplete node if necessary
163 n = (Node) ds.getPrimitiveById(id, OsmPrimitiveType.NODE);
164 if (n == null) {
165 n = new Node(id);
166 ds.addPrimitive(n);
167 }
168 }
169 if (n.isDeleted()) {
170 Logging.info(tr("Deleted node {0} is part of way {1}", Long.toString(id), Long.toString(w.getId())));
171 } else {
172 wayNodes.add(n);
173 }
174 }
175 w.setNodes(wayNodes);
176 if (w.hasIncompleteNodes()) {
177 Logging.info(tr("Way {0} with {1} nodes is incomplete because at least one node was missing in the loaded data.",
178 Long.toString(externalWayId), w.getNodesCount()));
179 }
180 ds.addPrimitive(w);
181 }
182 }
183
184 /**
185 * Completes the parsed relations with its members.
186 *
187 * @throws IllegalDataException if a data integrity problem is detected, i.e. if a
188 * relation member refers to a local primitive which wasn't available in the data
189 */
190 protected void processRelationsAfterParsing() throws IllegalDataException {
191
192 // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset
193 for (Long externalRelationId : relations.keySet()) {
194 Relation relation = (Relation) externalIdMap.get(
195 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
196 );
197 ds.addPrimitive(relation);
198 }
199
200 for (Entry<Long, Collection<RelationMemberData>> entry : relations.entrySet()) {
201 Long externalRelationId = entry.getKey();
202 Relation relation = (Relation) externalIdMap.get(
203 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION)
204 );
205 List<RelationMember> relationMembers = new ArrayList<>();
206 for (RelationMemberData rm : entry.getValue()) {
207 // lookup the member from the map of already created primitives
208 OsmPrimitive primitive = externalIdMap.get(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType()));
209
210 if (primitive == null) {
211 if (rm.getMemberId() <= 0)
212 // relation member refers to a primitive with a negative id which was not
213 // found in the data. This is always a data integrity problem and we abort
214 // with an exception
215 //
216 throw new IllegalDataException(
217 tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.",
218 Long.toString(externalRelationId),
219 Long.toString(rm.getMemberId())));
220
221 // member refers to OSM primitive which was not present in the parsed data
222 // -> create a new incomplete primitive and add it to the dataset
223 //
224 primitive = ds.getPrimitiveById(rm.getMemberId(), rm.getMemberType());
225 if (primitive == null) {
226 switch (rm.getMemberType()) {
227 case NODE:
228 primitive = new Node(rm.getMemberId()); break;
229 case WAY:
230 primitive = new Way(rm.getMemberId()); break;
231 case RELATION:
232 primitive = new Relation(rm.getMemberId()); break;
233 default: throw new AssertionError(); // can't happen
234 }
235
236 ds.addPrimitive(primitive);
237 externalIdMap.put(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType()), primitive);
238 }
239 }
240 if (primitive.isDeleted()) {
241 Logging.info(tr("Deleted member {0} is used by relation {1}",
242 Long.toString(primitive.getId()), Long.toString(relation.getId())));
243 } else {
244 relationMembers.add(new RelationMember(rm.getRole(), primitive));
245 }
246 }
247 relation.setMembers(relationMembers);
248 }
249 }
250
251 protected void processChangesetAfterParsing() {
252 if (uploadChangeset != null) {
253 for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) {
254 ds.addChangeSetTag(e.getKey(), e.getValue());
255 }
256 }
257 }
258
259 protected final void prepareDataSet() throws IllegalDataException {
260 ds.beginUpdate();
261 try {
262 processNodesAfterParsing();
263 processWaysAfterParsing();
264 processRelationsAfterParsing();
265 processChangesetAfterParsing();
266 } finally {
267 ds.endUpdate();
268 }
269 }
270
271 protected abstract DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException;
272
273 @FunctionalInterface
274 protected interface ParserWorker {
275 /**
276 * Effectively parses the file, depending on the format (XML, JSON, etc.)
277 * @param ir input stream reader
278 * @throws IllegalDataException in case of invalid data
279 * @throws IOException in case of I/O error
280 */
281 void accept(InputStreamReader ir) throws IllegalDataException, IOException;
282 }
283
284 protected final DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor, ParserWorker parserWorker)
285 throws IllegalDataException {
286 if (progressMonitor == null) {
287 progressMonitor = NullProgressMonitor.INSTANCE;
288 }
289 ProgressMonitor.CancelListener cancelListener = () -> cancel = true;
290 progressMonitor.addCancelListener(cancelListener);
291 CheckParameterUtil.ensureParameterNotNull(source, "source");
292 try {
293 progressMonitor.beginTask(tr("Prepare OSM data..."), 4); // read, prepare, post-process, render
294 progressMonitor.indeterminateSubTask(tr("Parsing OSM data..."));
295
296 try (InputStreamReader ir = UTFInputStreamReader.create(source)) {
297 parserWorker.accept(ir);
298 }
299 progressMonitor.worked(1);
300
301 boolean readOnly = getDataSet().isLocked();
302
303 progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
304 if (readOnly) {
305 getDataSet().unlock();
306 }
307 prepareDataSet();
308 if (readOnly) {
309 getDataSet().lock();
310 }
311 progressMonitor.worked(1);
312 progressMonitor.indeterminateSubTask(tr("Post-processing data set..."));
313 // iterate over registered postprocessors and give them each a chance
314 // to modify the dataset we have just loaded.
315 callPostProcessors(progressMonitor);
316 progressMonitor.worked(1);
317 // Make sure postprocessors did not change the read-only state
318 if (readOnly && !getDataSet().isLocked()) {
319 getDataSet().lock();
320 }
321 return getDataSet();
322 } catch (IllegalDataException e) {
323 throw e;
324 } catch (IOException e) {
325 throw new IllegalDataException(e);
326 } finally {
327 OptionalLong minId = externalIdMap.values().stream().mapToLong(AbstractPrimitive::getUniqueId).min();
328 if (minId.isPresent() && minId.getAsLong() < AbstractPrimitive.currentUniqueId()) {
329 AbstractPrimitive.advanceUniqueId(minId.getAsLong());
330 }
331 // don't call progressMonitor.finishTask() here, let caller do it
332 progressMonitor.removeCancelListener(cancelListener);
333 }
334 }
335
336 protected final long getLong(String name, String value) throws IllegalDataException {
337 if (value == null) {
338 throw new IllegalDataException(tr("Missing required attribute ''{0}''.", name));
339 }
340 try {
341 return Long.parseLong(value);
342 } catch (NumberFormatException e) {
343 throw new IllegalDataException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e);
344 }
345 }
346
347 protected final void parseVersion(String version) throws IllegalDataException {
348 validateVersion(version);
349 ds.setVersion(version);
350 }
351
352 private static void validateVersion(String version) throws IllegalDataException {
353 if (version == null) {
354 throw new IllegalDataException(tr("Missing mandatory attribute ''{0}''.", "version"));
355 }
356 if (!"0.6".equals(version)) {
357 throw new IllegalDataException(tr("Unsupported version: {0}", version));
358 }
359 }
360
361 protected final void parseDownloadPolicy(String key, String downloadPolicy) throws IllegalDataException {
362 parsePolicy(key, downloadPolicy, policy -> ds.setDownloadPolicy(DownloadPolicy.of(policy)));
363 }
364
365 protected final void parseUploadPolicy(String key, String uploadPolicy) throws IllegalDataException {
366 parsePolicy(key, uploadPolicy, policy -> ds.setUploadPolicy(UploadPolicy.of(policy)));
367 }
368
369 private static void parsePolicy(String key, String policy, Consumer<String> consumer) throws IllegalDataException {
370 if (policy != null) {
371 try {
372 consumer.accept(policy);
373 } catch (IllegalArgumentException e) {
374 throw new IllegalDataException(MessageFormat.format(
375 "Illegal value for attribute ''{0}''. Got ''{1}''.", key, policy), e);
376 }
377 }
378 }
379
380 protected final void parseLocked(String locked) {
381 if ("true".equalsIgnoreCase(locked)) {
382 ds.lock();
383 }
384 }
385
386 protected final void parseBounds(String generator, String minlon, String minlat, String maxlon, String maxlat, String origin)
387 throws IllegalDataException {
388 if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
389 if (origin == null) {
390 origin = generator;
391 }
392 Bounds bounds = new Bounds(
393 Double.parseDouble(minlat), Double.parseDouble(minlon),
394 Double.parseDouble(maxlat), Double.parseDouble(maxlon));
395 if (bounds.isOutOfTheWorld()) {
396 Bounds copy = new Bounds(bounds);
397 bounds.normalize();
398 Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds);
399 }
400 ds.addDataSource(new DataSource(bounds, origin));
401 } else {
402 throw new IllegalDataException(tr("Missing mandatory attributes on element ''bounds''. " +
403 "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.",
404 minlon, minlat, maxlon, maxlat, origin
405 ));
406 }
407 }
408
409 protected final void parseId(PrimitiveData current, long id) throws IllegalDataException {
410 current.setId(id);
411 if (current.getUniqueId() == 0) {
412 throw new IllegalDataException(tr("Illegal object with ID=0."));
413 }
414 }
415
416 protected final void parseTimestamp(PrimitiveData current, String time) {
417 if (time != null && !time.isEmpty()) {
418 current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000));
419 }
420 }
421
422 private static User createUser(String uid, String name) throws IllegalDataException {
423 if (uid == null) {
424 if (name == null)
425 return null;
426 return User.createLocalUser(name);
427 }
428 try {
429 return User.createOsmUser(Long.parseLong(uid), name);
430 } catch (NumberFormatException e) {
431 throw new IllegalDataException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e);
432 }
433 }
434
435 protected final void parseUser(PrimitiveData current, String user, long uid) {
436 current.setUser(User.createOsmUser(uid, user));
437 }
438
439 protected final void parseUser(PrimitiveData current, String user, String uid) throws IllegalDataException {
440 current.setUser(createUser(uid, user));
441 }
442
443 protected final void parseVisible(PrimitiveData current, String visible) {
444 if (visible != null) {
445 current.setVisible(Boolean.parseBoolean(visible));
446 }
447 }
448
449 protected final void parseVersion(PrimitiveData current, String versionString) throws IllegalDataException {
450 int version = 0;
451 if (versionString != null) {
452 try {
453 version = Integer.parseInt(versionString);
454 } catch (NumberFormatException e) {
455 throw new IllegalDataException(
456 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
457 Long.toString(current.getUniqueId()), versionString), e);
458 }
459 parseVersion(current, version);
460 } else {
461 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
462 if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) {
463 throw new IllegalDataException(
464 tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId())));
465 }
466 }
467 }
468
469 protected final void parseVersion(PrimitiveData current, int version) throws IllegalDataException {
470 switch (ds.getVersion()) {
471 case "0.6":
472 if (version <= 0 && !current.isNew()) {
473 throw new IllegalDataException(
474 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
475 Long.toString(current.getUniqueId()), version));
476 } else if (version < 0 && current.isNew()) {
477 Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.",
478 current.getUniqueId(), version, 0, "0.6"));
479 version = 0;
480 }
481 break;
482 default:
483 // should not happen. API version has been checked before
484 throw new IllegalDataException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion()));
485 }
486 current.setVersion(version);
487 }
488
489 protected final void parseAction(PrimitiveData current, String action) {
490 if (action == null) {
491 // do nothing
492 } else if ("delete".equals(action)) {
493 current.setDeleted(true);
494 current.setModified(current.isVisible());
495 } else if ("modify".equals(action)) {
496 current.setModified(true);
497 }
498 }
499
500 private static void handleIllegalChangeset(PrimitiveData current, IllegalArgumentException e, Object v)
501 throws IllegalDataException {
502 Logging.debug(e.getMessage());
503 if (current.isNew()) {
504 // for a new primitive we just log a warning
505 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
506 v, current.getUniqueId()));
507 current.setChangesetId(0);
508 } else {
509 // for an existing primitive this is a problem
510 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e);
511 }
512 }
513
514 protected final void parseChangeset(PrimitiveData current, String v) throws IllegalDataException {
515 if (v == null) {
516 current.setChangesetId(0);
517 } else {
518 try {
519 parseChangeset(current, Integer.parseInt(v));
520 } catch (NumberFormatException e) {
521 handleIllegalChangeset(current, e, v);
522 }
523 }
524 }
525
526 protected final void parseChangeset(PrimitiveData current, int v) throws IllegalDataException {
527 try {
528 current.setChangesetId(v);
529 } catch (IllegalArgumentException e) {
530 handleIllegalChangeset(current, e, v);
531 } catch (IllegalStateException e) {
532 // thrown for positive changeset id on new primitives
533 Logging.debug(e);
534 Logging.info(e.getMessage());
535 current.setChangesetId(0);
536 }
537 if (current.getChangesetId() <= 0) {
538 if (current.isNew()) {
539 // for a new primitive we just log a warning
540 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
541 v, current.getUniqueId()));
542 current.setChangesetId(0);
543 } else if (current.getChangesetId() < 0) {
544 // for an existing primitive this is a problem only for negative ids (GDPR extracts are set to 0)
545 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
546 }
547 }
548 }
549
550 protected final void parseTag(Tagged t, String key, String value) throws IllegalDataException {
551 if (key == null || value == null) {
552 throw new IllegalDataException(tr("Missing key or value attribute in tag."));
553 } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) {
554 // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data
555 // Drop the tag on import, but flag the primitive as modified
556 ((AbstractPrimitive) t).setModified(true);
557 } else {
558 t.put(key.intern(), value.intern());
559 }
560 }
561
562 @FunctionalInterface
563 protected interface CommonReader {
564 /**
565 * Reads the common primitive attributes and sets them in {@code pd}
566 * @param pd primitive data to update
567 * @throws IllegalDataException in case of invalid data
568 */
569 void accept(PrimitiveData pd) throws IllegalDataException;
570 }
571
572 @FunctionalInterface
573 protected interface NodeReader {
574 /**
575 * Reads the node tags.
576 * @param n node
577 * @throws IllegalDataException in case of invalid data
578 */
579 void accept(Node n) throws IllegalDataException;
580 }
581
582 @FunctionalInterface
583 protected interface WayReader {
584 /**
585 * Reads the way nodes and tags.
586 * @param w way
587 * @param nodeIds collection of resulting node ids
588 * @throws IllegalDataException in case of invalid data
589 */
590 void accept(Way w, Collection<Long> nodeIds) throws IllegalDataException;
591 }
592
593 @FunctionalInterface
594 protected interface RelationReader {
595 /**
596 * Reads the relation members and tags.
597 * @param r relation
598 * @param members collection of resulting members
599 * @throws IllegalDataException in case of invalid data
600 */
601 void accept(Relation r, Collection<RelationMemberData> members) throws IllegalDataException;
602 }
603
604 private static boolean areLatLonDefined(String lat, String lon) {
605 return lat != null && lon != null;
606 }
607
608 private static boolean areLatLonDefined(double lat, double lon) {
609 return !Double.isNaN(lat) && !Double.isNaN(lon);
610 }
611
612 protected OsmPrimitive buildPrimitive(PrimitiveData pd) {
613 OsmPrimitive p;
614 if (pd.getUniqueId() < AbstractPrimitive.currentUniqueId()) {
615 p = pd.getType().newInstance(pd.getUniqueId(), true);
616 } else {
617 p = pd.getType().newVersionedInstance(pd.getId(), pd.getVersion());
618 }
619 p.setVisible(pd.isVisible());
620 p.load(pd);
621 externalIdMap.put(pd.getPrimitiveId(), p);
622 return p;
623 }
624
625 private Node addNode(NodeData nd, NodeReader nodeReader) throws IllegalDataException {
626 Node n = (Node) buildPrimitive(nd);
627 nodeReader.accept(n);
628 return n;
629 }
630
631 protected final Node parseNode(double lat, double lon, CommonReader commonReader, NodeReader nodeReader)
632 throws IllegalDataException {
633 NodeData nd = new NodeData(0);
634 LatLon ll = null;
635 if (areLatLonDefined(lat, lon)) {
636 try {
637 ll = new LatLon(lat, lon);
638 nd.setCoor(ll);
639 } catch (NumberFormatException e) {
640 Logging.trace(e);
641 }
642 }
643 commonReader.accept(nd);
644 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
645 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
646 Long.toString(nd.getId()), lat, lon));
647 }
648 return addNode(nd, nodeReader);
649 }
650
651 protected final Node parseNode(String lat, String lon, CommonReader commonReader, NodeReader nodeReader)
652 throws IllegalDataException {
653 NodeData nd = new NodeData();
654 LatLon ll = null;
655 if (areLatLonDefined(lat, lon)) {
656 try {
657 ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
658 nd.setCoor(ll);
659 } catch (NumberFormatException e) {
660 Logging.trace(e);
661 }
662 }
663 commonReader.accept(nd);
664 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
665 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
666 Long.toString(nd.getId()), lat, lon));
667 }
668 return addNode(nd, nodeReader);
669 }
670
671 protected final Way parseWay(CommonReader commonReader, WayReader wayReader) throws IllegalDataException {
672 WayData wd = new WayData(0);
673 commonReader.accept(wd);
674 Way w = (Way) buildPrimitive(wd);
675
676 Collection<Long> nodeIds = new ArrayList<>();
677 wayReader.accept(w, nodeIds);
678 if (w.isDeleted() && !nodeIds.isEmpty()) {
679 Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId())));
680 nodeIds = new ArrayList<>();
681 }
682 ways.put(wd.getUniqueId(), nodeIds);
683 return w;
684 }
685
686 protected final Relation parseRelation(CommonReader commonReader, RelationReader relationReader) throws IllegalDataException {
687 RelationData rd = new RelationData(0);
688 commonReader.accept(rd);
689 Relation r = (Relation) buildPrimitive(rd);
690
691 Collection<RelationMemberData> members = new ArrayList<>();
692 relationReader.accept(r, members);
693 if (r.isDeleted() && !members.isEmpty()) {
694 Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId())));
695 members = new ArrayList<>();
696 }
697 relations.put(rd.getUniqueId(), members);
698 return r;
699 }
700
701 protected final RelationMemberData parseRelationMember(Relation r, String ref, String type, String role) throws IllegalDataException {
702 if (ref == null) {
703 throw new IllegalDataException(tr("Missing attribute ''ref'' on member in relation {0}.",
704 Long.toString(r.getUniqueId())));
705 }
706 try {
707 return parseRelationMember(r, Long.parseLong(ref), type, role);
708 } catch (NumberFormatException e) {
709 throw new IllegalDataException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}",
710 Long.toString(r.getUniqueId()), ref), e);
711 }
712 }
713
714 protected final RelationMemberData parseRelationMember(Relation r, long id, String type, String role) throws IllegalDataException {
715 if (id == 0) {
716 throw new IllegalDataException(tr("Incomplete <member> specification with ref=0"));
717 }
718 if (type == null) {
719 throw new IllegalDataException(tr("Missing attribute ''type'' on member {0} in relation {1}.",
720 Long.toString(id), Long.toString(r.getUniqueId())));
721 }
722 try {
723 return new RelationMemberData(role, OsmPrimitiveType.fromApiTypeName(type), id);
724 } catch (IllegalArgumentException e) {
725 throw new IllegalDataException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.",
726 Long.toString(id), Long.toString(r.getUniqueId()), type), e);
727 }
728 }
729}
Note: See TracBrowser for help on using the repository browser.