Changeset 14086 in josm for trunk/src/org/openstreetmap/josm/io/AbstractReader.java
- Timestamp:
- 2018-08-05T14:09:31+02:00 (6 years ago)
- File:
-
- 1 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 }
Note:
See TracChangeset
for help on using the changeset viewer.