[3719] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[2433] | 2 | package org.openstreetmap.josm.data.osm;
|
---|
[35] | 3 |
|
---|
[1690] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
[3033] | 6 | import java.util.ArrayList;
|
---|
[86] | 7 | import java.util.Collection;
|
---|
[57] | 8 | import java.util.HashMap;
|
---|
[2418] | 9 | import java.util.HashSet;
|
---|
[3423] | 10 | import java.util.Iterator;
|
---|
[57] | 11 | import java.util.LinkedList;
|
---|
[1862] | 12 | import java.util.List;
|
---|
[57] | 13 | import java.util.Map;
|
---|
[2418] | 14 | import java.util.Set;
|
---|
[35] | 15 |
|
---|
[3034] | 16 | import org.openstreetmap.josm.data.conflict.Conflict;
|
---|
[1750] | 17 | import org.openstreetmap.josm.data.conflict.ConflictCollection;
|
---|
[4684] | 18 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
[2845] | 19 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
---|
[35] | 20 |
|
---|
| 21 | /**
|
---|
[2433] | 22 | * A dataset merger which takes a target and a source dataset and merges the source data set
|
---|
| 23 | * onto the target dataset.
|
---|
[2512] | 24 | *
|
---|
[35] | 25 | */
|
---|
[2433] | 26 | public class DataSetMerger {
|
---|
[35] | 27 |
|
---|
[2417] | 28 | /** the collection of conflicts created during merging */
|
---|
[2985] | 29 | private final ConflictCollection conflicts;
|
---|
[86] | 30 |
|
---|
[2417] | 31 | /** the target dataset for merging */
|
---|
[2443] | 32 | private final DataSet targetDataSet;
|
---|
[2417] | 33 | /** the source dataset where primitives are merged from */
|
---|
[2443] | 34 | private final DataSet sourceDataSet;
|
---|
[86] | 35 |
|
---|
[1169] | 36 | /**
|
---|
[2417] | 37 | * A map of all primitives that got replaced with other primitives.
|
---|
[3040] | 38 | * Key is the PrimitiveId in their dataset, the value is the PrimitiveId in my dataset
|
---|
[1169] | 39 | */
|
---|
[3040] | 40 | private final Map<PrimitiveId, PrimitiveId> mergedMap;
|
---|
[2418] | 41 | /** a set of primitive ids for which we have to fix references (to nodes and
|
---|
| 42 | * to relation members) after the first phase of merging
|
---|
| 43 | */
|
---|
[2985] | 44 | private final Set<PrimitiveId> objectsWithChildrenToMerge;
|
---|
[3423] | 45 | private final Set<OsmPrimitive> objectsToDelete;
|
---|
[2443] | 46 |
|
---|
[1690] | 47 | /**
|
---|
| 48 | * constructor
|
---|
[1862] | 49 | *
|
---|
[5881] | 50 | * The visitor will merge <code>sourceDataSet</code> onto <code>targetDataSet</code>
|
---|
[1862] | 51 | *
|
---|
[5881] | 52 | * @param targetDataSet dataset with my primitives. Must not be null.
|
---|
[2443] | 53 | * @param sourceDataSet dataset with their primitives. Ignored, if null.
|
---|
[8291] | 54 | * @throws IllegalArgumentException if myDataSet is null
|
---|
[1690] | 55 | */
|
---|
[8291] | 56 | public DataSetMerger(DataSet targetDataSet, DataSet sourceDataSet) {
|
---|
[2845] | 57 | CheckParameterUtil.ensureParameterNotNull(targetDataSet, "targetDataSet");
|
---|
[2443] | 58 | this.targetDataSet = targetDataSet;
|
---|
| 59 | this.sourceDataSet = sourceDataSet;
|
---|
[1750] | 60 | conflicts = new ConflictCollection();
|
---|
[7005] | 61 | mergedMap = new HashMap<>();
|
---|
| 62 | objectsWithChildrenToMerge = new HashSet<>();
|
---|
| 63 | objectsToDelete = new HashSet<>();
|
---|
[1169] | 64 | }
|
---|
[35] | 65 |
|
---|
[1690] | 66 | /**
|
---|
[5881] | 67 | * Merges a primitive onto primitives dataset.
|
---|
[1862] | 68 | *
|
---|
[1690] | 69 | * If other.id != 0 it tries to merge it with an corresponding primitive from
|
---|
| 70 | * my dataset with the same id. If this is not possible a conflict is remembered
|
---|
[5266] | 71 | * in {@link #conflicts}.
|
---|
[1862] | 72 | *
|
---|
[5881] | 73 | * If other.id == 0 (new primitive) it tries to find a primitive in my dataset with id == 0 which
|
---|
[1690] | 74 | * is semantically equal. If it finds one it merges its technical attributes onto
|
---|
| 75 | * my primitive.
|
---|
[1862] | 76 | *
|
---|
[5881] | 77 | * @param source the primitive to merge
|
---|
| 78 | * @param candidates a set of possible candidates for a new primitive
|
---|
[1690] | 79 | */
|
---|
[4684] | 80 | protected void mergePrimitive(OsmPrimitive source, Collection<? extends OsmPrimitive> candidates) {
|
---|
[2443] | 81 | if (!source.isNew() ) {
|
---|
[6296] | 82 | // try to merge onto a matching primitive with the same defined id
|
---|
[1690] | 83 | //
|
---|
[2443] | 84 | if (mergeById(source))
|
---|
[1169] | 85 | return;
|
---|
[1690] | 86 | } else {
|
---|
[3039] | 87 | // ignore deleted primitives from source
|
---|
| 88 | if (source.isDeleted()) return;
|
---|
| 89 |
|
---|
[1694] | 90 | // try to merge onto a primitive which has no id assigned
|
---|
[1690] | 91 | // yet but which is equal in its semantic attributes
|
---|
| 92 | //
|
---|
[2443] | 93 | for (OsmPrimitive target : candidates) {
|
---|
[3039] | 94 | if (!target.isNew() || target.isDeleted()) {
|
---|
[1690] | 95 | continue;
|
---|
| 96 | }
|
---|
[2443] | 97 | if (target.hasEqualSemanticAttributes(source)) {
|
---|
[3040] | 98 | mergedMap.put(source.getPrimitiveId(), target.getPrimitiveId());
|
---|
[6296] | 99 | // copy the technical attributes from other version
|
---|
[3039] | 100 | target.setVisible(source.isVisible());
|
---|
| 101 | target.setUser(source.getUser());
|
---|
| 102 | target.setTimestamp(source.getTimestamp());
|
---|
| 103 | target.setModified(source.isModified());
|
---|
| 104 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[1690] | 105 | return;
|
---|
| 106 | }
|
---|
[1169] | 107 | }
|
---|
| 108 | }
|
---|
[2433] | 109 |
|
---|
[1690] | 110 | // If we get here we didn't find a suitable primitive in
|
---|
[2443] | 111 | // the target dataset. Create a clone and add it to the target dataset.
|
---|
[1690] | 112 | //
|
---|
[2443] | 113 | OsmPrimitive target = null;
|
---|
| 114 | switch(source.getType()) {
|
---|
| 115 | case NODE: target = source.isNew() ? new Node() : new Node(source.getId()); break;
|
---|
| 116 | case WAY: target = source.isNew() ? new Way() : new Way(source.getId()); break;
|
---|
| 117 | case RELATION: target = source.isNew() ? new Relation() : new Relation(source.getId()); break;
|
---|
[2990] | 118 | default: throw new AssertionError();
|
---|
[2417] | 119 | }
|
---|
[2443] | 120 | target.mergeFrom(source);
|
---|
| 121 | targetDataSet.addPrimitive(target);
|
---|
[3040] | 122 | mergedMap.put(source.getPrimitiveId(), target.getPrimitiveId());
|
---|
[2563] | 123 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[1169] | 124 | }
|
---|
[35] | 125 |
|
---|
[8291] | 126 | protected OsmPrimitive getMergeTarget(OsmPrimitive mergeSource) {
|
---|
[3040] | 127 | PrimitiveId targetId = mergedMap.get(mergeSource.getPrimitiveId());
|
---|
[2417] | 128 | if (targetId == null)
|
---|
[2444] | 129 | return null;
|
---|
[3040] | 130 | return targetDataSet.getPrimitiveById(targetId);
|
---|
[2417] | 131 | }
|
---|
[6069] | 132 |
|
---|
[5298] | 133 | protected void addConflict(Conflict<?> c) {
|
---|
| 134 | c.setMergedMap(mergedMap);
|
---|
| 135 | conflicts.add(c);
|
---|
| 136 | }
|
---|
[2417] | 137 |
|
---|
[5298] | 138 | protected void addConflict(OsmPrimitive my, OsmPrimitive their) {
|
---|
[7005] | 139 | addConflict(new Conflict<>(my, their));
|
---|
[5298] | 140 | }
|
---|
| 141 |
|
---|
[2417] | 142 | protected void fixIncomplete(Way other) {
|
---|
| 143 | Way myWay = (Way)getMergeTarget(other);
|
---|
| 144 | if (myWay == null)
|
---|
| 145 | throw new RuntimeException(tr("Missing merge target for way with id {0}", other.getUniqueId()));
|
---|
[1948] | 146 | }
|
---|
| 147 |
|
---|
[1169] | 148 | /**
|
---|
| 149 | * Postprocess the dataset and fix all merged references to point to the actual
|
---|
| 150 | * data.
|
---|
| 151 | */
|
---|
| 152 | public void fixReferences() {
|
---|
[2443] | 153 | for (Way w : sourceDataSet.getWays()) {
|
---|
[2563] | 154 | if (!conflicts.hasConflictForTheir(w) && objectsWithChildrenToMerge.contains(w.getPrimitiveId())) {
|
---|
[2417] | 155 | mergeNodeList(w);
|
---|
| 156 | fixIncomplete(w);
|
---|
| 157 | }
|
---|
[1690] | 158 | }
|
---|
[2443] | 159 | for (Relation r : sourceDataSet.getRelations()) {
|
---|
[2563] | 160 | if (!conflicts.hasConflictForTheir(r) && objectsWithChildrenToMerge.contains(r.getPrimitiveId())) {
|
---|
[2417] | 161 | mergeRelationMembers(r);
|
---|
| 162 | }
|
---|
[1690] | 163 | }
|
---|
[3423] | 164 |
|
---|
| 165 | deleteMarkedObjects();
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | /**
|
---|
| 169 | * Deleted objects in objectsToDelete set and create conflicts for objects that cannot
|
---|
| 170 | * be deleted because they're referenced in the target dataset.
|
---|
| 171 | */
|
---|
| 172 | protected void deleteMarkedObjects() {
|
---|
| 173 | boolean flag;
|
---|
| 174 | do {
|
---|
| 175 | flag = false;
|
---|
| 176 | for (Iterator<OsmPrimitive> it = objectsToDelete.iterator();it.hasNext();) {
|
---|
| 177 | OsmPrimitive target = it.next();
|
---|
| 178 | OsmPrimitive source = sourceDataSet.getPrimitiveById(target.getPrimitiveId());
|
---|
| 179 | if (source == null)
|
---|
[4253] | 180 | throw new RuntimeException(tr("Object of type {0} with id {1} was marked to be deleted, but it''s missing in the source dataset",
|
---|
[3423] | 181 | target.getType(), target.getUniqueId()));
|
---|
| 182 |
|
---|
| 183 | List<OsmPrimitive> referrers = target.getReferrers();
|
---|
| 184 | if (referrers.isEmpty()) {
|
---|
[5350] | 185 | resetPrimitive(target);
|
---|
| 186 | target.mergeFrom(source);
|
---|
[3423] | 187 | target.setDeleted(true);
|
---|
| 188 | it.remove();
|
---|
| 189 | flag = true;
|
---|
| 190 | } else {
|
---|
| 191 | for (OsmPrimitive referrer : referrers) {
|
---|
| 192 | // If one of object referrers isn't going to be deleted,
|
---|
| 193 | // add a conflict and don't delete the object
|
---|
| 194 | if (!objectsToDelete.contains(referrer)) {
|
---|
[5298] | 195 | addConflict(target, source);
|
---|
[3423] | 196 | it.remove();
|
---|
| 197 | flag = true;
|
---|
| 198 | break;
|
---|
| 199 | }
|
---|
| 200 | }
|
---|
| 201 | }
|
---|
| 202 |
|
---|
| 203 | }
|
---|
| 204 | } while (flag);
|
---|
| 205 |
|
---|
| 206 | if (!objectsToDelete.isEmpty()) {
|
---|
| 207 | // There are some more objects rest in the objectsToDelete set
|
---|
| 208 | // This can be because of cross-referenced relations.
|
---|
| 209 | for (OsmPrimitive osm: objectsToDelete) {
|
---|
[5350] | 210 | resetPrimitive(osm);
|
---|
[3423] | 211 | }
|
---|
| 212 | for (OsmPrimitive osm: objectsToDelete) {
|
---|
| 213 | osm.setDeleted(true);
|
---|
| 214 | osm.mergeFrom(sourceDataSet.getPrimitiveById(osm.getPrimitiveId()));
|
---|
| 215 | }
|
---|
[2443] | 216 | }
|
---|
[1169] | 217 | }
|
---|
[6069] | 218 |
|
---|
[5350] | 219 | private final void resetPrimitive(OsmPrimitive osm) {
|
---|
| 220 | if (osm instanceof Way) {
|
---|
| 221 | ((Way) osm).setNodes(null);
|
---|
| 222 | } else if (osm instanceof Relation) {
|
---|
| 223 | ((Relation) osm).setMembers(null);
|
---|
| 224 | }
|
---|
| 225 | }
|
---|
[86] | 226 |
|
---|
[2443] | 227 | /**
|
---|
| 228 | * Merges the node list of a source way onto its target way.
|
---|
[2512] | 229 | *
|
---|
[2443] | 230 | * @param source the source way
|
---|
[8291] | 231 | * @throws IllegalStateException if no target way can be found for the source way
|
---|
| 232 | * @throws IllegalStateException if there isn't a target node for one of the nodes in the source way
|
---|
[2512] | 233 | *
|
---|
[2443] | 234 | */
|
---|
[8291] | 235 | private void mergeNodeList(Way source) {
|
---|
[2443] | 236 | Way target = (Way)getMergeTarget(source);
|
---|
| 237 | if (target == null)
|
---|
| 238 | throw new IllegalStateException(tr("Missing merge target for way with id {0}", source.getUniqueId()));
|
---|
[1753] | 239 |
|
---|
[7005] | 240 | List<Node> newNodes = new ArrayList<>(source.getNodesCount());
|
---|
[2443] | 241 | for (Node sourceNode : source.getNodes()) {
|
---|
| 242 | Node targetNode = (Node)getMergeTarget(sourceNode);
|
---|
| 243 | if (targetNode != null) {
|
---|
[3336] | 244 | newNodes.add(targetNode);
|
---|
| 245 | if (targetNode.isDeleted() && !conflicts.hasConflictForMy(targetNode)) {
|
---|
[5298] | 246 | addConflict(new Conflict<OsmPrimitive>(targetNode, sourceNode, true));
|
---|
[3336] | 247 | targetNode.setDeleted(false);
|
---|
[1753] | 248 | }
|
---|
[2417] | 249 | } else
|
---|
[2443] | 250 | throw new IllegalStateException(tr("Missing merge target for node with id {0}", sourceNode.getUniqueId()));
|
---|
[2417] | 251 | }
|
---|
[2443] | 252 | target.setNodes(newNodes);
|
---|
[86] | 253 | }
|
---|
| 254 |
|
---|
[2443] | 255 | /**
|
---|
| 256 | * Merges the relation members of a source relation onto the corresponding target relation.
|
---|
| 257 | * @param source the source relation
|
---|
[8291] | 258 | * @throws IllegalStateException if there is no corresponding target relation
|
---|
| 259 | * @throws IllegalStateException if there isn't a corresponding target object for one of the relation
|
---|
[2443] | 260 | * members in source
|
---|
| 261 | */
|
---|
[8291] | 262 | private void mergeRelationMembers(Relation source) {
|
---|
[2443] | 263 | Relation target = (Relation) getMergeTarget(source);
|
---|
| 264 | if (target == null)
|
---|
| 265 | throw new IllegalStateException(tr("Missing merge target for relation with id {0}", source.getUniqueId()));
|
---|
[8338] | 266 | List<RelationMember> newMembers = new LinkedList<>();
|
---|
[2443] | 267 | for (RelationMember sourceMember : source.getMembers()) {
|
---|
| 268 | OsmPrimitive targetMember = getMergeTarget(sourceMember.getMember());
|
---|
| 269 | if (targetMember == null)
|
---|
[2626] | 270 | throw new IllegalStateException(tr("Missing merge target of type {0} with id {1}", sourceMember.getType(), sourceMember.getUniqueId()));
|
---|
[3336] | 271 | RelationMember newMember = new RelationMember(sourceMember.getRole(), targetMember);
|
---|
| 272 | newMembers.add(newMember);
|
---|
| 273 | if (targetMember.isDeleted() && !conflicts.hasConflictForMy(targetMember)) {
|
---|
[7005] | 274 | addConflict(new Conflict<>(targetMember, sourceMember.getMember(), true));
|
---|
[3336] | 275 | targetMember.setDeleted(false);
|
---|
[2417] | 276 | }
|
---|
| 277 | }
|
---|
[2443] | 278 | target.setMembers(newMembers);
|
---|
[1169] | 279 | }
|
---|
[366] | 280 |
|
---|
[1169] | 281 | /**
|
---|
[2443] | 282 | * Tries to merge a primitive <code>source</code> into an existing primitive with the same id.
|
---|
[1677] | 283 | *
|
---|
[2471] | 284 | * @param source the source primitive which is to be merged into a target primitive
|
---|
[2443] | 285 | * @return true, if this method was able to merge <code>source</code> into a target object; false, otherwise
|
---|
[1169] | 286 | */
|
---|
[2443] | 287 | private boolean mergeById(OsmPrimitive source) {
|
---|
| 288 | OsmPrimitive target = targetDataSet.getPrimitiveById(source.getId(), source.getType());
|
---|
[1567] | 289 | // merge other into an existing primitive with the same id, if possible
|
---|
| 290 | //
|
---|
[2443] | 291 | if (target == null)
|
---|
[2418] | 292 | return false;
|
---|
[2443] | 293 | // found a corresponding target, remember it
|
---|
[3040] | 294 | mergedMap.put(source.getPrimitiveId(), target.getPrimitiveId());
|
---|
[2443] | 295 |
|
---|
| 296 | if (target.getVersion() > source.getVersion())
|
---|
| 297 | // target.version > source.version => keep target version
|
---|
[2418] | 298 | return true;
|
---|
[3362] | 299 |
|
---|
| 300 | if (target.isIncomplete() && !source.isIncomplete()) {
|
---|
[2443] | 301 | // target is incomplete, source completes it
|
---|
| 302 | // => merge source into target
|
---|
[2418] | 303 | //
|
---|
[2443] | 304 | target.mergeFrom(source);
|
---|
[2563] | 305 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[2578] | 306 | } else if (!target.isIncomplete() && source.isIncomplete()) {
|
---|
[2443] | 307 | // target is complete and source is incomplete
|
---|
| 308 | // => keep target, it has more information already
|
---|
[2418] | 309 | //
|
---|
[2578] | 310 | } else if (target.isIncomplete() && source.isIncomplete()) {
|
---|
[2443] | 311 | // target and source are incomplete. Doesn't matter which one to
|
---|
| 312 | // take. We take target.
|
---|
[2418] | 313 | //
|
---|
[3965] | 314 | } else if (!target.isModified() && !source.isModified() && target.isVisible() != source.isVisible() && target.getVersion() == source.getVersion())
|
---|
| 315 | // Same version, but different "visible" attribute and neither of them are modified.
|
---|
| 316 | // It indicates a serious problem in datasets.
|
---|
[3423] | 317 | // For example, datasets can be fetched from different OSM servers or badly hand-modified.
|
---|
| 318 | // We shouldn't merge that datasets.
|
---|
[4253] | 319 | throw new DataIntegrityProblemException(tr("Conflict in ''visible'' attribute for object of type {0} with id {1}",
|
---|
[3423] | 320 | target.getType(), target.getId()));
|
---|
| 321 | else if (target.isDeleted() && ! source.isDeleted() && target.getVersion() == source.getVersion()) {
|
---|
[2443] | 322 | // same version, but target is deleted. Assume target takes precedence
|
---|
[2418] | 323 | // otherwise too many conflicts when refreshing from the server
|
---|
[2935] | 324 | // but, if source has a referrer that is not in the target dataset there is a conflict
|
---|
[3033] | 325 | // If target dataset refers to the deleted primitive, conflict will be added in fixReferences method
|
---|
[2935] | 326 | for (OsmPrimitive referrer: source.getReferrers()) {
|
---|
| 327 | if (targetDataSet.getPrimitiveById(referrer.getPrimitiveId()) == null) {
|
---|
[7005] | 328 | addConflict(new Conflict<>(target, source, true));
|
---|
[3034] | 329 | target.setDeleted(false);
|
---|
[2935] | 330 | break;
|
---|
| 331 | }
|
---|
[2933] | 332 | }
|
---|
[3423] | 333 | } else if (! target.isModified() && source.isDeleted()) {
|
---|
| 334 | // target not modified. We can assume that source is the most recent version,
|
---|
| 335 | // so mark it to be deleted.
|
---|
[2418] | 336 | //
|
---|
[3423] | 337 | objectsToDelete.add(target);
|
---|
[2443] | 338 | } else if (! target.isModified() && source.isModified()) {
|
---|
| 339 | // target not modified. We can assume that source is the most recent version.
|
---|
[3423] | 340 | // clone it into target.
|
---|
| 341 | target.mergeFrom(source);
|
---|
| 342 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[2443] | 343 | } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) {
|
---|
[2753] | 344 | // both not modified. Merge nevertheless.
|
---|
| 345 | // This helps when updating "empty" relations, see #4295
|
---|
| 346 | target.mergeFrom(source);
|
---|
| 347 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[2443] | 348 | } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) {
|
---|
[2418] | 349 | // my not modified but other is newer. clone other onto mine.
|
---|
| 350 | //
|
---|
[2443] | 351 | target.mergeFrom(source);
|
---|
[2563] | 352 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[2443] | 353 | } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) {
|
---|
[2444] | 354 | // target is same as source but target is modified
|
---|
[2634] | 355 | // => keep target and reset modified flag if target and source are semantically equal
|
---|
| 356 | if (target.hasEqualSemanticAttributes(source)) {
|
---|
| 357 | target.setModified(false);
|
---|
| 358 | }
|
---|
[3423] | 359 | } else if (source.isDeleted() != target.isDeleted()) {
|
---|
| 360 | // target is modified and deleted state differs.
|
---|
| 361 | // this have to be resolved manually.
|
---|
| 362 | //
|
---|
[5298] | 363 | addConflict(target,source);
|
---|
[2443] | 364 | } else if (! target.hasEqualSemanticAttributes(source)) {
|
---|
[2444] | 365 | // target is modified and is not semantically equal with source. Can't automatically
|
---|
[2418] | 366 | // resolve the differences
|
---|
| 367 | // => create a conflict
|
---|
[5298] | 368 | addConflict(target,source);
|
---|
[2418] | 369 | } else {
|
---|
[3377] | 370 | // clone from other. mergeFrom will mainly copy
|
---|
[2418] | 371 | // technical attributes like timestamp or user information. Semantic
|
---|
| 372 | // attributes should already be equal if we get here.
|
---|
| 373 | //
|
---|
[2443] | 374 | target.mergeFrom(source);
|
---|
[2563] | 375 | objectsWithChildrenToMerge.add(source.getPrimitiveId());
|
---|
[1169] | 376 | }
|
---|
[2418] | 377 | return true;
|
---|
[1169] | 378 | }
|
---|
[1690] | 379 |
|
---|
| 380 | /**
|
---|
[5266] | 381 | * Runs the merge operation. Successfully merged {@link OsmPrimitive}s are in
|
---|
[5881] | 382 | * {@link #getTargetDataSet()}.
|
---|
[1862] | 383 | *
|
---|
[5266] | 384 | * See {@link #getConflicts()} for a map of conflicts after the merge operation.
|
---|
[1690] | 385 | */
|
---|
| 386 | public void merge() {
|
---|
[4684] | 387 | merge(null);
|
---|
| 388 | }
|
---|
| 389 |
|
---|
| 390 | /**
|
---|
[5266] | 391 | * Runs the merge operation. Successfully merged {@link OsmPrimitive}s are in
|
---|
[5881] | 392 | * {@link #getTargetDataSet()}.
|
---|
[4684] | 393 | *
|
---|
[5266] | 394 | * See {@link #getConflicts()} for a map of conflicts after the merge operation.
|
---|
[6881] | 395 | * @param progressMonitor The progress monitor
|
---|
[4684] | 396 | */
|
---|
| 397 | public void merge(ProgressMonitor progressMonitor) {
|
---|
[2443] | 398 | if (sourceDataSet == null)
|
---|
[2433] | 399 | return;
|
---|
[4684] | 400 | if (progressMonitor != null) {
|
---|
| 401 | progressMonitor.beginTask(tr("Merging data..."), sourceDataSet.allPrimitives().size());
|
---|
| 402 | }
|
---|
[2497] | 403 | targetDataSet.beginUpdate();
|
---|
| 404 | try {
|
---|
[7005] | 405 | List<? extends OsmPrimitive> candidates = new ArrayList<>(targetDataSet.getNodes());
|
---|
[2497] | 406 | for (Node node: sourceDataSet.getNodes()) {
|
---|
[4684] | 407 | mergePrimitive(node, candidates);
|
---|
| 408 | if (progressMonitor != null) {
|
---|
| 409 | progressMonitor.worked(1);
|
---|
| 410 | }
|
---|
[2497] | 411 | }
|
---|
[4684] | 412 | candidates.clear();
|
---|
[7005] | 413 | candidates = new ArrayList<>(targetDataSet.getWays());
|
---|
[2497] | 414 | for (Way way: sourceDataSet.getWays()) {
|
---|
[4684] | 415 | mergePrimitive(way, candidates);
|
---|
| 416 | if (progressMonitor != null) {
|
---|
| 417 | progressMonitor.worked(1);
|
---|
| 418 | }
|
---|
[2497] | 419 | }
|
---|
[4684] | 420 | candidates.clear();
|
---|
[7005] | 421 | candidates = new ArrayList<>(targetDataSet.getRelations());
|
---|
[2497] | 422 | for (Relation relation: sourceDataSet.getRelations()) {
|
---|
[4684] | 423 | mergePrimitive(relation, candidates);
|
---|
| 424 | if (progressMonitor != null) {
|
---|
| 425 | progressMonitor.worked(1);
|
---|
| 426 | }
|
---|
[2497] | 427 | }
|
---|
[4684] | 428 | candidates.clear();
|
---|
[2497] | 429 | fixReferences();
|
---|
| 430 | } finally {
|
---|
| 431 | targetDataSet.endUpdate();
|
---|
[1690] | 432 | }
|
---|
[4684] | 433 | if (progressMonitor != null) {
|
---|
| 434 | progressMonitor.finishTask();
|
---|
| 435 | }
|
---|
[1690] | 436 | }
|
---|
| 437 |
|
---|
| 438 | /**
|
---|
| 439 | * replies my dataset
|
---|
[1862] | 440 | *
|
---|
[5881] | 441 | * @return the own (target) data set
|
---|
[1690] | 442 | */
|
---|
[2443] | 443 | public DataSet getTargetDataSet() {
|
---|
| 444 | return targetDataSet;
|
---|
[1690] | 445 | }
|
---|
| 446 |
|
---|
| 447 | /**
|
---|
| 448 | * replies the map of conflicts
|
---|
[1862] | 449 | *
|
---|
[1690] | 450 | * @return the map of conflicts
|
---|
| 451 | */
|
---|
[1750] | 452 | public ConflictCollection getConflicts() {
|
---|
[1690] | 453 | return conflicts;
|
---|
| 454 | }
|
---|
[35] | 455 | }
|
---|