source: josm/trunk/src/org/openstreetmap/josm/io/OsmReader.java@ 13212

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

fix #15643 - make sure OSM IDs do not use thousands separators in log and error messages

  • Property svn:eol-style set to native
File size: 26.3 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.List;
13import java.util.Objects;
14import java.util.regex.Matcher;
15import java.util.regex.Pattern;
16
17import javax.xml.stream.Location;
18import javax.xml.stream.XMLInputFactory;
19import javax.xml.stream.XMLStreamConstants;
20import javax.xml.stream.XMLStreamException;
21import javax.xml.stream.XMLStreamReader;
22
23import org.openstreetmap.josm.data.Bounds;
24import org.openstreetmap.josm.data.DataSource;
25import org.openstreetmap.josm.data.coor.LatLon;
26import org.openstreetmap.josm.data.osm.AbstractPrimitive;
27import org.openstreetmap.josm.data.osm.Changeset;
28import org.openstreetmap.josm.data.osm.DataSet;
29import org.openstreetmap.josm.data.osm.DataSet.UploadPolicy;
30import org.openstreetmap.josm.data.osm.Node;
31import org.openstreetmap.josm.data.osm.NodeData;
32import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
33import org.openstreetmap.josm.data.osm.PrimitiveData;
34import org.openstreetmap.josm.data.osm.Relation;
35import org.openstreetmap.josm.data.osm.RelationData;
36import org.openstreetmap.josm.data.osm.RelationMemberData;
37import org.openstreetmap.josm.data.osm.Tagged;
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.UncheckedParseException;
46import org.openstreetmap.josm.tools.Utils;
47import org.openstreetmap.josm.tools.date.DateUtils;
48
49/**
50 * Parser for the Osm Api. Read from an input stream and construct a dataset out of it.
51 *
52 * For each xml element, there is a dedicated method.
53 * The XMLStreamReader cursor points to the start of the element, when the method is
54 * entered, and it must point to the end of the same element, when it is exited.
55 */
56public class OsmReader extends AbstractReader {
57
58 protected XMLStreamReader parser;
59
60 protected boolean cancel;
61
62 /** Used by plugins to register themselves as data postprocessors. */
63 private static volatile List<OsmServerReadPostprocessor> postprocessors;
64
65 /** Register a new postprocessor.
66 * @param pp postprocessor
67 * @see #deregisterPostprocessor
68 */
69 public static void registerPostprocessor(OsmServerReadPostprocessor pp) {
70 if (postprocessors == null) {
71 postprocessors = new ArrayList<>();
72 }
73 postprocessors.add(pp);
74 }
75
76 /**
77 * Deregister a postprocessor previously registered with {@link #registerPostprocessor}.
78 * @param pp postprocessor
79 * @see #registerPostprocessor
80 */
81 public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) {
82 if (postprocessors != null) {
83 postprocessors.remove(pp);
84 }
85 }
86
87 /**
88 * constructor (for private and subclasses use only)
89 *
90 * @see #parseDataSet(InputStream, ProgressMonitor)
91 */
92 protected OsmReader() {
93 // Restricts visibility
94 }
95
96 protected void setParser(XMLStreamReader parser) {
97 this.parser = parser;
98 }
99
100 protected void throwException(String msg, Throwable th) throws XMLStreamException {
101 throw new XmlStreamParsingException(msg, parser.getLocation(), th);
102 }
103
104 protected void throwException(String msg) throws XMLStreamException {
105 throw new XmlStreamParsingException(msg, parser.getLocation());
106 }
107
108 protected void parse() throws XMLStreamException {
109 int event = parser.getEventType();
110 while (true) {
111 if (event == XMLStreamConstants.START_ELEMENT) {
112 parseRoot();
113 } else if (event == XMLStreamConstants.END_ELEMENT)
114 return;
115 if (parser.hasNext()) {
116 event = parser.next();
117 } else {
118 break;
119 }
120 }
121 parser.close();
122 }
123
124 protected void parseRoot() throws XMLStreamException {
125 if ("osm".equals(parser.getLocalName())) {
126 parseOsm();
127 } else {
128 parseUnknown();
129 }
130 }
131
132 private void parseOsm() throws XMLStreamException {
133 String v = parser.getAttributeValue(null, "version");
134 if (v == null) {
135 throwException(tr("Missing mandatory attribute ''{0}''.", "version"));
136 }
137 if (!"0.6".equals(v)) {
138 throwException(tr("Unsupported version: {0}", v));
139 }
140 ds.setVersion(v);
141 String upload = parser.getAttributeValue(null, "upload");
142 if (upload != null) {
143 for (UploadPolicy policy : UploadPolicy.values()) {
144 if (policy.getXmlFlag().equalsIgnoreCase(upload)) {
145 ds.setUploadPolicy(policy);
146 break;
147 }
148 }
149 }
150 String generator = parser.getAttributeValue(null, "generator");
151 Long uploadChangesetId = null;
152 if (parser.getAttributeValue(null, "upload-changeset") != null) {
153 uploadChangesetId = getLong("upload-changeset");
154 }
155 while (true) {
156 int event = parser.next();
157
158 if (cancel) {
159 cancel = false;
160 throw new OsmParsingCanceledException(tr("Reading was canceled"), parser.getLocation());
161 }
162
163 if (event == XMLStreamConstants.START_ELEMENT) {
164 switch (parser.getLocalName()) {
165 case "bounds":
166 parseBounds(generator);
167 break;
168 case "node":
169 parseNode();
170 break;
171 case "way":
172 parseWay();
173 break;
174 case "relation":
175 parseRelation();
176 break;
177 case "changeset":
178 parseChangeset(uploadChangesetId);
179 break;
180 default:
181 parseUnknown();
182 }
183 } else if (event == XMLStreamConstants.END_ELEMENT)
184 return;
185 }
186 }
187
188 private void parseBounds(String generator) throws XMLStreamException {
189 String minlon = parser.getAttributeValue(null, "minlon");
190 String minlat = parser.getAttributeValue(null, "minlat");
191 String maxlon = parser.getAttributeValue(null, "maxlon");
192 String maxlat = parser.getAttributeValue(null, "maxlat");
193 String origin = parser.getAttributeValue(null, "origin");
194 if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
195 if (origin == null) {
196 origin = generator;
197 }
198 Bounds bounds = new Bounds(
199 Double.parseDouble(minlat), Double.parseDouble(minlon),
200 Double.parseDouble(maxlat), Double.parseDouble(maxlon));
201 if (bounds.isOutOfTheWorld()) {
202 Bounds copy = new Bounds(bounds);
203 bounds.normalize();
204 Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds);
205 }
206 DataSource src = new DataSource(bounds, origin);
207 ds.addDataSource(src);
208 } else {
209 throwException(tr("Missing mandatory attributes on element ''bounds''. " +
210 "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.",
211 minlon, minlat, maxlon, maxlat, origin
212 ));
213 }
214 jumpToEnd();
215 }
216
217 protected Node parseNode() throws XMLStreamException {
218 NodeData nd = new NodeData();
219 String lat = parser.getAttributeValue(null, "lat");
220 String lon = parser.getAttributeValue(null, "lon");
221 LatLon ll = null;
222 if (lat != null && lon != null) {
223 try {
224 ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
225 nd.setCoor(ll);
226 } catch (NumberFormatException e) {
227 Logging.trace(e);
228 }
229 }
230 readCommon(nd);
231 if (lat != null && lon != null && (ll == null || !ll.isValid())) {
232 throwException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
233 Long.toString(nd.getId()), lat, lon));
234 }
235 Node n = new Node(nd.getId(), nd.getVersion());
236 n.setVisible(nd.isVisible());
237 n.load(nd);
238 externalIdMap.put(nd.getPrimitiveId(), n);
239 while (true) {
240 int event = parser.next();
241 if (event == XMLStreamConstants.START_ELEMENT) {
242 if ("tag".equals(parser.getLocalName())) {
243 parseTag(n);
244 } else {
245 parseUnknown();
246 }
247 } else if (event == XMLStreamConstants.END_ELEMENT)
248 return n;
249 }
250 }
251
252 protected Way parseWay() throws XMLStreamException {
253 WayData wd = new WayData();
254 readCommon(wd);
255 Way w = new Way(wd.getId(), wd.getVersion());
256 w.setVisible(wd.isVisible());
257 w.load(wd);
258 externalIdMap.put(wd.getPrimitiveId(), w);
259
260 Collection<Long> nodeIds = new ArrayList<>();
261 while (true) {
262 int event = parser.next();
263 if (event == XMLStreamConstants.START_ELEMENT) {
264 switch (parser.getLocalName()) {
265 case "nd":
266 nodeIds.add(parseWayNode(w));
267 break;
268 case "tag":
269 parseTag(w);
270 break;
271 default:
272 parseUnknown();
273 }
274 } else if (event == XMLStreamConstants.END_ELEMENT) {
275 break;
276 }
277 }
278 if (w.isDeleted() && !nodeIds.isEmpty()) {
279 Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId())));
280 nodeIds = new ArrayList<>();
281 }
282 ways.put(wd.getUniqueId(), nodeIds);
283 return w;
284 }
285
286 private long parseWayNode(Way w) throws XMLStreamException {
287 if (parser.getAttributeValue(null, "ref") == null) {
288 throwException(
289 tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}.", "ref", Long.toString(w.getUniqueId()))
290 );
291 }
292 long id = getLong("ref");
293 if (id == 0) {
294 throwException(
295 tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}.", Long.toString(id))
296 );
297 }
298 jumpToEnd();
299 return id;
300 }
301
302 protected Relation parseRelation() throws XMLStreamException {
303 RelationData rd = new RelationData();
304 readCommon(rd);
305 Relation r = new Relation(rd.getId(), rd.getVersion());
306 r.setVisible(rd.isVisible());
307 r.load(rd);
308 externalIdMap.put(rd.getPrimitiveId(), r);
309
310 Collection<RelationMemberData> members = new ArrayList<>();
311 while (true) {
312 int event = parser.next();
313 if (event == XMLStreamConstants.START_ELEMENT) {
314 switch (parser.getLocalName()) {
315 case "member":
316 members.add(parseRelationMember(r));
317 break;
318 case "tag":
319 parseTag(r);
320 break;
321 default:
322 parseUnknown();
323 }
324 } else if (event == XMLStreamConstants.END_ELEMENT) {
325 break;
326 }
327 }
328 if (r.isDeleted() && !members.isEmpty()) {
329 Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId())));
330 members = new ArrayList<>();
331 }
332 relations.put(rd.getUniqueId(), members);
333 return r;
334 }
335
336 private RelationMemberData parseRelationMember(Relation r) throws XMLStreamException {
337 OsmPrimitiveType type = null;
338 long id = 0;
339 String value = parser.getAttributeValue(null, "ref");
340 if (value == null) {
341 throwException(tr("Missing attribute ''ref'' on member in relation {0}.", Long.toString(r.getUniqueId())));
342 }
343 try {
344 id = Long.parseLong(value);
345 } catch (NumberFormatException e) {
346 throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(r.getUniqueId()),
347 value), e);
348 }
349 value = parser.getAttributeValue(null, "type");
350 if (value == null) {
351 throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(id), Long.toString(r.getUniqueId())));
352 }
353 try {
354 type = OsmPrimitiveType.fromApiTypeName(value);
355 } catch (IllegalArgumentException e) {
356 throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.",
357 Long.toString(id), Long.toString(r.getUniqueId()), value), e);
358 }
359 String role = parser.getAttributeValue(null, "role");
360
361 if (id == 0) {
362 throwException(tr("Incomplete <member> specification with ref=0"));
363 }
364 jumpToEnd();
365 return new RelationMemberData(role, type, id);
366 }
367
368 private void parseChangeset(Long uploadChangesetId) throws XMLStreamException {
369
370 Long id = null;
371 if (parser.getAttributeValue(null, "id") != null) {
372 id = getLong("id");
373 }
374 // Read changeset info if neither upload-changeset nor id are set, or if they are both set to the same value
375 if (Objects.equals(id, uploadChangesetId)) {
376 uploadChangeset = new Changeset(id != null ? id.intValue() : 0);
377 while (true) {
378 int event = parser.next();
379 if (event == XMLStreamConstants.START_ELEMENT) {
380 if ("tag".equals(parser.getLocalName())) {
381 parseTag(uploadChangeset);
382 } else {
383 parseUnknown();
384 }
385 } else if (event == XMLStreamConstants.END_ELEMENT)
386 return;
387 }
388 } else {
389 jumpToEnd(false);
390 }
391 }
392
393 private void parseTag(Tagged t) throws XMLStreamException {
394 String key = parser.getAttributeValue(null, "k");
395 String value = parser.getAttributeValue(null, "v");
396 if (key == null || value == null) {
397 throwException(tr("Missing key or value attribute in tag."));
398 } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) {
399 // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data
400 // Drop the tag on import, but flag the primitive as modified
401 ((AbstractPrimitive) t).setModified(true);
402 } else {
403 t.put(key.intern(), value.intern());
404 }
405 jumpToEnd();
406 }
407
408 protected void parseUnknown(boolean printWarning) throws XMLStreamException {
409 final String element = parser.getLocalName();
410 if (printWarning && ("note".equals(element) || "meta".equals(element))) {
411 // we know that Overpass API returns those elements
412 Logging.debug(tr("Undefined element ''{0}'' found in input stream. Skipping.", element));
413 } else if (printWarning) {
414 Logging.info(tr("Undefined element ''{0}'' found in input stream. Skipping.", element));
415 }
416 while (true) {
417 int event = parser.next();
418 if (event == XMLStreamConstants.START_ELEMENT) {
419 parseUnknown(false); /* no more warning for inner elements */
420 } else if (event == XMLStreamConstants.END_ELEMENT)
421 return;
422 }
423 }
424
425 protected void parseUnknown() throws XMLStreamException {
426 parseUnknown(true);
427 }
428
429 /**
430 * When cursor is at the start of an element, moves it to the end tag of that element.
431 * Nested content is skipped.
432 *
433 * This is basically the same code as parseUnknown(), except for the warnings, which
434 * are displayed for inner elements and not at top level.
435 * @param printWarning if {@code true}, a warning message will be printed if an unknown element is met
436 * @throws XMLStreamException if there is an error processing the underlying XML source
437 */
438 private void jumpToEnd(boolean printWarning) throws XMLStreamException {
439 while (true) {
440 int event = parser.next();
441 if (event == XMLStreamConstants.START_ELEMENT) {
442 parseUnknown(printWarning);
443 } else if (event == XMLStreamConstants.END_ELEMENT)
444 return;
445 }
446 }
447
448 private void jumpToEnd() throws XMLStreamException {
449 jumpToEnd(true);
450 }
451
452 private User createUser(String uid, String name) throws XMLStreamException {
453 if (uid == null) {
454 if (name == null)
455 return null;
456 return User.createLocalUser(name);
457 }
458 try {
459 long id = Long.parseLong(uid);
460 return User.createOsmUser(id, name);
461 } catch (NumberFormatException e) {
462 throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e);
463 }
464 return null;
465 }
466
467 /**
468 * Read out the common attributes and put them into current OsmPrimitive.
469 * @param current primitive to update
470 * @throws XMLStreamException if there is an error processing the underlying XML source
471 */
472 private void readCommon(PrimitiveData current) throws XMLStreamException {
473 current.setId(getLong("id"));
474 if (current.getUniqueId() == 0) {
475 throwException(tr("Illegal object with ID=0."));
476 }
477
478 String time = parser.getAttributeValue(null, "timestamp");
479 if (time != null && !time.isEmpty()) {
480 current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000));
481 }
482
483 String user = parser.getAttributeValue(null, "user");
484 String uid = parser.getAttributeValue(null, "uid");
485 current.setUser(createUser(uid, user));
486
487 String visible = parser.getAttributeValue(null, "visible");
488 if (visible != null) {
489 current.setVisible(Boolean.parseBoolean(visible));
490 }
491
492 String versionString = parser.getAttributeValue(null, "version");
493 int version = 0;
494 if (versionString != null) {
495 try {
496 version = Integer.parseInt(versionString);
497 } catch (NumberFormatException e) {
498 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
499 Long.toString(current.getUniqueId()), versionString), e);
500 }
501 switch (ds.getVersion()) {
502 case "0.6":
503 if (version <= 0 && !current.isNew()) {
504 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
505 Long.toString(current.getUniqueId()), versionString));
506 } else if (version < 0 && current.isNew()) {
507 Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.",
508 current.getUniqueId(), version, 0, "0.6"));
509 version = 0;
510 }
511 break;
512 default:
513 // should not happen. API version has been checked before
514 throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion()));
515 }
516 } else {
517 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
518 if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) {
519 throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId())));
520 }
521 }
522 current.setVersion(version);
523
524 String action = parser.getAttributeValue(null, "action");
525 if (action == null) {
526 // do nothing
527 } else if ("delete".equals(action)) {
528 current.setDeleted(true);
529 current.setModified(current.isVisible());
530 } else if ("modify".equals(action)) {
531 current.setModified(true);
532 }
533
534 String v = parser.getAttributeValue(null, "changeset");
535 if (v == null) {
536 current.setChangesetId(0);
537 } else {
538 try {
539 current.setChangesetId(Integer.parseInt(v));
540 } catch (IllegalArgumentException e) {
541 Logging.debug(e.getMessage());
542 if (current.isNew()) {
543 // for a new primitive we just log a warning
544 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
545 v, current.getUniqueId()));
546 current.setChangesetId(0);
547 } else {
548 // for an existing primitive this is a problem
549 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e);
550 }
551 } catch (IllegalStateException e) {
552 // thrown for positive changeset id on new primitives
553 Logging.debug(e);
554 Logging.info(e.getMessage());
555 current.setChangesetId(0);
556 }
557 if (current.getChangesetId() <= 0) {
558 if (current.isNew()) {
559 // for a new primitive we just log a warning
560 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
561 v, current.getUniqueId()));
562 current.setChangesetId(0);
563 } else {
564 // for an existing primitive this is a problem
565 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
566 }
567 }
568 }
569 }
570
571 private long getLong(String name) throws XMLStreamException {
572 String value = parser.getAttributeValue(null, name);
573 if (value == null) {
574 throwException(tr("Missing required attribute ''{0}''.", name));
575 }
576 try {
577 return Long.parseLong(value);
578 } catch (NumberFormatException e) {
579 throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e);
580 }
581 return 0; // should not happen
582 }
583
584 /**
585 * Exception thrown after user cancelation.
586 */
587 private static final class OsmParsingCanceledException extends XmlStreamParsingException implements ImportCancelException {
588 /**
589 * Constructs a new {@code OsmParsingCanceledException}.
590 * @param msg The error message
591 * @param location The parser location
592 */
593 OsmParsingCanceledException(String msg, Location location) {
594 super(msg, location);
595 }
596 }
597
598 @Override
599 protected DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
600 if (progressMonitor == null) {
601 progressMonitor = NullProgressMonitor.INSTANCE;
602 }
603 ProgressMonitor.CancelListener cancelListener = () -> cancel = true;
604 progressMonitor.addCancelListener(cancelListener);
605 CheckParameterUtil.ensureParameterNotNull(source, "source");
606 try {
607 progressMonitor.beginTask(tr("Prepare OSM data...", 2));
608 progressMonitor.indeterminateSubTask(tr("Parsing OSM data..."));
609
610 try (InputStreamReader ir = UTFInputStreamReader.create(source)) {
611 XMLInputFactory factory = XMLInputFactory.newInstance();
612 // do not try to load external entities
613 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
614 factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
615 setParser(factory.createXMLStreamReader(ir));
616 parse();
617 }
618 progressMonitor.worked(1);
619
620 progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
621 prepareDataSet();
622 progressMonitor.worked(1);
623
624 // iterate over registered postprocessors and give them each a chance
625 // to modify the dataset we have just loaded.
626 if (postprocessors != null) {
627 for (OsmServerReadPostprocessor pp : postprocessors) {
628 pp.postprocessDataSet(getDataSet(), progressMonitor);
629 }
630 }
631 return getDataSet();
632 } catch (IllegalDataException e) {
633 throw e;
634 } catch (XmlStreamParsingException | UncheckedParseException e) {
635 throw new IllegalDataException(e.getMessage(), e);
636 } catch (XMLStreamException e) {
637 String msg = e.getMessage();
638 Pattern p = Pattern.compile("Message: (.+)");
639 Matcher m = p.matcher(msg);
640 if (m.find()) {
641 msg = m.group(1);
642 }
643 if (e.getLocation() != null)
644 throw new IllegalDataException(tr("Line {0} column {1}: ",
645 e.getLocation().getLineNumber(), e.getLocation().getColumnNumber()) + msg, e);
646 else
647 throw new IllegalDataException(msg, e);
648 } catch (IOException e) {
649 throw new IllegalDataException(e);
650 } finally {
651 progressMonitor.finishTask();
652 progressMonitor.removeCancelListener(cancelListener);
653 }
654 }
655
656 /**
657 * Parse the given input source and return the dataset.
658 *
659 * @param source the source input stream. Must not be null.
660 * @param progressMonitor the progress monitor. If null, {@link NullProgressMonitor#INSTANCE} is assumed
661 *
662 * @return the dataset with the parsed data
663 * @throws IllegalDataException if an error was found while parsing the data from the source
664 * @throws IllegalArgumentException if source is null
665 */
666 public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
667 return new OsmReader().doParseDataSet(source, progressMonitor);
668 }
669}
Note: See TracBrowser for help on using the repository browser.