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

Last change on this file since 14099 was 14099, checked in by Don-vip, 6 years ago

fix sonarqube issues

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