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

Last change on this file since 18636 was 18208, checked in by Don-vip, 3 years ago

global use of Utils.isEmpty/isBlank

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