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

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

fix #17242 - DataIntegrityProblemException when reading large files (regression from r14535)

  • Property svn:eol-style set to native
File size: 29.9 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 progressMonitor.indeterminateSubTask(tr("Rendering data set..."));
318 // Make sure postprocessors did not change the read-only state
319 if (readOnly && !getDataSet().isLocked()) {
320 getDataSet().lock();
321 }
322 return getDataSet();
323 } catch (IllegalDataException e) {
324 throw e;
325 } catch (IOException e) {
326 throw new IllegalDataException(e);
327 } finally {
328 OptionalLong minId = externalIdMap.values().stream().mapToLong(AbstractPrimitive::getUniqueId).min();
329 if (minId.isPresent() && minId.getAsLong() < AbstractPrimitive.currentUniqueId()) {
330 AbstractPrimitive.advanceUniqueId(minId.getAsLong());
331 }
332 progressMonitor.finishTask();
333 progressMonitor.removeCancelListener(cancelListener);
334 }
335 }
336
337 protected final long getLong(String name, String value) throws IllegalDataException {
338 if (value == null) {
339 throw new IllegalDataException(tr("Missing required attribute ''{0}''.", name));
340 }
341 try {
342 return Long.parseLong(value);
343 } catch (NumberFormatException e) {
344 throw new IllegalDataException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e);
345 }
346 }
347
348 protected final void parseVersion(String version) throws IllegalDataException {
349 validateVersion(version);
350 ds.setVersion(version);
351 }
352
353 private static void validateVersion(String version) throws IllegalDataException {
354 if (version == null) {
355 throw new IllegalDataException(tr("Missing mandatory attribute ''{0}''.", "version"));
356 }
357 if (!"0.6".equals(version)) {
358 throw new IllegalDataException(tr("Unsupported version: {0}", version));
359 }
360 }
361
362 protected final void parseDownloadPolicy(String key, String downloadPolicy) throws IllegalDataException {
363 parsePolicy(key, downloadPolicy, policy -> ds.setDownloadPolicy(DownloadPolicy.of(policy)));
364 }
365
366 protected final void parseUploadPolicy(String key, String uploadPolicy) throws IllegalDataException {
367 parsePolicy(key, uploadPolicy, policy -> ds.setUploadPolicy(UploadPolicy.of(policy)));
368 }
369
370 private static void parsePolicy(String key, String policy, Consumer<String> consumer) throws IllegalDataException {
371 if (policy != null) {
372 try {
373 consumer.accept(policy);
374 } catch (IllegalArgumentException e) {
375 throw new IllegalDataException(MessageFormat.format(
376 "Illegal value for attribute ''{0}''. Got ''{1}''.", key, policy), e);
377 }
378 }
379 }
380
381 protected final void parseLocked(String locked) {
382 if ("true".equalsIgnoreCase(locked)) {
383 ds.lock();
384 }
385 }
386
387 protected final void parseBounds(String generator, String minlon, String minlat, String maxlon, String maxlat, String origin)
388 throws IllegalDataException {
389 if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
390 if (origin == null) {
391 origin = generator;
392 }
393 Bounds bounds = new Bounds(
394 Double.parseDouble(minlat), Double.parseDouble(minlon),
395 Double.parseDouble(maxlat), Double.parseDouble(maxlon));
396 if (bounds.isOutOfTheWorld()) {
397 Bounds copy = new Bounds(bounds);
398 bounds.normalize();
399 Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds);
400 }
401 ds.addDataSource(new DataSource(bounds, origin));
402 } else {
403 throw new IllegalDataException(tr("Missing mandatory attributes on element ''bounds''. " +
404 "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.",
405 minlon, minlat, maxlon, maxlat, origin
406 ));
407 }
408 }
409
410 protected final void parseId(PrimitiveData current, long id) throws IllegalDataException {
411 current.setId(id);
412 if (current.getUniqueId() == 0) {
413 throw new IllegalDataException(tr("Illegal object with ID=0."));
414 }
415 }
416
417 protected final void parseTimestamp(PrimitiveData current, String time) {
418 if (time != null && !time.isEmpty()) {
419 current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000));
420 }
421 }
422
423 private static User createUser(String uid, String name) throws IllegalDataException {
424 if (uid == null) {
425 if (name == null)
426 return null;
427 return User.createLocalUser(name);
428 }
429 try {
430 return User.createOsmUser(Long.parseLong(uid), name);
431 } catch (NumberFormatException e) {
432 throw new IllegalDataException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e);
433 }
434 }
435
436 protected final void parseUser(PrimitiveData current, String user, long uid) {
437 current.setUser(User.createOsmUser(uid, user));
438 }
439
440 protected final void parseUser(PrimitiveData current, String user, String uid) throws IllegalDataException {
441 current.setUser(createUser(uid, user));
442 }
443
444 protected final void parseVisible(PrimitiveData current, String visible) {
445 if (visible != null) {
446 current.setVisible(Boolean.parseBoolean(visible));
447 }
448 }
449
450 protected final void parseVersion(PrimitiveData current, String versionString) throws IllegalDataException {
451 int version = 0;
452 if (versionString != null) {
453 try {
454 version = Integer.parseInt(versionString);
455 } catch (NumberFormatException e) {
456 throw new IllegalDataException(
457 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
458 Long.toString(current.getUniqueId()), versionString), e);
459 }
460 parseVersion(current, version);
461 } else {
462 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
463 if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) {
464 throw new IllegalDataException(
465 tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId())));
466 }
467 }
468 }
469
470 protected final void parseVersion(PrimitiveData current, int version) throws IllegalDataException {
471 switch (ds.getVersion()) {
472 case "0.6":
473 if (version <= 0 && !current.isNew()) {
474 throw new IllegalDataException(
475 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
476 Long.toString(current.getUniqueId()), version));
477 } else if (version < 0 && current.isNew()) {
478 Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.",
479 current.getUniqueId(), version, 0, "0.6"));
480 version = 0;
481 }
482 break;
483 default:
484 // should not happen. API version has been checked before
485 throw new IllegalDataException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion()));
486 }
487 current.setVersion(version);
488 }
489
490 protected final void parseAction(PrimitiveData current, String action) {
491 if (action == null) {
492 // do nothing
493 } else if ("delete".equals(action)) {
494 current.setDeleted(true);
495 current.setModified(current.isVisible());
496 } else if ("modify".equals(action)) {
497 current.setModified(true);
498 }
499 }
500
501 private static void handleIllegalChangeset(PrimitiveData current, IllegalArgumentException e, Object v)
502 throws IllegalDataException {
503 Logging.debug(e.getMessage());
504 if (current.isNew()) {
505 // for a new primitive we just log a warning
506 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
507 v, current.getUniqueId()));
508 current.setChangesetId(0);
509 } else {
510 // for an existing primitive this is a problem
511 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e);
512 }
513 }
514
515 protected final void parseChangeset(PrimitiveData current, String v) throws IllegalDataException {
516 if (v == null) {
517 current.setChangesetId(0);
518 } else {
519 try {
520 parseChangeset(current, Integer.parseInt(v));
521 } catch (NumberFormatException e) {
522 handleIllegalChangeset(current, e, v);
523 }
524 }
525 }
526
527 protected final void parseChangeset(PrimitiveData current, int v) throws IllegalDataException {
528 try {
529 current.setChangesetId(v);
530 } catch (IllegalArgumentException e) {
531 handleIllegalChangeset(current, e, v);
532 } catch (IllegalStateException e) {
533 // thrown for positive changeset id on new primitives
534 Logging.debug(e);
535 Logging.info(e.getMessage());
536 current.setChangesetId(0);
537 }
538 if (current.getChangesetId() <= 0) {
539 if (current.isNew()) {
540 // for a new primitive we just log a warning
541 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
542 v, current.getUniqueId()));
543 current.setChangesetId(0);
544 } else if (current.getChangesetId() < 0) {
545 // for an existing primitive this is a problem only for negative ids (GDPR extracts are set to 0)
546 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
547 }
548 }
549 }
550
551 protected final void parseTag(Tagged t, String key, String value) throws IllegalDataException {
552 if (key == null || value == null) {
553 throw new IllegalDataException(tr("Missing key or value attribute in tag."));
554 } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) {
555 // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data
556 // Drop the tag on import, but flag the primitive as modified
557 ((AbstractPrimitive) t).setModified(true);
558 } else {
559 t.put(key.intern(), value.intern());
560 }
561 }
562
563 @FunctionalInterface
564 protected interface CommonReader {
565 /**
566 * Reads the common primitive attributes and sets them in {@code pd}
567 * @param pd primitive data to update
568 * @throws IllegalDataException in case of invalid data
569 */
570 void accept(PrimitiveData pd) throws IllegalDataException;
571 }
572
573 @FunctionalInterface
574 protected interface NodeReader {
575 /**
576 * Reads the node tags.
577 * @param n node
578 * @throws IllegalDataException in case of invalid data
579 */
580 void accept(Node n) throws IllegalDataException;
581 }
582
583 @FunctionalInterface
584 protected interface WayReader {
585 /**
586 * Reads the way nodes and tags.
587 * @param w way
588 * @param nodeIds collection of resulting node ids
589 * @throws IllegalDataException in case of invalid data
590 */
591 void accept(Way w, Collection<Long> nodeIds) throws IllegalDataException;
592 }
593
594 @FunctionalInterface
595 protected interface RelationReader {
596 /**
597 * Reads the relation members and tags.
598 * @param r relation
599 * @param members collection of resulting members
600 * @throws IllegalDataException in case of invalid data
601 */
602 void accept(Relation r, Collection<RelationMemberData> members) throws IllegalDataException;
603 }
604
605 private static boolean areLatLonDefined(String lat, String lon) {
606 return lat != null && lon != null;
607 }
608
609 private static boolean areLatLonDefined(double lat, double lon) {
610 return !Double.isNaN(lat) && !Double.isNaN(lon);
611 }
612
613 protected OsmPrimitive buildPrimitive(PrimitiveData pd) {
614 OsmPrimitive p;
615 if (pd.getUniqueId() < AbstractPrimitive.currentUniqueId()) {
616 p = pd.getType().newInstance(pd.getUniqueId(), true);
617 AbstractPrimitive.advanceUniqueId(pd.getUniqueId());
618 } else {
619 p = pd.getType().newVersionedInstance(pd.getId(), pd.getVersion());
620 }
621 p.setVisible(pd.isVisible());
622 p.load(pd);
623 externalIdMap.put(pd.getPrimitiveId(), p);
624 return p;
625 }
626
627 private Node addNode(NodeData nd, NodeReader nodeReader) throws IllegalDataException {
628 Node n = (Node) buildPrimitive(nd);
629 nodeReader.accept(n);
630 return n;
631 }
632
633 protected final Node parseNode(double lat, double lon, CommonReader commonReader, NodeReader nodeReader)
634 throws IllegalDataException {
635 NodeData nd = new NodeData(0);
636 LatLon ll = null;
637 if (areLatLonDefined(lat, lon)) {
638 try {
639 ll = new LatLon(lat, lon);
640 nd.setCoor(ll);
641 } catch (NumberFormatException e) {
642 Logging.trace(e);
643 }
644 }
645 commonReader.accept(nd);
646 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
647 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
648 Long.toString(nd.getId()), lat, lon));
649 }
650 return addNode(nd, nodeReader);
651 }
652
653 protected final Node parseNode(String lat, String lon, CommonReader commonReader, NodeReader nodeReader)
654 throws IllegalDataException {
655 NodeData nd = new NodeData();
656 LatLon ll = null;
657 if (areLatLonDefined(lat, lon)) {
658 try {
659 ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
660 nd.setCoor(ll);
661 } catch (NumberFormatException e) {
662 Logging.trace(e);
663 }
664 }
665 commonReader.accept(nd);
666 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
667 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
668 Long.toString(nd.getId()), lat, lon));
669 }
670 return addNode(nd, nodeReader);
671 }
672
673 protected final Way parseWay(CommonReader commonReader, WayReader wayReader) throws IllegalDataException {
674 WayData wd = new WayData(0);
675 commonReader.accept(wd);
676 Way w = (Way) buildPrimitive(wd);
677
678 Collection<Long> nodeIds = new ArrayList<>();
679 wayReader.accept(w, nodeIds);
680 if (w.isDeleted() && !nodeIds.isEmpty()) {
681 Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId())));
682 nodeIds = new ArrayList<>();
683 }
684 ways.put(wd.getUniqueId(), nodeIds);
685 return w;
686 }
687
688 protected final Relation parseRelation(CommonReader commonReader, RelationReader relationReader) throws IllegalDataException {
689 RelationData rd = new RelationData(0);
690 commonReader.accept(rd);
691 Relation r = (Relation) buildPrimitive(rd);
692
693 Collection<RelationMemberData> members = new ArrayList<>();
694 relationReader.accept(r, members);
695 if (r.isDeleted() && !members.isEmpty()) {
696 Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId())));
697 members = new ArrayList<>();
698 }
699 relations.put(rd.getUniqueId(), members);
700 return r;
701 }
702
703 protected final RelationMemberData parseRelationMember(Relation r, String ref, String type, String role) throws IllegalDataException {
704 if (ref == null) {
705 throw new IllegalDataException(tr("Missing attribute ''ref'' on member in relation {0}.",
706 Long.toString(r.getUniqueId())));
707 }
708 try {
709 return parseRelationMember(r, Long.parseLong(ref), type, role);
710 } catch (NumberFormatException e) {
711 throw new IllegalDataException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}",
712 Long.toString(r.getUniqueId()), ref), e);
713 }
714 }
715
716 protected final RelationMemberData parseRelationMember(Relation r, long id, String type, String role) throws IllegalDataException {
717 if (id == 0) {
718 throw new IllegalDataException(tr("Incomplete <member> specification with ref=0"));
719 }
720 if (type == null) {
721 throw new IllegalDataException(tr("Missing attribute ''type'' on member {0} in relation {1}.",
722 Long.toString(id), Long.toString(r.getUniqueId())));
723 }
724 try {
725 return new RelationMemberData(role, OsmPrimitiveType.fromApiTypeName(type), id);
726 } catch (IllegalArgumentException e) {
727 throw new IllegalDataException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.",
728 Long.toString(id), Long.toString(r.getUniqueId()), type), e);
729 }
730 }
731}
Note: See TracBrowser for help on using the repository browser.