- Timestamp:
- 2009-11-13T10:24:58+01:00 (15 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/DataSetMerger.java
r2433 r2443 26 26 27 27 /** the target dataset for merging */ 28 private final DataSet myDataSet;28 private final DataSet targetDataSet; 29 29 /** the source dataset where primitives are merged from */ 30 private final DataSet theirDataSet;30 private final DataSet sourceDataSet; 31 31 32 32 /** … … 38 38 * to relation members) after the first phase of merging 39 39 */ 40 private Set<Long> fixReferences; 40 private Set<Long> childrenToMerge; 41 42 private Set<OsmPrimitive> deletedObjectsToUnlink; 41 43 42 44 /** … … 45 47 * The visitor will merge <code>theirDataSet</code> onto <code>myDataSet</code> 46 48 * 47 * @param myDataSet dataset with my primitives. Must not be null.48 * @param theirDataSet dataset with their primitives. Ignored, if null.49 * @param targetDataSet dataset with my primitives. Must not be null. 50 * @param sourceDataSet dataset with their primitives. Ignored, if null. 49 51 * @throws IllegalArgumentException thrown if myDataSet is null 50 52 */ 51 public DataSetMerger(DataSet myDataSet, DataSet theirDataSet) throws IllegalArgumentException {52 if ( myDataSet == null)53 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null" ));54 this. myDataSet = myDataSet;55 this. theirDataSet = theirDataSet;53 public DataSetMerger(DataSet targetDataSet, DataSet sourceDataSet) throws IllegalArgumentException { 54 if (targetDataSet == null) 55 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "targetDataSet")); 56 this.targetDataSet = targetDataSet; 57 this.sourceDataSet = sourceDataSet; 56 58 conflicts = new ConflictCollection(); 57 59 mergedMap = new HashMap<Long, Long>(); 58 fixReferences = new HashSet<Long>(); 60 childrenToMerge = new HashSet<Long>(); 61 deletedObjectsToUnlink = new HashSet<OsmPrimitive>(); 59 62 } 60 63 … … 71 74 * 72 75 * @param <P> the type of the other primitive 73 * @param otherthe other primitive74 */ 75 protected <P extends OsmPrimitive> void mergePrimitive(P other) {76 if (! other.isNew() ) {76 * @param source the other primitive 77 */ 78 protected <P extends OsmPrimitive> void mergePrimitive(P source) { 79 if (!source.isNew() ) { 77 80 // try to merge onto a matching primitive with the same 78 81 // defined id 79 82 // 80 if (mergeById( other))83 if (mergeById(source)) 81 84 return; 82 if (! other.isVisible())85 if (!source.isVisible()) 83 86 // ignore it 84 87 return; … … 88 91 // 89 92 Collection<? extends OsmPrimitive> candidates = null; 90 switch( other.getType()) {91 case NODE: candidates = myDataSet.getNodes(); break;92 case WAY: candidates = myDataSet.getWays(); break;93 case RELATION: candidates = myDataSet.getRelations(); break;94 } 95 for (OsmPrimitive my: candidates) {96 if (! my.isNew()) {93 switch(source.getType()) { 94 case NODE: candidates = targetDataSet.getNodes(); break; 95 case WAY: candidates =targetDataSet.getWays(); break; 96 case RELATION: candidates = targetDataSet.getRelations(); break; 97 } 98 for (OsmPrimitive target : candidates) { 99 if (!target.isNew()) { 97 100 continue; 98 101 } 99 if ( my.hasEqualSemanticAttributes(other)) {100 mergedMap.put( other.getUniqueId(), my.getUniqueId());101 if ( my.isDeleted() != other.isDeleted()) {102 if (target.hasEqualSemanticAttributes(source)) { 103 mergedMap.put(source.getUniqueId(), target.getUniqueId()); 104 if (target.isDeleted() != source.isDeleted()) { 102 105 // differences in deleted state have to be merged manually 103 106 // 104 conflicts.add( my, other);107 conflicts.add(target, source); 105 108 } else { 106 109 // copy the technical attributes from other 107 110 // version 108 my.setVisible(other.isVisible());109 my.setUser(other.getUser());110 my.setTimestamp(other.getTimestamp());111 my.setModified(other.isModified());112 fixReferences.add(other.getUniqueId());111 target.setVisible(source.isVisible()); 112 target.setUser(source.getUser()); 113 target.setTimestamp(source.getTimestamp()); 114 target.setModified(source.isModified()); 115 childrenToMerge.add(source.getUniqueId()); 113 116 } 114 117 return; … … 118 121 119 122 // If we get here we didn't find a suitable primitive in 120 // my dataset. Create a clone and add it to mydataset.123 // the target dataset. Create a clone and add it to the target dataset. 121 124 // 122 OsmPrimitive my= null;123 switch( other.getType()) {124 case NODE: my = other.isNew() ? new Node() : new Node(other.getId()); break;125 case WAY: my = other.isNew() ? new Way() : new Way(other.getId()); break;126 case RELATION: my = other.isNew() ? new Relation() : new Relation(other.getId()); break;127 } 128 my.mergeFrom(other);129 myDataSet.addPrimitive(my);130 mergedMap.put( other.getUniqueId(), my.getUniqueId());131 fixReferences.add(other.getUniqueId());125 OsmPrimitive target = null; 126 switch(source.getType()) { 127 case NODE: target = source.isNew() ? new Node() : new Node(source.getId()); break; 128 case WAY: target = source.isNew() ? new Way() : new Way(source.getId()); break; 129 case RELATION: target = source.isNew() ? new Relation() : new Relation(source.getId()); break; 130 } 131 target.mergeFrom(source); 132 targetDataSet.addPrimitive(target); 133 mergedMap.put(source.getUniqueId(), target.getUniqueId()); 134 childrenToMerge.add(source.getUniqueId()); 132 135 } 133 136 … … 136 139 if (targetId == null) 137 140 throw new RuntimeException(tr("Missing merge target for way with id {0}", mergeSource.getUniqueId())); 138 return myDataSet.getPrimitiveById(targetId, mergeSource.getType());141 return targetDataSet.getPrimitiveById(targetId, mergeSource.getType()); 139 142 } 140 143 … … 156 159 */ 157 160 public void fixReferences() { 158 for (Way w : theirDataSet.getWays()) {159 if (!conflicts.hasConflictForTheir(w) && fixReferences.contains(w.getUniqueId())) {161 for (Way w : sourceDataSet.getWays()) { 162 if (!conflicts.hasConflictForTheir(w) && childrenToMerge.contains(w.getUniqueId())) { 160 163 mergeNodeList(w); 161 164 fixIncomplete(w); 162 165 } 163 166 } 164 for (Relation r : theirDataSet.getRelations()) {165 if (!conflicts.hasConflictForTheir(r) && fixReferences.contains(r.getUniqueId())) {167 for (Relation r : sourceDataSet.getRelations()) { 168 if (!conflicts.hasConflictForTheir(r) && childrenToMerge.contains(r.getUniqueId())) { 166 169 mergeRelationMembers(r); 167 170 } 168 171 } 169 } 170 171 private void mergeNodeList(Way other) { 172 Way myWay = (Way)getMergeTarget(other); 173 if (myWay == null) 174 throw new RuntimeException(tr("Missing merge target for way with id {0}", other.getUniqueId())); 175 176 List<Node> myNodes = new LinkedList<Node>(); 177 for (Node otherNode : other.getNodes()) { 178 Node myNode = (Node)getMergeTarget(otherNode); 179 if (myNode != null) { 180 if (!myNode.isDeleted()) { 181 myNodes.add(myNode); 172 for (OsmPrimitive source: deletedObjectsToUnlink) { 173 OsmPrimitive target = getMergeTarget(source); 174 if (target == null) 175 throw new RuntimeException(tr("Missing merge target for object with id {0}", source.getUniqueId())); 176 targetDataSet.unlinkReferencesToPrimitive(target); 177 } 178 } 179 180 /** 181 * Merges the node list of a source way onto its target way. 182 * 183 * @param source the source way 184 * @throws IllegalStateException thrown if no target way can be found for the source way 185 * @throws IllegalStateException thrown if there isn't a target node for one of the nodes in the source way 186 * 187 */ 188 private void mergeNodeList(Way source) throws IllegalStateException { 189 Way target = (Way)getMergeTarget(source); 190 if (target == null) 191 throw new IllegalStateException(tr("Missing merge target for way with id {0}", source.getUniqueId())); 192 193 List<Node> newNodes = new LinkedList<Node>(); 194 for (Node sourceNode : source.getNodes()) { 195 Node targetNode = (Node)getMergeTarget(sourceNode); 196 if (targetNode != null) { 197 if (!targetNode.isDeleted() && targetNode.isVisible()) { 198 newNodes.add(targetNode); 199 } else { 200 target.setModified(true); 182 201 } 183 202 } else 184 throw new RuntimeException(tr("Missing merge target for node with id {0}", otherNode.getUniqueId())); 185 } 186 187 // check whether the node list has changed. If so, set the modified flag on the way 188 // 189 if (myWay.getNodes().size() != myNodes.size()) { 190 myWay.setModified(true); 191 } else { 192 for (int i=0; i< myWay.getNodesCount();i++) { 193 Node n1 = myWay.getNode(i); 194 Node n2 = myNodes.get(i); 195 if (n1.isNew() ^ n2.isNew()) { 196 myWay.setModified(true); 197 break; 198 } else if (n1.isNew() && n1 != n2) { 199 myWay.setModified(true); 200 break; 201 } else if (! n1.isNew() && n1.getId() != n2.getId()) { 202 myWay.setModified(true); 203 break; 204 } 205 } 206 } 207 myWay.setNodes(myNodes); 208 } 209 210 private void mergeRelationMembers(Relation other) { 211 Relation myRelation = (Relation) getMergeTarget(other); 212 if (myRelation == null) 213 throw new RuntimeException(tr("Missing merge target for relation with id {0}", other.getUniqueId())); 203 throw new IllegalStateException(tr("Missing merge target for node with id {0}", sourceNode.getUniqueId())); 204 } 205 target.setNodes(newNodes); 206 } 207 208 209 /** 210 * Merges the relation members of a source relation onto the corresponding target relation. 211 * @param source the source relation 212 * @throws IllegalStateException thrown if there is no corresponding target relation 213 * @throws IllegalStateException thrown if there isn't a corresponding target object for one of the relation 214 * members in source 215 */ 216 private void mergeRelationMembers(Relation source) throws IllegalStateException { 217 Relation target = (Relation) getMergeTarget(source); 218 if (target == null) 219 throw new IllegalStateException(tr("Missing merge target for relation with id {0}", source.getUniqueId())); 214 220 LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>(); 215 for (RelationMember otherMember : other.getMembers()) {216 OsmPrimitive mergedMember = getMergeTarget(otherMember.getMember());217 if ( mergedMember == null)218 throw new RuntimeException(tr("Missing merge target of type {0} with id {1}", mergedMember.getType(), mergedMember.getUniqueId()));219 if (! mergedMember.isDeleted()) {220 RelationMember newMember = new RelationMember( otherMember.getRole(), mergedMember);221 for (RelationMember sourceMember : source.getMembers()) { 222 OsmPrimitive targetMember = getMergeTarget(sourceMember.getMember()); 223 if (targetMember == null) 224 throw new IllegalStateException(tr("Missing merge target of type {0} with id {1}", targetMember.getType(), targetMember.getUniqueId())); 225 if (! targetMember.isDeleted() && targetMember.isVisible()) { 226 RelationMember newMember = new RelationMember(sourceMember.getRole(), targetMember); 221 227 newMembers.add(newMember); 222 } 223 } 224 225 // check whether the list of relation members has changed 226 // 227 if (other.getMembersCount() != newMembers.size()) { 228 myRelation.setModified(true); 229 } else { 230 for (int i=0; i<other.getMembersCount();i++) { 231 RelationMember rm1 = other.getMember(i); 232 RelationMember rm2 = newMembers.get(i); 233 if (!rm1.getRole().equals(rm2.getRole())) { 234 myRelation.setModified(true); 235 break; 236 } else if (rm1.getMember().isNew() ^ rm2.getMember().isNew()) { 237 myRelation.setModified(true); 238 break; 239 } else if (rm1.getMember().isNew() && rm1.getMember() != rm2.getMember()) { 240 myRelation.setModified(true); 241 break; 242 } else if (! rm1.getMember().isNew() && rm1.getMember().getId() != rm2.getMember().getId()) { 243 myRelation.setModified(true); 244 break; 245 } 246 } 247 } 248 myRelation.setMembers(newMembers); 249 } 250 251 /** 252 * Tries to merge a primitive <code>other</code> into an existing primitive with the same id. 253 * 254 * @param other the other primitive which is to be merged onto a primitive in my primitives 255 * @return true, if this method was able to merge <code>other</code> with an existing node; false, otherwise 256 */ 257 private <P extends OsmPrimitive> boolean mergeById(P other) { 258 OsmPrimitive my = myDataSet.getPrimitiveById(other.getId(), other.getType()); 228 } else { 229 target.setModified(true); 230 } 231 } 232 target.setMembers(newMembers); 233 } 234 235 /** 236 * Tries to merge a primitive <code>source</code> into an existing primitive with the same id. 237 * 238 * @param source the other primitive which is to be merged onto a primitive in my primitives 239 * @return true, if this method was able to merge <code>source</code> into a target object; false, otherwise 240 */ 241 private boolean mergeById(OsmPrimitive source) { 242 OsmPrimitive target = targetDataSet.getPrimitiveById(source.getId(), source.getType()); 259 243 // merge other into an existing primitive with the same id, if possible 260 244 // 261 if ( my== null)245 if (target == null) 262 246 return false; 263 mergedMap.put(other.getUniqueId(), my.getUniqueId()); 264 if (my.getVersion() > other.getVersion()) 265 // my.version > other.version => keep my version 247 // found a corresponding target, remember it 248 mergedMap.put(source.getUniqueId(), target.getUniqueId()); 249 250 if (target.getVersion() > source.getVersion()) 251 // target.version > source.version => keep target version 266 252 return true; 267 if (! my.isVisible() && other.isVisible()) {253 if (! target.isVisible() && source.isVisible()) { 268 254 // should not happen 269 255 // 270 logger.warning(tr(" My primitivewith id {0} and version {1} is visible although "271 + " their primitivewith lower version {2} is not visible. "272 + "Can' t deal with this inconsistency. Keeping my primitive. ",273 Long.toString( my.getId()),Long.toString(my.getVersion()), Long.toString(other.getVersion())256 logger.warning(tr("Target object with id {0} and version {1} is visible although " 257 + "source object with lower version {2} is not visible. " 258 + "Can''t deal with this inconsistency. Keeping target object. ", 259 Long.toString(target.getId()),Long.toString(target.getVersion()), Long.toString(source.getVersion()) 274 260 )); 275 } else if ( my.isVisible() && ! other.isVisible()) {261 } else if (target.isVisible() && ! source.isVisible()) { 276 262 // this is always a conflict because the user has to decide whether 277 // he wants to create a clone of its localprimitive or whether he278 // wants to purge myfrom the local dataset. He can't keep it unchanged263 // he wants to create a clone of its target primitive or whether he 264 // wants to purge the target from the local dataset. He can't keep it unchanged 279 265 // because it was deleted on the server. 280 266 // 281 conflicts.add( my,other);282 } else if ( my.incomplete && !other.incomplete) {283 // my is incomplete, othercompletes it284 // => merge other onto my285 // 286 my.mergeFrom(other);287 fixReferences.add(other.getUniqueId());288 } else if (! my.incomplete && other.incomplete) {289 // my is complete and the otheris incomplete290 // => keep mine, we havemore information already291 // 292 } else if ( my.incomplete && other.incomplete) {293 // my and otherare incomplete. Doesn't matter which one to294 // take. We take mine.295 // 296 } else if ( my.isDeleted() && ! other.isDeleted() && my.getVersion() == other.getVersion()) {297 // same version, but my is deleted. Assume minetakes precedence267 conflicts.add(target,source); 268 } else if (target.incomplete && !source.incomplete) { 269 // target is incomplete, source completes it 270 // => merge source into target 271 // 272 target.mergeFrom(source); 273 childrenToMerge.add(source.getUniqueId()); 274 } else if (!target.incomplete && source.incomplete) { 275 // target is complete and source is incomplete 276 // => keep target, it has more information already 277 // 278 } else if (target.incomplete && source.incomplete) { 279 // target and source are incomplete. Doesn't matter which one to 280 // take. We take target. 281 // 282 } else if (target.isDeleted() && ! source.isDeleted() && target.getVersion() == source.getVersion()) { 283 // same version, but target is deleted. Assume target takes precedence 298 284 // otherwise too many conflicts when refreshing from the server 299 } else if ( my.isDeleted() != other.isDeleted()) {285 } else if (target.isDeleted() != source.isDeleted()) { 300 286 // differences in deleted state have to be resolved manually 301 287 // 302 conflicts.add( my,other);303 } else if (! my.isModified() && other.isModified()) {304 // my not modified. We can assume that otheris the most recent version.305 // clone it onto my. But check first, whether otheris deleted. if so,306 // make sure that my is not referencesanymore in myDataSet.307 // 308 if ( other.isDeleted()) {309 myDataSet.unlinkReferencesToPrimitive(my);310 } 311 my.mergeFrom(other);312 fixReferences.add(other.getUniqueId());313 } else if (! my.isModified() && !other.isModified() && my.getVersion() == other.getVersion()) {288 conflicts.add(target,source); 289 } else if (! target.isModified() && source.isModified()) { 290 // target not modified. We can assume that source is the most recent version. 291 // clone it into target. But check first, whether source is deleted. if so, 292 // make sure that target is not referenced anymore in myDataSet. 293 // 294 if (source.isDeleted()) { 295 deletedObjectsToUnlink.add(source); 296 } 297 target.mergeFrom(source); 298 childrenToMerge.add(source.getUniqueId()); 299 } else if (! target.isModified() && !source.isModified() && target.getVersion() == source.getVersion()) { 314 300 // both not modified. Keep mine 315 301 // 316 } else if (! my.isModified() && !other.isModified() && my.getVersion() < other.getVersion()) {302 } else if (! target.isModified() && !source.isModified() && target.getVersion() < source.getVersion()) { 317 303 // my not modified but other is newer. clone other onto mine. 318 304 // 319 my.mergeFrom(other);320 fixReferences.add(other.getUniqueId());321 } else if ( my.isModified() && ! other.isModified() && my.getVersion() == other.getVersion()) {305 target.mergeFrom(source); 306 childrenToMerge.add(source.getUniqueId()); 307 } else if (target.isModified() && ! source.isModified() && target.getVersion() == source.getVersion()) { 322 308 // my is same as other but mine is modified 323 309 // => keep mine 324 } else if (! my.hasEqualSemanticAttributes(other)) {310 } else if (! target.hasEqualSemanticAttributes(source)) { 325 311 // my is modified and is not semantically equal with other. Can't automatically 326 312 // resolve the differences 327 313 // => create a conflict 328 conflicts.add( my,other);314 conflicts.add(target,source); 329 315 } else { 330 316 // clone from other, but keep the modified flag. mergeFrom will mainly copy … … 332 318 // attributes should already be equal if we get here. 333 319 // 334 my.mergeFrom(other);335 my.setModified(true);336 fixReferences.add(other.getUniqueId());320 target.mergeFrom(source); 321 target.setModified(true); 322 childrenToMerge.add(source.getUniqueId()); 337 323 } 338 324 return true; … … 346 332 */ 347 333 public void merge() { 348 if ( theirDataSet == null)334 if (sourceDataSet == null) 349 335 return; 350 for (Node node: theirDataSet.getNodes()) {336 for (Node node: sourceDataSet.getNodes()) { 351 337 mergePrimitive(node); 352 338 } 353 for (Way way: theirDataSet.getWays()) {339 for (Way way: sourceDataSet.getWays()) { 354 340 mergePrimitive(way); 355 341 } 356 for (Relation relation: theirDataSet.getRelations()) {342 for (Relation relation: sourceDataSet.getRelations()) { 357 343 mergePrimitive(relation); 358 344 } … … 365 351 * @return 366 352 */ 367 public DataSet get MyDataSet() {368 return myDataSet;353 public DataSet getTargetDataSet() { 354 return targetDataSet; 369 355 } 370 356 -
trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java
r2433 r2443 269 269 DataSetMerger visitor = new DataSetMerger(ret,ds); 270 270 visitor.merge(); 271 ret = visitor.get MyDataSet();271 ret = visitor.getTargetDataSet(); 272 272 } 273 273 DataSet ds = getReferringRelations(progressMonitor.createSubTaskMonitor(1, false)); 274 274 DataSetMerger visitor = new DataSetMerger(ret,ds); 275 275 visitor.merge(); 276 ret = visitor.get MyDataSet();276 ret = visitor.getTargetDataSet(); 277 277 readIncompletePrimitives(ret, progressMonitor.createSubTaskMonitor(1, false)); 278 278 return ret;
Note:
See TracChangeset
for help on using the changeset viewer.