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

Last change on this file since 16008 was 15820, checked in by Don-vip, 4 years ago

fix #18654 - Separate unique identifiers per primitive type

This allows to easily update .osm files with negative ids across multiple sessions, such as internal JOSM boundaries file.

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