Changeset 14086 in josm
- Timestamp:
- 2018-08-05T14:09:31+02:00 (6 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/io/AbstractReader.java
r13197 r14086 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.io.IOException; 6 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.text.MessageFormat; 7 10 import java.util.ArrayList; 8 11 import java.util.Collection; … … 11 14 import java.util.Map; 12 15 import java.util.Map.Entry; 13 16 import java.util.function.Consumer; 17 18 import org.openstreetmap.josm.data.Bounds; 19 import org.openstreetmap.josm.data.DataSource; 20 import org.openstreetmap.josm.data.coor.LatLon; 21 import org.openstreetmap.josm.data.osm.AbstractPrimitive; 14 22 import org.openstreetmap.josm.data.osm.Changeset; 15 23 import org.openstreetmap.josm.data.osm.DataSet; 24 import org.openstreetmap.josm.data.osm.DownloadPolicy; 16 25 import org.openstreetmap.josm.data.osm.Node; 26 import org.openstreetmap.josm.data.osm.NodeData; 17 27 import org.openstreetmap.josm.data.osm.OsmPrimitive; 18 28 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 29 import org.openstreetmap.josm.data.osm.PrimitiveData; 19 30 import org.openstreetmap.josm.data.osm.PrimitiveId; 20 31 import org.openstreetmap.josm.data.osm.Relation; 32 import org.openstreetmap.josm.data.osm.RelationData; 21 33 import org.openstreetmap.josm.data.osm.RelationMember; 22 34 import org.openstreetmap.josm.data.osm.RelationMemberData; 23 35 import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 36 import org.openstreetmap.josm.data.osm.Tagged; 37 import org.openstreetmap.josm.data.osm.UploadPolicy; 38 import org.openstreetmap.josm.data.osm.User; 24 39 import org.openstreetmap.josm.data.osm.Way; 40 import org.openstreetmap.josm.data.osm.WayData; 41 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 25 42 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 43 import org.openstreetmap.josm.tools.CheckParameterUtil; 26 44 import org.openstreetmap.josm.tools.Logging; 45 import org.openstreetmap.josm.tools.Utils; 46 import org.openstreetmap.josm.tools.date.DateUtils; 27 47 28 48 /** 29 49 * Abstract Reader, allowing other implementations than OsmReader (PbfReader in PBF plugin for example) 30 50 * @author Vincent 31 * 51 * @since 4490 32 52 */ 33 53 public abstract class AbstractReader { 54 55 /** Used by plugins to register themselves as data postprocessors. */ 56 private static volatile List<OsmServerReadPostprocessor> postprocessors; 57 58 protected boolean cancel; 59 60 /** 61 * Register a new postprocessor. 62 * @param pp postprocessor 63 * @see #deregisterPostprocessor 64 * @since xxx (moved from OsmReader) 65 */ 66 public static void registerPostprocessor(OsmServerReadPostprocessor pp) { 67 if (postprocessors == null) { 68 postprocessors = new ArrayList<>(); 69 } 70 postprocessors.add(pp); 71 } 72 73 /** 74 * Deregister a postprocessor previously registered with {@link #registerPostprocessor}. 75 * @param pp postprocessor 76 * @see #registerPostprocessor 77 * @since xxx (moved from OsmReader) 78 */ 79 public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) { 80 if (postprocessors != null) { 81 postprocessors.remove(pp); 82 } 83 } 34 84 35 85 /** … … 63 113 public DataSet getDataSet() { 64 114 return ds; 115 } 116 117 /** 118 * Iterate over registered postprocessors and give them each a chance to modify the dataset we have just loaded. 119 * @param progressMonitor Progress monitor 120 */ 121 protected void callPostProcessors(ProgressMonitor progressMonitor) { 122 if (postprocessors != null) { 123 for (OsmServerReadPostprocessor pp : postprocessors) { 124 pp.postprocessDataSet(getDataSet(), progressMonitor); 125 } 126 } 65 127 } 66 128 … … 207 269 208 270 protected abstract DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException; 271 272 @FunctionalInterface 273 protected interface ParserWorker { 274 void accept(InputStreamReader ir) throws IllegalDataException, IOException; 275 } 276 277 protected final DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor, ParserWorker parserWorker) 278 throws IllegalDataException { 279 if (progressMonitor == null) { 280 progressMonitor = NullProgressMonitor.INSTANCE; 281 } 282 ProgressMonitor.CancelListener cancelListener = () -> cancel = true; 283 progressMonitor.addCancelListener(cancelListener); 284 CheckParameterUtil.ensureParameterNotNull(source, "source"); 285 try { 286 progressMonitor.beginTask(tr("Prepare OSM data...", 2)); 287 progressMonitor.indeterminateSubTask(tr("Parsing OSM data...")); 288 289 try (InputStreamReader ir = UTFInputStreamReader.create(source)) { 290 parserWorker.accept(ir); 291 } 292 progressMonitor.worked(1); 293 294 boolean readOnly = getDataSet().isLocked(); 295 296 progressMonitor.indeterminateSubTask(tr("Preparing data set...")); 297 if (readOnly) { 298 getDataSet().unlock(); 299 } 300 prepareDataSet(); 301 if (readOnly) { 302 getDataSet().lock(); 303 } 304 progressMonitor.worked(1); 305 306 // iterate over registered postprocessors and give them each a chance 307 // to modify the dataset we have just loaded. 308 callPostProcessors(progressMonitor); 309 // Make sure postprocessors did not change the read-only state 310 if (readOnly && !getDataSet().isLocked()) { 311 getDataSet().lock(); 312 } 313 return getDataSet(); 314 } catch (IllegalDataException e) { 315 throw e; 316 } catch (IOException e) { 317 throw new IllegalDataException(e); 318 } finally { 319 progressMonitor.finishTask(); 320 progressMonitor.removeCancelListener(cancelListener); 321 } 322 } 323 324 protected final long getLong(String name, String value) throws IllegalDataException { 325 if (value == null) { 326 throw new IllegalDataException(tr("Missing required attribute ''{0}''.", name)); 327 } 328 try { 329 return Long.parseLong(value); 330 } catch (NumberFormatException e) { 331 throw new IllegalDataException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e); 332 } 333 } 334 335 protected final void parseVersion(String version) throws IllegalDataException { 336 validateVersion(version); 337 ds.setVersion(version); 338 } 339 340 private static void validateVersion(String version) throws IllegalDataException { 341 if (version == null) { 342 throw new IllegalDataException(tr("Missing mandatory attribute ''{0}''.", "version")); 343 } 344 if (!"0.6".equals(version)) { 345 throw new IllegalDataException(tr("Unsupported version: {0}", version)); 346 } 347 } 348 349 protected final void parseDownloadPolicy(String key, String downloadPolicy) throws IllegalDataException { 350 parsePolicy(key, downloadPolicy, policy -> ds.setDownloadPolicy(DownloadPolicy.of(policy))); 351 } 352 353 protected final void parseUploadPolicy(String key, String uploadPolicy) throws IllegalDataException { 354 parsePolicy(key, uploadPolicy, policy -> ds.setUploadPolicy(UploadPolicy.of(policy))); 355 } 356 357 private static void parsePolicy(String key, String policy, Consumer<String> consumer) throws IllegalDataException { 358 if (policy != null) { 359 try { 360 consumer.accept(policy); 361 } catch (IllegalArgumentException e) { 362 throw new IllegalDataException(MessageFormat.format( 363 "Illegal value for attribute ''{0}''. Got ''{1}''.", key, policy), e); 364 } 365 } 366 } 367 368 protected final void parseLocked(String locked) { 369 if ("true".equalsIgnoreCase(locked)) { 370 ds.lock(); 371 } 372 } 373 374 protected final void parseBounds(String generator, String minlon, String minlat, String maxlon, String maxlat, String origin) 375 throws IllegalDataException { 376 if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 377 if (origin == null) { 378 origin = generator; 379 } 380 Bounds bounds = new Bounds( 381 Double.parseDouble(minlat), Double.parseDouble(minlon), 382 Double.parseDouble(maxlat), Double.parseDouble(maxlon)); 383 if (bounds.isOutOfTheWorld()) { 384 Bounds copy = new Bounds(bounds); 385 bounds.normalize(); 386 Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds); 387 } 388 ds.addDataSource(new DataSource(bounds, origin)); 389 } else { 390 throw new IllegalDataException(tr("Missing mandatory attributes on element ''bounds''. " + 391 "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.", 392 minlon, minlat, maxlon, maxlat, origin 393 )); 394 } 395 } 396 397 protected final void parseId(PrimitiveData current, long id) throws IllegalDataException { 398 current.setId(id); 399 if (current.getUniqueId() == 0) { 400 throw new IllegalDataException(tr("Illegal object with ID=0.")); 401 } 402 } 403 404 protected final void parseTimestamp(PrimitiveData current, String time) { 405 if (time != null && !time.isEmpty()) { 406 current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000)); 407 } 408 } 409 410 private static User createUser(String uid, String name) throws IllegalDataException { 411 if (uid == null) { 412 if (name == null) 413 return null; 414 return User.createLocalUser(name); 415 } 416 try { 417 return User.createOsmUser(Long.parseLong(uid), name); 418 } catch (NumberFormatException e) { 419 throw new IllegalDataException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e); 420 } 421 } 422 423 protected final void parseUser(PrimitiveData current, String user, long uid) { 424 current.setUser(User.createOsmUser(uid, user)); 425 } 426 427 protected final void parseUser(PrimitiveData current, String user, String uid) throws IllegalDataException { 428 current.setUser(createUser(uid, user)); 429 } 430 431 protected final void parseVisible(PrimitiveData current, String visible) { 432 if (visible != null) { 433 current.setVisible(Boolean.parseBoolean(visible)); 434 } 435 } 436 437 protected final void parseVersion(PrimitiveData current, String versionString) throws IllegalDataException { 438 int version = 0; 439 if (versionString != null) { 440 try { 441 version = Integer.parseInt(versionString); 442 } catch (NumberFormatException e) { 443 throw new IllegalDataException( 444 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", 445 Long.toString(current.getUniqueId()), versionString), e); 446 } 447 parseVersion(current, version); 448 } else { 449 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 450 if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) { 451 throw new IllegalDataException( 452 tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId()))); 453 } 454 } 455 } 456 457 protected final void parseVersion(PrimitiveData current, int version) throws IllegalDataException { 458 switch (ds.getVersion()) { 459 case "0.6": 460 if (version <= 0 && !current.isNew()) { 461 throw new IllegalDataException( 462 tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", 463 Long.toString(current.getUniqueId()), version)); 464 } else if (version < 0 && current.isNew()) { 465 Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", 466 current.getUniqueId(), version, 0, "0.6")); 467 version = 0; 468 } 469 break; 470 default: 471 // should not happen. API version has been checked before 472 throw new IllegalDataException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion())); 473 } 474 current.setVersion(version); 475 } 476 477 protected final void parseAction(PrimitiveData current, String action) { 478 if (action == null) { 479 // do nothing 480 } else if ("delete".equals(action)) { 481 current.setDeleted(true); 482 current.setModified(current.isVisible()); 483 } else if ("modify".equals(action)) { 484 current.setModified(true); 485 } 486 } 487 488 private static void handleIllegalChangeset(PrimitiveData current, IllegalArgumentException e, Object v) 489 throws IllegalDataException { 490 Logging.debug(e.getMessage()); 491 if (current.isNew()) { 492 // for a new primitive we just log a warning 493 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", 494 v, current.getUniqueId())); 495 current.setChangesetId(0); 496 } else { 497 // for an existing primitive this is a problem 498 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e); 499 } 500 } 501 502 protected final void parseChangeset(PrimitiveData current, String v) throws IllegalDataException { 503 if (v == null) { 504 current.setChangesetId(0); 505 } else { 506 try { 507 parseChangeset(current, Integer.parseInt(v)); 508 } catch (NumberFormatException e) { 509 handleIllegalChangeset(current, e, v); 510 } 511 } 512 } 513 514 protected final void parseChangeset(PrimitiveData current, int v) throws IllegalDataException { 515 try { 516 current.setChangesetId(v); 517 } catch (IllegalArgumentException e) { 518 handleIllegalChangeset(current, e, v); 519 } catch (IllegalStateException e) { 520 // thrown for positive changeset id on new primitives 521 Logging.debug(e); 522 Logging.info(e.getMessage()); 523 current.setChangesetId(0); 524 } 525 if (current.getChangesetId() <= 0) { 526 if (current.isNew()) { 527 // for a new primitive we just log a warning 528 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", 529 v, current.getUniqueId())); 530 current.setChangesetId(0); 531 } else if (current.getChangesetId() < 0) { 532 // for an existing primitive this is a problem only for negative ids (GDPR extracts are set to 0) 533 throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 534 } 535 } 536 } 537 538 protected final void parseTag(Tagged t, String key, String value) throws IllegalDataException { 539 if (key == null || value == null) { 540 throw new IllegalDataException(tr("Missing key or value attribute in tag.")); 541 } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) { 542 // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data 543 // Drop the tag on import, but flag the primitive as modified 544 ((AbstractPrimitive) t).setModified(true); 545 } else { 546 t.put(key.intern(), value.intern()); 547 } 548 } 549 550 @FunctionalInterface 551 protected interface CommonReader { 552 void accept(PrimitiveData pd) throws IllegalDataException; 553 } 554 555 @FunctionalInterface 556 protected interface NodeReader { 557 void accept(Node n) throws IllegalDataException; 558 } 559 560 @FunctionalInterface 561 protected interface WayReader { 562 void accept(Way w, Collection<Long> nodeIds) throws IllegalDataException; 563 } 564 565 @FunctionalInterface 566 protected interface RelationReader { 567 void accept(Relation r, Collection<RelationMemberData> members) throws IllegalDataException; 568 } 569 570 private static boolean areLatLonDefined(String lat, String lon) { 571 return lat != null && lon != null; 572 } 573 574 private static boolean areLatLonDefined(double lat, double lon) { 575 return lat != Double.NaN && lon != Double.NaN; 576 } 577 578 private Node addNode(NodeData nd, NodeReader nodeReader) throws IllegalDataException { 579 Node n = new Node(nd.getId(), nd.getVersion()); 580 n.setVisible(nd.isVisible()); 581 n.load(nd); 582 nodeReader.accept(n); 583 externalIdMap.put(nd.getPrimitiveId(), n); 584 return n; 585 } 586 587 protected final Node parseNode(double lat, double lon, CommonReader commonReader, NodeReader nodeReader) 588 throws IllegalDataException { 589 NodeData nd = new NodeData(); 590 LatLon ll = null; 591 if (areLatLonDefined(lat, lon)) { 592 try { 593 ll = new LatLon(lat, lon); 594 nd.setCoor(ll); 595 } catch (NumberFormatException e) { 596 Logging.trace(e); 597 } 598 } 599 commonReader.accept(nd); 600 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) { 601 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.", 602 Long.toString(nd.getId()), lat, lon)); 603 } 604 return addNode(nd, nodeReader); 605 } 606 607 protected final Node parseNode(String lat, String lon, CommonReader commonReader, NodeReader nodeReader) 608 throws IllegalDataException { 609 NodeData nd = new NodeData(); 610 LatLon ll = null; 611 if (areLatLonDefined(lat, lon)) { 612 try { 613 ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon)); 614 nd.setCoor(ll); 615 } catch (NumberFormatException e) { 616 Logging.trace(e); 617 } 618 } 619 commonReader.accept(nd); 620 if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) { 621 throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.", 622 Long.toString(nd.getId()), lat, lon)); 623 } 624 return addNode(nd, nodeReader); 625 } 626 627 protected final Way parseWay(CommonReader commonReader, WayReader wayReader) throws IllegalDataException { 628 WayData wd = new WayData(); 629 commonReader.accept(wd); 630 Way w = new Way(wd.getId(), wd.getVersion()); 631 w.setVisible(wd.isVisible()); 632 w.load(wd); 633 externalIdMap.put(wd.getPrimitiveId(), w); 634 635 Collection<Long> nodeIds = new ArrayList<>(); 636 wayReader.accept(w, nodeIds); 637 if (w.isDeleted() && !nodeIds.isEmpty()) { 638 Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId()))); 639 nodeIds = new ArrayList<>(); 640 } 641 ways.put(wd.getUniqueId(), nodeIds); 642 return w; 643 } 644 645 protected final Relation parseRelation(CommonReader commonReader, RelationReader relationReader) throws IllegalDataException { 646 RelationData rd = new RelationData(); 647 commonReader.accept(rd); 648 Relation r = new Relation(rd.getId(), rd.getVersion()); 649 r.setVisible(rd.isVisible()); 650 r.load(rd); 651 externalIdMap.put(rd.getPrimitiveId(), r); 652 653 Collection<RelationMemberData> members = new ArrayList<>(); 654 relationReader.accept(r, members); 655 if (r.isDeleted() && !members.isEmpty()) { 656 Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId()))); 657 members = new ArrayList<>(); 658 } 659 relations.put(rd.getUniqueId(), members); 660 return r; 661 } 662 663 protected final RelationMemberData parseRelationMember(Relation r, String ref, String type, String role) throws IllegalDataException { 664 if (ref == null) { 665 throw new IllegalDataException(tr("Missing attribute ''ref'' on member in relation {0}.", 666 Long.toString(r.getUniqueId()))); 667 } 668 try { 669 return parseRelationMember(r, Long.parseLong(ref), type, role); 670 } catch (NumberFormatException e) { 671 throw new IllegalDataException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", 672 Long.toString(r.getUniqueId()), ref), e); 673 } 674 } 675 676 protected final RelationMemberData parseRelationMember(Relation r, long id, String type, String role) throws IllegalDataException { 677 if (id == 0) { 678 throw new IllegalDataException(tr("Incomplete <member> specification with ref=0")); 679 } 680 if (type == null) { 681 throw new IllegalDataException(tr("Missing attribute ''type'' on member {0} in relation {1}.", 682 Long.toString(id), Long.toString(r.getUniqueId()))); 683 } 684 try { 685 return new RelationMemberData(role, OsmPrimitiveType.fromApiTypeName(type), id); 686 } catch (IllegalArgumentException e) { 687 throw new IllegalDataException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", 688 Long.toString(id), Long.toString(r.getUniqueId()), type), e); 689 } 690 } 209 691 } -
trunk/src/org/openstreetmap/josm/io/OsmReader.java
r14078 r14086 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.io.IOException;7 6 import java.io.InputStream; 8 import java.io.InputStreamReader;9 import java.text.MessageFormat;10 import java.util.ArrayList;11 7 import java.util.Collection; 12 import java.util.List;13 8 import java.util.Objects; 14 import java.util.function.Consumer;15 9 import java.util.regex.Matcher; 16 10 import java.util.regex.Pattern; … … 21 15 import javax.xml.stream.XMLStreamReader; 22 16 23 import org.openstreetmap.josm.data.Bounds;24 import org.openstreetmap.josm.data.DataSource;25 import org.openstreetmap.josm.data.coor.LatLon;26 import org.openstreetmap.josm.data.osm.AbstractPrimitive;27 17 import org.openstreetmap.josm.data.osm.Changeset; 28 18 import org.openstreetmap.josm.data.osm.DataSet; 29 import org.openstreetmap.josm.data.osm.DownloadPolicy;30 19 import org.openstreetmap.josm.data.osm.Node; 31 import org.openstreetmap.josm.data.osm.NodeData;32 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;33 20 import org.openstreetmap.josm.data.osm.PrimitiveData; 34 21 import org.openstreetmap.josm.data.osm.Relation; 35 import org.openstreetmap.josm.data.osm.RelationData;36 22 import org.openstreetmap.josm.data.osm.RelationMemberData; 37 23 import org.openstreetmap.josm.data.osm.Tagged; 38 import org.openstreetmap.josm.data.osm.UploadPolicy;39 import org.openstreetmap.josm.data.osm.User;40 24 import org.openstreetmap.josm.data.osm.Way; 41 import org.openstreetmap.josm.data.osm.WayData;42 25 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 43 26 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 44 import org.openstreetmap.josm.tools.CheckParameterUtil;45 27 import org.openstreetmap.josm.tools.Logging; 46 28 import org.openstreetmap.josm.tools.UncheckedParseException; 47 import org.openstreetmap.josm.tools.Utils;48 29 import org.openstreetmap.josm.tools.XmlUtils; 49 import org.openstreetmap.josm.tools.date.DateUtils;50 30 51 31 /** 52 * Parser for the Osm A pi. Read from an input stream and construct a dataset out of it.32 * Parser for the Osm API (XML output). Read from an input stream and construct a dataset out of it. 53 33 * 54 34 * For each xml element, there is a dedicated method. … … 59 39 60 40 protected XMLStreamReader parser; 61 62 protected boolean cancel;63 64 /** Used by plugins to register themselves as data postprocessors. */65 private static volatile List<OsmServerReadPostprocessor> postprocessors;66 67 /** Register a new postprocessor.68 * @param pp postprocessor69 * @see #deregisterPostprocessor70 */71 public static void registerPostprocessor(OsmServerReadPostprocessor pp) {72 if (postprocessors == null) {73 postprocessors = new ArrayList<>();74 }75 postprocessors.add(pp);76 }77 78 /**79 * Deregister a postprocessor previously registered with {@link #registerPostprocessor}.80 * @param pp postprocessor81 * @see #registerPostprocessor82 */83 public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) {84 if (postprocessors != null) {85 postprocessors.remove(pp);86 }87 }88 41 89 42 /** … … 98 51 protected void setParser(XMLStreamReader parser) { 99 52 this.parser = parser; 53 } 54 55 protected void throwException(Throwable th) throws XMLStreamException { 56 throw new XmlStreamParsingException(th.getMessage(), parser.getLocation(), th); 100 57 } 101 58 … … 133 90 134 91 private void parseOsm() throws XMLStreamException { 135 String v = parser.getAttributeValue(null, "version"); 136 if (v == null) { 137 throwException(tr("Missing mandatory attribute ''{0}''.", "version")); 138 } 139 if (!"0.6".equals(v)) { 140 throwException(tr("Unsupported version: {0}", v)); 141 } 142 ds.setVersion(v); 143 parsePolicy("download", policy -> ds.setDownloadPolicy(DownloadPolicy.of(policy))); 144 parsePolicy("upload", policy -> ds.setUploadPolicy(UploadPolicy.of(policy))); 145 if ("true".equalsIgnoreCase(parser.getAttributeValue(null, "locked"))) { 146 ds.lock(); 92 try { 93 parseVersion(parser.getAttributeValue(null, "version")); 94 parseDownloadPolicy("download", parser.getAttributeValue(null, "download")); 95 parseUploadPolicy("upload", parser.getAttributeValue(null, "upload")); 96 parseLocked(parser.getAttributeValue(null, "locked")); 97 } catch (IllegalDataException e) { 98 throwException(e); 147 99 } 148 100 String generator = parser.getAttributeValue(null, "generator"); … … 181 133 } else if (event == XMLStreamConstants.END_ELEMENT) { 182 134 return; 183 }184 }185 }186 187 private void parsePolicy(String key, Consumer<String> consumer) throws XMLStreamException {188 String policy = parser.getAttributeValue(null, key);189 if (policy != null) {190 try {191 consumer.accept(policy);192 } catch (IllegalArgumentException e) {193 throwException(MessageFormat.format("Illegal value for attribute ''{0}''. Got ''{1}''.", key, policy), e);194 135 } 195 136 } … … 202 143 String maxlat = parser.getAttributeValue(null, "maxlat"); 203 144 String origin = parser.getAttributeValue(null, "origin"); 204 if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 205 if (origin == null) { 206 origin = generator; 207 } 208 Bounds bounds = new Bounds( 209 Double.parseDouble(minlat), Double.parseDouble(minlon), 210 Double.parseDouble(maxlat), Double.parseDouble(maxlon)); 211 if (bounds.isOutOfTheWorld()) { 212 Bounds copy = new Bounds(bounds); 213 bounds.normalize(); 214 Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds); 215 } 216 DataSource src = new DataSource(bounds, origin); 217 ds.addDataSource(src); 218 } else { 219 throwException(tr("Missing mandatory attributes on element ''bounds''. " + 220 "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.", 221 minlon, minlat, maxlon, maxlat, origin 222 )); 145 try { 146 parseBounds(generator, minlon, minlat, maxlon, maxlat, origin); 147 } catch (IllegalDataException e) { 148 throwException(e); 223 149 } 224 150 jumpToEnd(); … … 226 152 227 153 protected Node parseNode() throws XMLStreamException { 228 NodeData nd = new NodeData();229 154 String lat = parser.getAttributeValue(null, "lat"); 230 155 String lon = parser.getAttributeValue(null, "lon"); 231 LatLon ll = null; 232 if (lat != null && lon != null) { 233 try { 234 ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon)); 235 nd.setCoor(ll); 236 } catch (NumberFormatException e) { 237 Logging.trace(e); 238 } 239 } 240 readCommon(nd); 241 if (lat != null && lon != null && (ll == null || !ll.isValid())) { 242 throwException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.", 243 Long.toString(nd.getId()), lat, lon)); 244 } 245 Node n = new Node(nd.getId(), nd.getVersion()); 246 n.setVisible(nd.isVisible()); 247 n.load(nd); 248 externalIdMap.put(nd.getPrimitiveId(), n); 249 while (true) { 250 int event = parser.next(); 251 if (event == XMLStreamConstants.START_ELEMENT) { 252 if ("tag".equals(parser.getLocalName())) { 253 parseTag(n); 254 } else { 255 parseUnknown(); 156 try { 157 return parseNode(lat, lon, this::readCommon, this::parseNodeTags); 158 } catch (IllegalDataException e) { 159 throwException(e); 160 } 161 return null; 162 } 163 164 private void parseNodeTags(Node n) throws IllegalDataException { 165 try { 166 while (parser.hasNext()) { 167 int event = parser.next(); 168 if (event == XMLStreamConstants.START_ELEMENT) { 169 if ("tag".equals(parser.getLocalName())) { 170 parseTag(n); 171 } else { 172 parseUnknown(); 173 } 174 } else if (event == XMLStreamConstants.END_ELEMENT) { 175 return; 256 176 } 257 } else if (event == XMLStreamConstants.END_ELEMENT) 258 return n; 177 } 178 } catch (XMLStreamException e) { 179 throw new IllegalDataException(e); 259 180 } 260 181 } 261 182 262 183 protected Way parseWay() throws XMLStreamException { 263 WayData wd = new WayData(); 264 readCommon(wd); 265 Way w = new Way(wd.getId(), wd.getVersion()); 266 w.setVisible(wd.isVisible()); 267 w.load(wd); 268 externalIdMap.put(wd.getPrimitiveId(), w); 269 270 Collection<Long> nodeIds = new ArrayList<>(); 271 while (true) { 272 int event = parser.next(); 273 if (event == XMLStreamConstants.START_ELEMENT) { 274 switch (parser.getLocalName()) { 275 case "nd": 276 nodeIds.add(parseWayNode(w)); 277 break; 278 case "tag": 279 parseTag(w); 280 break; 281 default: 282 parseUnknown(); 184 try { 185 return parseWay(this::readCommon, this::parseWayNodesAndTags); 186 } catch (IllegalDataException e) { 187 throwException(e); 188 } 189 return null; 190 } 191 192 private void parseWayNodesAndTags(Way w, Collection<Long> nodeIds) throws IllegalDataException { 193 try { 194 while (parser.hasNext()) { 195 int event = parser.next(); 196 if (event == XMLStreamConstants.START_ELEMENT) { 197 switch (parser.getLocalName()) { 198 case "nd": 199 nodeIds.add(parseWayNode(w)); 200 break; 201 case "tag": 202 parseTag(w); 203 break; 204 default: 205 parseUnknown(); 206 } 207 } else if (event == XMLStreamConstants.END_ELEMENT) { 208 break; 283 209 } 284 } else if (event == XMLStreamConstants.END_ELEMENT) { 285 break; 286 } 287 } 288 if (w.isDeleted() && !nodeIds.isEmpty()) { 289 Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId()))); 290 nodeIds = new ArrayList<>(); 291 } 292 ways.put(wd.getUniqueId(), nodeIds); 293 return w; 210 } 211 } catch (XMLStreamException e) { 212 throw new IllegalDataException(e); 213 } 294 214 } 295 215 … … 311 231 312 232 protected Relation parseRelation() throws XMLStreamException { 313 RelationData rd = new RelationData(); 314 readCommon(rd); 315 Relation r = new Relation(rd.getId(), rd.getVersion()); 316 r.setVisible(rd.isVisible()); 317 r.load(rd); 318 externalIdMap.put(rd.getPrimitiveId(), r); 319 320 Collection<RelationMemberData> members = new ArrayList<>(); 321 while (true) { 322 int event = parser.next(); 323 if (event == XMLStreamConstants.START_ELEMENT) { 324 switch (parser.getLocalName()) { 325 case "member": 326 members.add(parseRelationMember(r)); 327 break; 328 case "tag": 329 parseTag(r); 330 break; 331 default: 332 parseUnknown(); 233 try { 234 return parseRelation(this::readCommon, this::parseRelationMembersAndTags); 235 } catch (IllegalDataException e) { 236 throw new XMLStreamException(e); 237 } 238 } 239 240 private void parseRelationMembersAndTags(Relation r, Collection<RelationMemberData> members) throws IllegalDataException { 241 try { 242 while (parser.hasNext()) { 243 int event = parser.next(); 244 if (event == XMLStreamConstants.START_ELEMENT) { 245 switch (parser.getLocalName()) { 246 case "member": 247 members.add(parseRelationMember(r)); 248 break; 249 case "tag": 250 parseTag(r); 251 break; 252 default: 253 parseUnknown(); 254 } 255 } else if (event == XMLStreamConstants.END_ELEMENT) { 256 break; 333 257 } 334 } else if (event == XMLStreamConstants.END_ELEMENT) { 335 break; 336 } 337 } 338 if (r.isDeleted() && !members.isEmpty()) { 339 Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId()))); 340 members = new ArrayList<>(); 341 } 342 relations.put(rd.getUniqueId(), members); 343 return r; 258 } 259 } catch (XMLStreamException e) { 260 throw new IllegalDataException(e); 261 } 344 262 } 345 263 346 264 private RelationMemberData parseRelationMember(Relation r) throws XMLStreamException { 347 OsmPrimitiveType type = null; 348 long id = 0; 349 String value = parser.getAttributeValue(null, "ref"); 350 if (value == null) { 351 throwException(tr("Missing attribute ''ref'' on member in relation {0}.", Long.toString(r.getUniqueId()))); 352 } 353 try { 354 id = Long.parseLong(value); 355 } catch (NumberFormatException e) { 356 throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(r.getUniqueId()), 357 value), e); 358 } 359 value = parser.getAttributeValue(null, "type"); 360 if (value == null) { 361 throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(id), Long.toString(r.getUniqueId()))); 362 } 363 try { 364 type = OsmPrimitiveType.fromApiTypeName(value); 365 } catch (IllegalArgumentException e) { 366 throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", 367 Long.toString(id), Long.toString(r.getUniqueId()), value), e); 368 } 369 String role = parser.getAttributeValue(null, "role"); 370 371 if (id == 0) { 372 throwException(tr("Incomplete <member> specification with ref=0")); 373 } 374 jumpToEnd(); 375 return new RelationMemberData(role, type, id); 265 RelationMemberData result = null; 266 try { 267 String ref = parser.getAttributeValue(null, "ref"); 268 String type = parser.getAttributeValue(null, "type"); 269 String role = parser.getAttributeValue(null, "role"); 270 result = parseRelationMember(r, ref, type, role); 271 jumpToEnd(); 272 } catch (IllegalDataException e) { 273 throwException(e); 274 } 275 return result; 376 276 } 377 277 … … 404 304 String key = parser.getAttributeValue(null, "k"); 405 305 String value = parser.getAttributeValue(null, "v"); 406 if (key == null || value == null) { 407 throwException(tr("Missing key or value attribute in tag.")); 408 } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) { 409 // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data 410 // Drop the tag on import, but flag the primitive as modified 411 ((AbstractPrimitive) t).setModified(true); 412 } else { 413 t.put(key.intern(), value.intern()); 306 try { 307 parseTag(t, key, value); 308 } catch (IllegalDataException e) { 309 throwException(e); 414 310 } 415 311 jumpToEnd(); … … 460 356 } 461 357 462 private User createUser(String uid, String name) throws XMLStreamException {463 if (uid == null) {464 if (name == null)465 return null;466 return User.createLocalUser(name);467 }468 try {469 long id = Long.parseLong(uid);470 return User.createOsmUser(id, name);471 } catch (NumberFormatException e) {472 throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e);473 }474 return null;475 }476 477 358 /** 478 359 * Read out the common attributes and put them into current OsmPrimitive. 479 360 * @param current primitive to update 480 * @throws XMLStreamException if there is an error processing the underlying XML source361 * @throws IllegalDataException if there is an error processing the underlying XML source 481 362 */ 482 private void readCommon(PrimitiveData current) throws XMLStreamException { 483 current.setId(getLong("id")); 484 if (current.getUniqueId() == 0) { 485 throwException(tr("Illegal object with ID=0.")); 486 } 487 488 String time = parser.getAttributeValue(null, "timestamp"); 489 if (time != null && !time.isEmpty()) { 490 current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000)); 491 } 492 493 String user = parser.getAttributeValue(null, "user"); 494 String uid = parser.getAttributeValue(null, "uid"); 495 current.setUser(createUser(uid, user)); 496 497 String visible = parser.getAttributeValue(null, "visible"); 498 if (visible != null) { 499 current.setVisible(Boolean.parseBoolean(visible)); 500 } 501 502 String versionString = parser.getAttributeValue(null, "version"); 503 int version = 0; 504 if (versionString != null) { 505 try { 506 version = Integer.parseInt(versionString); 507 } catch (NumberFormatException e) { 508 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", 509 Long.toString(current.getUniqueId()), versionString), e); 510 } 511 switch (ds.getVersion()) { 512 case "0.6": 513 if (version <= 0 && !current.isNew()) { 514 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", 515 Long.toString(current.getUniqueId()), versionString)); 516 } else if (version < 0 && current.isNew()) { 517 Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", 518 current.getUniqueId(), version, 0, "0.6")); 519 version = 0; 520 } 521 break; 522 default: 523 // should not happen. API version has been checked before 524 throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion())); 525 } 526 } else { 527 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 528 if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) { 529 throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId()))); 530 } 531 } 532 current.setVersion(version); 533 534 String action = parser.getAttributeValue(null, "action"); 535 if (action == null) { 536 // do nothing 537 } else if ("delete".equals(action)) { 538 current.setDeleted(true); 539 current.setModified(current.isVisible()); 540 } else if ("modify".equals(action)) { 541 current.setModified(true); 542 } 543 544 String v = parser.getAttributeValue(null, "changeset"); 545 if (v == null) { 546 current.setChangesetId(0); 547 } else { 548 try { 549 current.setChangesetId(Integer.parseInt(v)); 550 } catch (IllegalArgumentException e) { 551 Logging.debug(e.getMessage()); 552 if (current.isNew()) { 553 // for a new primitive we just log a warning 554 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", 555 v, current.getUniqueId())); 556 current.setChangesetId(0); 557 } else { 558 // for an existing primitive this is a problem 559 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e); 560 } 561 } catch (IllegalStateException e) { 562 // thrown for positive changeset id on new primitives 563 Logging.debug(e); 564 Logging.info(e.getMessage()); 565 current.setChangesetId(0); 566 } 567 if (current.getChangesetId() <= 0) { 568 if (current.isNew()) { 569 // for a new primitive we just log a warning 570 Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", 571 v, current.getUniqueId())); 572 current.setChangesetId(0); 573 } else if (current.getChangesetId() < 0) { 574 // for an existing primitive this is a problem only for negative ids (GDPR extracts are set to 0) 575 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 576 } 577 } 363 private void readCommon(PrimitiveData current) throws IllegalDataException { 364 try { 365 parseId(current, getLong("id")); 366 parseTimestamp(current, parser.getAttributeValue(null, "timestamp")); 367 parseUser(current, parser.getAttributeValue(null, "user"), parser.getAttributeValue(null, "uid")); 368 parseVisible(current, parser.getAttributeValue(null, "visible")); 369 parseVersion(current, parser.getAttributeValue(null, "version")); 370 parseAction(current, parser.getAttributeValue(null, "action")); 371 parseChangeset(current, parser.getAttributeValue(null, "changeset")); 372 } catch (UncheckedParseException | XMLStreamException e) { 373 throw new IllegalDataException(e); 578 374 } 579 375 } … … 581 377 private long getLong(String name) throws XMLStreamException { 582 378 String value = parser.getAttributeValue(null, name); 583 if (value == null) { 584 throwException(tr("Missing required attribute ''{0}''.", name)); 585 } 586 try { 587 return Long.parseLong(value); 588 } catch (NumberFormatException e) { 589 throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e); 379 try { 380 return getLong(name, value); 381 } catch (IllegalDataException e) { 382 throwException(e); 590 383 } 591 384 return 0; // should not happen … … 608 401 @Override 609 402 protected DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { 610 if (progressMonitor == null) { 611 progressMonitor = NullProgressMonitor.INSTANCE; 612 } 613 ProgressMonitor.CancelListener cancelListener = () -> cancel = true; 614 progressMonitor.addCancelListener(cancelListener); 615 CheckParameterUtil.ensureParameterNotNull(source, "source"); 616 try { 617 progressMonitor.beginTask(tr("Prepare OSM data...", 2)); 618 progressMonitor.indeterminateSubTask(tr("Parsing OSM data...")); 619 620 try (InputStreamReader ir = UTFInputStreamReader.create(source)) { 403 return doParseDataSet(source, progressMonitor, ir -> { 404 try { 621 405 setParser(XmlUtils.newSafeXMLInputFactory().createXMLStreamReader(ir)); 622 406 parse(); 623 } 624 progressMonitor.worked(1); 625 626 boolean readOnly = getDataSet().isLocked(); 627 628 progressMonitor.indeterminateSubTask(tr("Preparing data set...")); 629 if (readOnly) { 630 getDataSet().unlock(); 631 } 632 prepareDataSet(); 633 if (readOnly) { 634 getDataSet().lock(); 635 } 636 progressMonitor.worked(1); 637 638 // iterate over registered postprocessors and give them each a chance 639 // to modify the dataset we have just loaded. 640 if (postprocessors != null) { 641 for (OsmServerReadPostprocessor pp : postprocessors) { 642 pp.postprocessDataSet(getDataSet(), progressMonitor); 407 } catch (XmlStreamParsingException | UncheckedParseException e) { 408 throw new IllegalDataException(e.getMessage(), e); 409 } catch (XMLStreamException e) { 410 String msg = e.getMessage(); 411 Pattern p = Pattern.compile("Message: (.+)"); 412 Matcher m = p.matcher(msg); 413 if (m.find()) { 414 msg = m.group(1); 643 415 } 644 } 645 // Make sure postprocessors did not change the read-only state 646 if (readOnly && !getDataSet().isLocked()) { 647 getDataSet().lock(); 648 } 649 return getDataSet(); 650 } catch (IllegalDataException e) { 651 throw e; 652 } catch (XmlStreamParsingException | UncheckedParseException e) { 653 throw new IllegalDataException(e.getMessage(), e); 654 } catch (XMLStreamException e) { 655 String msg = e.getMessage(); 656 Pattern p = Pattern.compile("Message: (.+)"); 657 Matcher m = p.matcher(msg); 658 if (m.find()) { 659 msg = m.group(1); 660 } 661 if (e.getLocation() != null) 662 throw new IllegalDataException(tr("Line {0} column {1}: ", 663 e.getLocation().getLineNumber(), e.getLocation().getColumnNumber()) + msg, e); 664 else 665 throw new IllegalDataException(msg, e); 666 } catch (IOException e) { 667 throw new IllegalDataException(e); 668 } finally { 669 progressMonitor.finishTask(); 670 progressMonitor.removeCancelListener(cancelListener); 671 } 416 if (e.getLocation() != null) 417 throw new IllegalDataException(tr("Line {0} column {1}: ", 418 e.getLocation().getLineNumber(), e.getLocation().getColumnNumber()) + msg, e); 419 else 420 throw new IllegalDataException(msg, e); 421 } 422 }); 672 423 } 673 424 … … 676 427 * 677 428 * @param source the source input stream. Must not be null. 678 * @param progressMonitor 429 * @param progressMonitor the progress monitor. If null, {@link NullProgressMonitor#INSTANCE} is assumed 679 430 * 680 431 * @return the dataset with the parsed data -
trunk/src/org/openstreetmap/josm/io/OverpassDownloadReader.java
r14015 r14086 83 83 } 84 84 85 static final class OverpassOsmJsonReader extends OsmJsonReader { 86 87 } 88 85 89 /** 86 90 * Possible Overpass API output format, with the {@code [out:<directive>]} statement. … … 165 169 static { 166 170 registerOverpassOutpoutFormatReader(OverpassOutpoutFormat.OSM_XML, OverpassOsmReader.class); 171 registerOverpassOutpoutFormatReader(OverpassOutpoutFormat.OSM_JSON, OverpassOsmJsonReader.class); 167 172 } 168 173 … … 410 415 return query == null ? query : query 411 416 .replaceAll("out( body| skel| ids)?( id| qt)?;", "out meta$2;") 412 .replaceAll("(?s)\\[out:( json|csv)[^\\]]*\\]", "[out:xml]");417 .replaceAll("(?s)\\[out:(csv)[^\\]]*\\]", "[out:xml]"); 413 418 } 414 419 }
Note:
See TracChangeset
for help on using the changeset viewer.