Changeset 17341 in josm for trunk/src


Ignore:
Timestamp:
2020-11-23T20:45:59+01:00 (3 years ago)
Author:
GerdP
Message:

fix #20091: Downloading incomplete, deleted members leads to data inconsitency

  • changes the logic of "download members" so that all non-new members of all selected relations are downloaded. It doesn't recurse down on child relations. The old code did a download of members using the relation id, so the server returned the members of the version as known by the server. Disadvantage: The new code is slower.
  • changes and simplifies DataSetMerger to fix the wrong handling of deleted objects in the source
  • additional unit tests to cover more branches in mergeById()
  • also fixes #19783
Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java

    r14397 r17341  
    77import java.awt.event.ActionEvent;
    88import java.util.Collection;
     9import java.util.List;
     10import java.util.stream.Collectors;
    911
    1012import org.openstreetmap.josm.data.osm.IPrimitive;
    11 import org.openstreetmap.josm.data.osm.Relation;
     13import org.openstreetmap.josm.data.osm.PrimitiveId;
    1214import org.openstreetmap.josm.gui.MainApplication;
    13 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask;
     15import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask;
    1416import org.openstreetmap.josm.tools.ImageProvider;
    15 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
    16 import org.openstreetmap.josm.tools.Utils;
    1717
    1818/**
     
    3535    public void actionPerformed(ActionEvent e) {
    3636        if (!isEnabled() || relations.isEmpty() || !MainApplication.isDisplayingMapView()) return;
    37         MainApplication.worker.submit(new DownloadRelationTask(
    38                 Utils.filteredCollection(relations, Relation.class), MainApplication.getLayerManager().getEditLayer()));
     37        List<PrimitiveId> members = relations.stream()
     38                .flatMap(r -> r.getMemberPrimitivesList().stream().filter(osm -> !osm.isNew()).map(IPrimitive::getOsmPrimitiveId))
     39                .distinct()
     40                .collect(Collectors.toList());
     41
     42        MainApplication.worker.submit(new DownloadPrimitivesTask(MainApplication.getLayerManager().getEditLayer(), members, false));
    3943    }
    4044
    4145    @Override
    4246    public void setPrimitives(Collection<? extends IPrimitive> primitives) {
    43         // selected non-new relations
    44         this.relations = SubclassFilteredCollection.filter(getRelations(primitives), r -> !r.isNew());
     47        this.relations = getRelations(primitives);
    4548        updateEnabledState();
    4649    }
  • trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java

    r17136 r17341  
    302302            return true;
    303303
    304         if (target.isIncomplete() && !source.isIncomplete()) {
    305             // target is incomplete, source completes it
    306             // => merge source into target
    307             //
    308             target.mergeFrom(source);
    309             objectsWithChildrenToMerge.add(source.getPrimitiveId());
    310         } else if (!target.isIncomplete() && source.isIncomplete()) {
    311             // target is complete and source is incomplete
    312             // => keep target, it has more information already
    313             //
    314         } else if (target.isIncomplete() && source.isIncomplete()) {
    315             // target and source are incomplete. Doesn't matter which one to
    316             // take. We take target.
    317             //
    318         } else if (!target.isModified() && !source.isModified() && target.isVisible() != source.isVisible()
    319                 && target.getVersion() == source.getVersion()) {
     304        boolean mergeFromSource = false;
     305        boolean haveSameVersion = target.getVersion() == source.getVersion();
     306
     307        if (haveSameVersion && !target.isModified() && !source.isModified()
     308                && target.isVisible() != source.isVisible()) {
    320309            // Same version, but different "visible" attribute and neither of them are modified.
    321310            // It indicates a serious problem in datasets.
     
    324313            throw new DataIntegrityProblemException(tr("Conflict in ''visible'' attribute for object of type {0} with id {1}",
    325314                    target.getType(), target.getId()));
    326         } else if (target.isDeleted() && !source.isDeleted() && target.getVersion() == source.getVersion()) {
     315        }
     316
     317        if (!target.isModified() && source.isDeleted()) {
     318            // target not modified and source is deleted
     319            // So mark it to be deleted. See #20091
     320            //
     321            objectsToDelete.add(target);
     322        } else if (source.isIncomplete()) {
     323            // source is incomplete. Nothing to do.
     324            //
     325        } else if (target.isIncomplete()) {
     326            // target is incomplete, source completes it
     327            // => merge source into target
     328            //
     329            mergeFromSource = true;
     330        } else if (target.isDeleted() && source.isDeleted() && !haveSameVersion) {
     331            // both deleted. Source is newer. Take source. See #19783
     332            mergeFromSource = true;
     333        } else if (target.isDeleted() && !source.isDeleted() && haveSameVersion) {
    327334            // same version, but target is deleted. Assume target takes precedence
    328335            // otherwise too many conflicts when refreshing from the server
     
    340347                }
    341348            }
    342         } else if (!target.isModified() && source.isDeleted()) {
    343             // target not modified. We can assume that source is the most recent version,
    344             // so mark it to be deleted.
    345             //
    346             objectsToDelete.add(target);
    347349        } else if (!target.isModified() && source.isModified()) {
    348350            // target not modified. We can assume that source is the most recent version.
    349351            // clone it into target.
    350             target.mergeFrom(source);
    351             objectsWithChildrenToMerge.add(source.getPrimitiveId());
    352         } else if (!target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
    353             // both not modified. Merge nevertheless.
     352            mergeFromSource = true;
     353        } else if (!target.isModified() && !source.isModified()) {
     354            // both not modified. Merge nevertheless, even if versions are the same
    354355            // This helps when updating "empty" relations, see #4295
    355             target.mergeFrom(source);
    356             objectsWithChildrenToMerge.add(source.getPrimitiveId());
    357         } else if (!target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) {
    358             // my not modified but other is newer. clone other onto mine.
    359             //
    360             target.mergeFrom(source);
    361             objectsWithChildrenToMerge.add(source.getPrimitiveId());
    362         } else if (target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
     356            mergeFromSource = true;
     357        } else if (target.isModified() && !source.isModified() && haveSameVersion) {
    363358            // target is same as source but target is modified
    364359            // => keep target and reset modified flag if target and source are semantically equal
     
    368363        } else if (source.isDeleted() != target.isDeleted()) {
    369364            // target is modified and deleted state differs.
    370             // this have to be resolved manually.
     365            // this has to be resolved manually.
    371366            //
    372367            addConflict(target, source);
     
    381376            // attributes should already be equal if we get here.
    382377            //
     378            mergeFromSource = true;
     379        }
     380        if (mergeFromSource) {
    383381            target.mergeFrom(source);
    384382            objectsWithChildrenToMerge.add(source.getPrimitiveId());
Note: See TracChangeset for help on using the changeset viewer.