- Timestamp:
- 2009-11-09T01:03:08+01:00 (15 years ago)
- Location:
- trunk
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/Node.java
r2410 r2417 106 106 } 107 107 108 /** 109 * Merges the technical and semantical attributes from <code>other</code> onto this. 110 * 111 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code> 112 * have an assigend OSM id, the IDs have to be the same. 113 * 114 * @param other the other primitive. Must not be null. 115 * @throws IllegalArgumentException thrown if other is null. 116 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not 117 * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId() 118 */ 119 @Override 120 public void mergeFrom(OsmPrimitive other) { 121 super.mergeFrom(other); 122 if (!other.incomplete) { 123 setCoor(new LatLon(((Node)other).coor)); 124 } 125 } 126 108 127 @Override public void load(PrimitiveData data) { 109 128 super.load(data); -
trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
r2415 r2417 789 789 clearCached(); 790 790 clearErrors(); 791 } 792 793 /** 794 * Merges the technical and semantical attributes from <code>other</code> onto this. 795 * 796 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code> 797 * have an assigend OSM id, the IDs have to be the same. 798 * 799 * @param other the other primitive. Must not be null. 800 * @throws IllegalArgumentException thrown if other is null. 801 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not 802 * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId() 803 */ 804 public void mergeFrom(OsmPrimitive other) { 805 if (other == null) 806 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "other")); 807 if (other.isNew() ^ isNew()) 808 throw new DataIntegrityProblemException(tr("Can''t merge because either of the participating primitives is new and the other is not")); 809 if (! other.isNew() && other.getId() != id) 810 throw new DataIntegrityProblemException(tr("Can''t merge primitives with different ids. This id is {0}, the other is {1}", id, other.getId())); 811 keys = other.keys == null ? null : new HashMap<String, String>(other.keys); 812 timestamp = other.timestamp; 813 version = other.version; 814 incomplete = other.incomplete; 815 flags = other.flags; 816 user= other.user; 791 817 } 792 818 -
trunk/src/org/openstreetmap/josm/data/osm/Way.java
r2410 r2417 229 229 return false; 230 230 Way w = (Way)other; 231 return Arrays.equals(nodes, w.nodes); 231 if (getNodesCount() != w.getNodesCount()) return false; 232 for (int i=0;i<getNodesCount();i++) { 233 if (! getNode(i).hasEqualSemanticAttributes(w.getNode(i))) 234 return false; 235 } 236 return true; 232 237 } 233 238 -
trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java
r2406 r2417 26 26 private static Logger logger = Logger.getLogger(MergeVisitor.class.getName()); 27 27 28 /** 29 * Map from primitives in the database to visited primitives. (Attention: The other way 30 * round than merged) 31 */ 28 /** the collection of conflicts created during merging */ 32 29 private ConflictCollection conflicts; 33 30 34 31 /** the target dataset for merging */ 35 32 private final DataSet myDataSet; 33 /** the source dataset where primitives are merged from */ 36 34 private final DataSet theirDataSet; 37 35 38 private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>(); 39 private final HashMap<Long, Way> wayshash = new HashMap<Long, Way>(); 40 private final HashMap<Long, Relation> relshash = new HashMap<Long, Relation>(); 41 42 /** 43 * A list of all primitives that got replaced with other primitives. 44 * Key is the primitives in the other's dataset and the value is the one that is now 45 * in ds.nodes instead. 46 */ 47 private Map<OsmPrimitive, OsmPrimitive> merged; 36 /** 37 * A map of all primitives that got replaced with other primitives. 38 * Key is the primitive id in their dataset, the value is the id in my dataset 39 */ 40 private Map<Long, Long> merged; 48 41 49 42 /** … … 58 51 this.myDataSet = myDataSet; 59 52 this.theirDataSet = theirDataSet; 60 61 for (Node n : myDataSet.getNodes()) {62 if (!n.isNew()) {63 nodeshash.put(n.getId(), n);64 }65 }66 for (Way w : myDataSet.getWays()) {67 if (!w.isNew()) {68 wayshash.put(w.getId(), w);69 }70 }71 for (Relation r : myDataSet.getRelations()) {72 if (!r.isNew()) {73 relshash.put(r.getId(), r);74 }75 }76 53 conflicts = new ConflictCollection(); 77 merged = new HashMap< OsmPrimitive, OsmPrimitive>();54 merged = new HashMap<Long, Long>(); 78 55 } 79 56 … … 91 68 * @param <P> the type of the other primitive 92 69 * @param other the other primitive 93 * @param myPrimitives the collection of my relevant primitives (i.e. only my 94 * primitives of the same type) 95 * @param otherPrimitives the collection of the other primitives 96 * @param primitivesWithDefinedIds the collection of my primitives with an 97 * assigned id (i.e. id != 0) 98 */ 99 protected <P extends OsmPrimitive> void mergePrimitive(P other, 100 DataSet myDataset, Collection<P> myPrimitives, HashMap<Long, P> primitivesWithDefinedIds) { 101 70 */ 71 protected <P extends OsmPrimitive> void mergePrimitive(P other) { 102 72 if (!other.isNew() ) { 103 73 // try to merge onto a matching primitive with the same 104 74 // defined id 105 75 // 106 if (mergeById( primitivesWithDefinedIds,other))76 if (mergeById(other)) 107 77 return; 108 78 } else { … … 110 80 // yet but which is equal in its semantic attributes 111 81 // 112 for (P my : myPrimitives) { 82 Collection<? extends OsmPrimitive> candidates = null; 83 switch(other.getType()) { 84 case NODE: candidates = myDataSet.getNodes(); break; 85 case WAY: candidates =myDataSet.getWays(); break; 86 case RELATION: candidates = myDataSet.getRelations(); break; 87 } 88 for (OsmPrimitive my : candidates) { 113 89 if (!my.isNew()) { 114 90 continue; … … 119 95 // 120 96 conflicts.add(my, other); 97 merged.put(other.getUniqueId(), my.getUniqueId()); 121 98 } else { 122 99 // copy the technical attributes from other … … 126 103 my.setTimestamp(other.getTimestamp()); 127 104 my.setModified(other.isModified()); 128 merged.put(other , my);105 merged.put(other.getUniqueId(), my.getUniqueId()); 129 106 } 130 107 return; … … 133 110 } 134 111 // If we get here we didn't find a suitable primitive in 135 // my dataset. Just add otherto my dataset.112 // my dataset. Create a clone and add it to my dataset. 136 113 // 137 138 //TODO primitive must be only in one dataset at one time, so remove it from theirDataset. This obviously 139 // breaks theirDataset and we can only hope that nobody will try to use theirDataset after merging is finished 140 theirDataSet.removePrimitive(other); 141 myDataset.addPrimitive(other); 114 OsmPrimitive my = null; 115 switch(other.getType()) { 116 case NODE: my = other.isNew() ? new Node() : new Node(other.getId()); break; 117 case WAY: my = other.isNew() ? new Way() : new Way(other.getId()); break; 118 case RELATION: my = other.isNew() ? new Relation() : new Relation(other.getId()); break; 119 } 120 my.mergeFrom(other); 121 myDataSet.addPrimitive(my); 122 merged.put(other.getUniqueId(), my.getUniqueId()); 142 123 } 143 124 144 125 public void visit(Node other) { 145 mergePrimitive(other , myDataSet, myDataSet.getNodes(), nodeshash);126 mergePrimitive(other); 146 127 } 147 128 148 129 public void visit(Way other) { 149 fixWay(other); 150 mergePrimitive(other, myDataSet, myDataSet.getWays(), wayshash); 130 mergePrimitive(other); 151 131 } 152 132 153 133 public void visit(Relation other) { 154 fixRelation(other); 155 mergePrimitive(other, myDataSet, myDataSet.getRelations(), relshash); 156 } 157 158 protected void fixIncomplete(Way w) { 159 if (!w.incomplete)return; 160 if (w.incomplete && w.getNodesCount() == 0) return; 161 for (Node n: w.getNodes()) { 134 mergePrimitive(other); 135 } 136 137 protected OsmPrimitive getMergeTarget(OsmPrimitive mergeSource) { 138 Long targetId = merged.get(mergeSource.getUniqueId()); 139 if (targetId == null) 140 throw new RuntimeException("no merge target for merge primitive " + mergeSource.getUniqueId() + " of type " + mergeSource.getType()); 141 return myDataSet.getPrimitiveById(targetId, mergeSource.getType()); 142 } 143 144 protected void fixIncomplete(Way other) { 145 Way myWay = (Way)getMergeTarget(other); 146 if (myWay == null) 147 throw new RuntimeException(tr("Missing merge target for way with id {0}", other.getUniqueId())); 148 if (!myWay.incomplete)return; 149 if (myWay.incomplete && other.getNodesCount() == 0) return; 150 for (Node n: myWay.getNodes()) { 162 151 if (n.incomplete) return; 163 152 } 164 w.incomplete = false;153 myWay.incomplete = false; 165 154 } 166 155 … … 170 159 */ 171 160 public void fixReferences() { 172 for (Way w : myDataSet.getWays()) { 173 fixWay(w); 174 fixIncomplete(w); 175 } 176 for (Relation r : myDataSet.getRelations()) { 177 fixRelation(r); 178 } 179 for (OsmPrimitive osm : conflicts.getMyConflictParties()) 180 if (osm instanceof Way) { 181 fixWay((Way) osm); 182 } else if (osm instanceof Relation) { 183 fixRelation((Relation) osm); 184 } 185 } 186 187 188 private void fixWay(Way w) { 189 boolean replacedSomething = false; 190 List<Node> newNodes = new LinkedList<Node>(); 191 for (Node myNode : w.getNodes()) { 192 Node mergedNode = (Node) merged.get(myNode); 193 if (mergedNode != null) { 194 if (!mergedNode.isDeleted()) { 195 newNodes.add(mergedNode); 196 } else { 197 // we've removed a node from a way during merging. 198 // Flag the way as being modified. 199 // 200 w.setModified(true); 201 } 202 replacedSomething = true; 203 } else { 204 newNodes.add(myNode); 205 } 206 } 207 if (replacedSomething) { 208 w.setNodes(newNodes); 209 } 210 } 211 212 private void fixRelation(Relation r) { 213 boolean replacedSomething = false; 161 for (Way w : theirDataSet.getWays()) { 162 if (!conflicts.hasConflictForTheir(w)) { 163 mergeNodeList(w); 164 fixIncomplete(w); 165 } 166 } 167 for (Relation r : theirDataSet.getRelations()) { 168 if (!conflicts.hasConflictForTheir(r)) { 169 mergeRelationMembers(r); 170 } 171 } 172 } 173 174 private void mergeNodeList(Way other) { 175 Way myWay = (Way)getMergeTarget(other); 176 if (myWay == null) 177 throw new RuntimeException(tr("Missing merge target for way with id {0}", other.getUniqueId())); 178 179 List<Node> myNodes = new LinkedList<Node>(); 180 for (Node otherNode : other.getNodes()) { 181 Node myNode = (Node)getMergeTarget(otherNode); 182 if (myNode != null) { 183 if (!myNode.isDeleted()) { 184 myNodes.add(myNode); 185 } 186 } else 187 throw new RuntimeException(tr("Missing merge target for node with id {0}", otherNode.getUniqueId())); 188 } 189 190 // check whether the node list has changed. If so, set the modified flag on the way 191 // 192 if (myWay.getNodes().size() != myNodes.size()) { 193 myWay.setModified(true); 194 } else { 195 for (int i=0; i< myWay.getNodesCount();i++) { 196 Node n1 = myWay.getNode(i); 197 Node n2 = myNodes.get(i); 198 if (n1.isNew() ^ n2.isNew()) { 199 myWay.setModified(true); 200 break; 201 } else if (n1.isNew() && n1 != n2) { 202 myWay.setModified(true); 203 break; 204 } else if (! n1.isNew() && n1.getId() != n2.getId()) { 205 myWay.setModified(true); 206 break; 207 } 208 } 209 } 210 myWay.setNodes(myNodes); 211 } 212 213 private void mergeRelationMembers(Relation other) { 214 Relation myRelation = (Relation) getMergeTarget(other); 215 if (myRelation == null) 216 throw new RuntimeException(tr("Missing merge target for relation with id {0}", other.getUniqueId())); 214 217 LinkedList<RelationMember> newMembers = new LinkedList<RelationMember>(); 215 for (RelationMember myMember : r.getMembers()) { 216 OsmPrimitive mergedMember = merged.get(myMember.getMember()); 217 if (mergedMember == null) { 218 newMembers.add(myMember); 219 } else { 220 if (! mergedMember.isDeleted()) { 221 RelationMember newMember = new RelationMember(myMember.getRole(), mergedMember); 222 newMembers.add(newMember); 223 } 224 replacedSomething = true; 225 } 226 } 227 if (replacedSomething) { 228 r.setMembers(newMembers); 229 } 218 for (RelationMember otherMember : other.getMembers()) { 219 OsmPrimitive mergedMember = getMergeTarget(otherMember.getMember()); 220 if (mergedMember == null) 221 throw new RuntimeException(tr("Missing merge target of type {0} with id {1}", mergedMember.getType(), mergedMember.getUniqueId())); 222 if (! mergedMember.isDeleted()) { 223 RelationMember newMember = new RelationMember(otherMember.getRole(), mergedMember); 224 newMembers.add(newMember); 225 } 226 } 227 228 // check whether the list of relation members has changed 229 // 230 if (other.getMembersCount() != newMembers.size()) { 231 myRelation.setModified(true); 232 } else { 233 for (int i=0; i<other.getMembersCount();i++) { 234 RelationMember rm1 = other.getMember(i); 235 RelationMember rm2 = newMembers.get(i); 236 if (!rm1.getRole().equals(rm2.getRole())) { 237 myRelation.setModified(true); 238 break; 239 } else if (rm1.getMember().isNew() ^ rm2.getMember().isNew()) { 240 myRelation.setModified(true); 241 break; 242 } else if (rm1.getMember().isNew() && rm1.getMember() != rm2.getMember()) { 243 myRelation.setModified(true); 244 break; 245 } else if (! rm1.getMember().isNew() && rm1.getMember().getId() != rm2.getMember().getId()) { 246 myRelation.setModified(true); 247 break; 248 } 249 } 250 } 251 myRelation.setMembers(newMembers); 230 252 } 231 253 … … 233 255 * Tries to merge a primitive <code>other</code> into an existing primitive with the same id. 234 256 * 235 * @param myPrimitivesWithDefinedIds the map of primitives (potential merge targets) with an id <> 0, for faster lookup236 * by id. Key is the id, value the primitive with the given value. myPrimitives.valueSet() is a237 * subset of primitives.238 257 * @param other the other primitive which is to be merged onto a primitive in my primitives 239 258 * @return true, if this method was able to merge <code>other</code> with an existing node; false, otherwise 240 259 */ 241 private <P extends OsmPrimitive> boolean mergeById( HashMap<Long, P> myPrimitivesWithDefinedIds,P other) {242 260 private <P extends OsmPrimitive> boolean mergeById(P other) { 261 OsmPrimitive my = myDataSet.getPrimitiveById(other.getId(), other.getType()); 243 262 // merge other into an existing primitive with the same id, if possible 244 263 // 245 if (myPrimitivesWithDefinedIds.containsKey(other.getId())) { 246 P my = myPrimitivesWithDefinedIds.get(other.getId()); 264 if (my != null) { 247 265 if (my.getVersion() <= other.getVersion()) { 248 266 if (! my.isVisible() && other.isVisible()) { … … 254 272 Long.toString(my.getId()),Long.toString(my.getVersion()), Long.toString(other.getVersion()) 255 273 )); 256 merged.put(other , my);274 merged.put(other.getUniqueId(), my.getUniqueId()); 257 275 } else if (my.isVisible() && ! other.isVisible()) { 258 276 // this is always a conflict because the user has to decide whether … … 262 280 // 263 281 conflicts.add(my,other); 282 merged.put(other.getUniqueId(), my.getUniqueId()); 264 283 } else if (my.incomplete && !other.incomplete) { 265 284 // my is incomplete, other completes it 266 285 // => merge other onto my 267 286 // 268 my.incomplete = false; 269 my.cloneFrom(other); 270 merged.put(other, my); 287 my.mergeFrom(other); 288 merged.put(other.getUniqueId(), my.getUniqueId()); 271 289 } else if (!my.incomplete && other.incomplete) { 272 290 // my is complete and the other is incomplete 273 291 // => keep mine, we have more information already 274 292 // 275 merged.put(other , my);293 merged.put(other.getUniqueId(), my.getUniqueId()); 276 294 } else if (my.incomplete && other.incomplete) { 277 295 // my and other are incomplete. Doesn't matter which one to 278 296 // take. We take mine. 279 297 // 280 merged.put(other , my);298 merged.put(other.getUniqueId(), my.getUniqueId()); 281 299 } else if (my.isDeleted() && ! other.isDeleted() && my.getVersion() == other.getVersion()) { 282 300 // same version, but my is deleted. Assume mine takes precedence 283 301 // otherwise too many conflicts when refreshing from the server 284 merged.put(other , my);302 merged.put(other.getUniqueId(), my.getUniqueId()); 285 303 } else if (my.isDeleted() != other.isDeleted()) { 286 304 // differences in deleted state have to be resolved manually 287 305 // 288 306 conflicts.add(my,other); 307 merged.put(other.getUniqueId(), my.getUniqueId()); 289 308 } else if (! my.isModified() && other.isModified()) { 290 309 // my not modified. We can assume that other is the most recent version. … … 295 314 myDataSet.unlinkReferencesToPrimitive(my); 296 315 } 297 my. cloneFrom(other);298 merged.put(other , my);316 my.mergeFrom(other); 317 merged.put(other.getUniqueId(), my.getUniqueId()); 299 318 } else if (! my.isModified() && !other.isModified() && my.getVersion() == other.getVersion()) { 300 319 // both not modified. Keep mine 301 320 // 302 merged.put(other ,my);321 merged.put(other.getUniqueId(),my.getUniqueId()); 303 322 } else if (! my.isModified() && !other.isModified() && my.getVersion() < other.getVersion()) { 304 323 // my not modified but other is newer. clone other onto mine. 305 324 // 306 my. cloneFrom(other);307 merged.put(other ,my);325 my.mergeFrom(other); 326 merged.put(other.getUniqueId(),my.getUniqueId()); 308 327 } else if (my.isModified() && ! other.isModified() && my.getVersion() == other.getVersion()) { 309 328 // my is same as other but mine is modified 310 329 // => keep mine 311 merged.put(other , my);330 merged.put(other.getUniqueId(), my.getUniqueId()); 312 331 } else if (! my.hasEqualSemanticAttributes(other)) { 313 332 // my is modified and is not semantically equal with other. Can't automatically … … 315 334 // => create a conflict 316 335 conflicts.add(my,other); 336 merged.put(other.getUniqueId(), my.getUniqueId()); 317 337 } else { 318 338 // clone from other, but keep the modified flag. Clone will mainly copy … … 320 340 // attributes should already be equal if we get here. 321 341 // 322 my. cloneFrom(other);342 my.mergeFrom(other); 323 343 my.setModified(true); 324 merged.put(other , my);344 merged.put(other.getUniqueId(), my.getUniqueId()); 325 345 } 326 346 } else { 327 347 // my.version > other.version => keep my version 328 merged.put(other , my);348 merged.put(other.getUniqueId(), my.getUniqueId()); 329 349 } 330 350 return true; … … 341 361 */ 342 362 public void merge() { 343 for (final OsmPrimitive primitive : theirDataSet.allPrimitives()) { 344 primitive.visit(this); 363 for (Node node: theirDataSet.getNodes()) { 364 node.visit(this); 365 } 366 for (Way way: theirDataSet.getWays()) { 367 way.visit(this); 368 } 369 for (Relation relation: theirDataSet.getRelations()) { 370 relation.visit(this); 345 371 } 346 372 fixReferences(); … … 356 382 } 357 383 358 359 384 /** 360 385 * replies the map of conflicts -
trunk/test/unit/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java
r2396 r2417 553 553 their.addPrimitive(n5); 554 554 555 their.addPrimitive(n5);556 557 555 Node n4 = new Node(new LatLon(2,2)); 558 556 n4.setOsmId(2,2); … … 586 584 * their way has a higher version and different nodes. My way is modified. 587 585 * 588 * => merge it onto my way not possbile,conflict586 * => merge onto my way not possible, create a conflict 589 587 * 590 588 */ … … 621 619 Node n5 = new Node(new LatLon(1,1)); 622 620 n5.setOsmId(4,1); 623 their.addPrimitive(n5);624 625 621 their.addPrimitive(n5); 626 622 … … 788 784 theirWay.addNode(n3); 789 785 theirWay.addNode(n4); 790 theirWay.setUser(User.createOsmUser(1111, "their")); 786 User user = User.createOsmUser(1111, "their"); 787 theirWay.setUser(user); 791 788 theirWay.setTimestamp(new Date()); 792 789 their.addPrimitive(theirWay); … … 797 794 assertEquals(0,visitor.getConflicts().size()); 798 795 assertEquals("their", myWay.getUser().getName()); 799 assertEquals(1111, myWay.getUser().getId());800 796 assertEquals(1111, myWay.getUser().getId()); 801 797 assertEquals(theirWay.getTimestamp(), myWay.getTimestamp());
Note:
See TracChangeset
for help on using the changeset viewer.