Changeset 2198 in josm
- Timestamp:
- 2009-09-27T16:29:21+02:00 (15 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 1 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/MergeSelectionAction.java
r2017 r2198 51 51 setEnabled(!getEditLayer().data.getSelected().isEmpty()); 52 52 } 53 54 55 56 53 } -
trunk/src/org/openstreetmap/josm/actions/UploadAction.java
r2189 r2198 11 11 import java.util.Collection; 12 12 import java.util.Date; 13 import java.util.HashSet; 13 14 import java.util.LinkedList; 14 15 import java.util.logging.Logger; … … 29 30 import org.openstreetmap.josm.data.osm.OsmPrimitive; 30 31 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 32 import org.openstreetmap.josm.gui.DefaultNameFormatter; 31 33 import org.openstreetmap.josm.gui.ExceptionDialogUtil; 32 34 import org.openstreetmap.josm.gui.PleaseWaitRunnable; … … 37 39 import org.openstreetmap.josm.io.OsmApiException; 38 40 import org.openstreetmap.josm.io.OsmApiInitializationException; 41 import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException; 39 42 import org.openstreetmap.josm.io.OsmChangesetCloseException; 40 43 import org.openstreetmap.josm.io.OsmServerWriter; 44 import org.openstreetmap.josm.io.OsmTransferException; 41 45 import org.openstreetmap.josm.tools.DateUtils; 42 46 import org.openstreetmap.josm.tools.Shortcut; … … 350 354 * @see UpdateSelectionAction#handlePrimitiveGoneException(long) 351 355 */ 352 protected void handleGoneForKnownPrimitive(OsmPrimitiveType primitiveType, String id) {356 protected void handleGoneForKnownPrimitive(OsmPrimitiveType primitiveType, long id) { 353 357 UpdateSelectionAction act = new UpdateSelectionAction(); 354 act.handlePrimitiveGoneException( Long.parseLong(id),primitiveType);358 act.handlePrimitiveGoneException(id,primitiveType); 355 359 } 356 360 … … 363 367 * @param e the exception 364 368 */ 365 protected void handleGone(OsmApiException e) { 366 String pattern = "The (\\S+) with the id (\\d+) has already been deleted"; 367 Pattern p = Pattern.compile(pattern); 368 Matcher m = p.matcher(e.getErrorHeader()); 369 if (m.matches()) { 370 handleGoneForKnownPrimitive(OsmPrimitiveType.from(m.group(1)), m.group(2)); 369 protected void handleGone(OsmApiPrimitiveGoneException e) { 370 if (e.isKnownPrimitive()) { 371 handleGoneForKnownPrimitive(e.getPrimitiveType(), e.getPrimitiveId()); 371 372 } else { 372 logger.warning(tr("Error header \"{0}\" does not match expected pattern \"{1}\"",e.getErrorHeader(), pattern));373 373 ExceptionDialogUtil.explainGoneForUnknownPrimitive(e); 374 374 } … … 391 391 if (e instanceof OsmChangesetCloseException) { 392 392 ExceptionDialogUtil.explainOsmChangesetCloseException((OsmChangesetCloseException)e); 393 return; 394 } 395 if (e instanceof OsmApiPrimitiveGoneException) { 396 handleGone((OsmApiPrimitiveGoneException)e); 393 397 return; 394 398 } … … 406 410 else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) { 407 411 ExceptionDialogUtil.explainPreconditionFailed(ex); 408 return;409 }410 // Tried to delete an already deleted primitive? Let the user411 // decide whether and how to resolve this conflict.412 //413 else if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) {414 handleGone(ex);415 412 return; 416 413 } … … 489 486 } 490 487 491 public Upload DiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {492 return new Upload DiffTask(layer, toUpload, changeset, closeChangesetAfterUpload);488 public UploadPrimitivesTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) { 489 return new UploadPrimitivesTask(layer, toUpload, changeset, closeChangesetAfterUpload); 493 490 } 494 491 … … 497 494 * 498 495 */ 499 public class Upload DiffTask extends PleaseWaitRunnable {496 public class UploadPrimitivesTask extends PleaseWaitRunnable { 500 497 private boolean uploadCancelled = false; 501 498 private Exception lastException = null; … … 505 502 private Changeset changeset; 506 503 private boolean closeChangesetAfterUpload; 504 private HashSet<OsmPrimitive> processedPrimitives; 507 505 508 506 /** … … 513 511 * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading 514 512 */ 515 private Upload DiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {513 private UploadPrimitivesTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) { 516 514 super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */); 517 515 this.toUpload = toUpload; … … 519 517 this.changeset = changeset; 520 518 this.closeChangesetAfterUpload = closeChangesetAfterUpload; 519 this.processedPrimitives = new HashSet<OsmPrimitive>(); 520 } 521 522 protected OsmPrimitive getPrimitive(OsmPrimitiveType type, long id) { 523 for (OsmPrimitive p: toUpload) { 524 if (OsmPrimitiveType.from(p).equals(type) && p.getId() == id) 525 return p; 526 } 527 return null; 528 } 529 530 /** 531 * Retries to recover the upload operation from an exception which was thrown because 532 * an uploaded primitive was already deleted on the server. 533 * 534 * @param e the exception throw by the API 535 * @param monitor a progress monitor 536 * @throws OsmTransferException thrown if we can't recover from the exception 537 */ 538 protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{ 539 if (!e.isKnownPrimitive()) throw e; 540 OsmPrimitive p = getPrimitive(e.getPrimitiveType(), e.getPrimitiveId()); 541 if (p == null) throw e; 542 if (p.isDeleted()) { 543 // we tried to delete an already deleted primitive. 544 // 545 System.out.println(tr("Warning: primitive ''{0}'' is already deleted on the server. Skipping this primitive and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance()))); 546 processedPrimitives.addAll(writer.getProcessedPrimitives()); 547 processedPrimitives.add(p); 548 toUpload.removeAll(processedPrimitives); 549 return; 550 } 551 // exception was thrown because we tried to *update* an already deleted 552 // primitive. We can't resolve this automatically. Re-throw exception, 553 // a conflict is going to be created later. 554 throw e; 521 555 } 522 556 … … 524 558 writer = new OsmServerWriter(); 525 559 try { 526 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 527 writer.uploadOsm(layer.data.version, toUpload, changeset,closeChangesetAfterUpload, monitor); 528 } catch (Exception sxe) { 560 // 561 while(true) { 562 try { 563 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 564 writer.uploadOsm(layer.data.version, toUpload, changeset, monitor); 565 processedPrimitives.addAll(writer.getProcessedPrimitives()); 566 // if we get here we've successfully uploaded the data. We 567 // can exit the loop. 568 // 569 break; 570 } catch(OsmApiPrimitiveGoneException e) { 571 // try to recover from the 410 Gone 572 recoverFromGoneOnServer(e, getProgressMonitor()); 573 } 574 } 575 // if required close the changeset 576 // 577 if (closeChangesetAfterUpload) { 578 if (changeset != null && changeset.getId() > 0) { 579 OsmApi.getOsmApi().closeChangeset(changeset, progressMonitor.createSubTaskMonitor(0,false)); 580 } 581 } 582 } catch (Exception e) { 529 583 if (uploadCancelled) { 530 System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + sxe.toString());584 System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + e.toString()); 531 585 return; 532 586 } 533 lastException = sxe;587 lastException = e; 534 588 } 535 589 } … … 539 593 return; 540 594 541 // we always clean the data, even in case of errors. It's possible the data was595 // we always clean up the data, even in case of errors. It's possible the data was 542 596 // partially uploaded 543 597 // 544 layer.cleanupAfterUpload( writer.getProcessedPrimitives());598 layer.cleanupAfterUpload(processedPrimitives); 545 599 DataSet.fireSelectionChanged(layer.data.getSelected()); 546 600 layer.fireDataChange(); 547 601 if (lastException != null) { 548 602 handleFailedUpload(lastException); 549 } else { 550 // run post upload action on the layer 551 // 552 layer.onPostUploadToServer(); 553 // refresh changeset dialog with the updated changeset 554 // 555 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset); 556 } 603 } 604 layer.onPostUploadToServer(); 605 UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset); 557 606 } 558 607 … … 563 612 } 564 613 } 565 566 public boolean isSuccessful() {567 return !isCancelled() && !isFailed();568 }569 570 public boolean isCancelled() {571 return uploadCancelled;572 }573 574 public boolean isFailed() {575 return lastException != null;576 }577 614 } 578 615 } -
trunk/src/org/openstreetmap/josm/actions/upload/RelationUploadOrderHook.java
r2168 r2198 33 33 public class RelationUploadOrderHook implements UploadHook { 34 34 35 /** the data to be analy sed */35 /** the data to be analyzed */ 36 36 private APIDataSet data; 37 37 -
trunk/src/org/openstreetmap/josm/command/ConflictResolveCommand.java
r2163 r2198 12 12 13 13 /** 14 * This is the common bas se class for {@see Command}s which manipulate {@see Conflict}s in14 * This is the common base class for {@see Command}s which manipulate {@see Conflict}s in 15 15 * addition to {@see OsmPrimitive}s. 16 16 * 17 17 * A ConflictResolverCommand can remember a collection of conflicts it resolves. Upon undoing 18 * it reconstitutes the se conflicts.18 * it reconstitutes them. 19 19 * 20 20 */ … … 30 30 } 31 31 32 public ConflictResolveCommand(OsmDataLayer layer) { 33 super(layer); 34 resolvedConflicts = new ConflictCollection(); 35 } 36 32 37 /** 33 38 * remembers a conflict in the internal list of remembered conflicts … … 35 40 * @param c the remembered conflict 36 41 */ 37 protected void rememberConflict(Conflict c) {42 protected void rememberConflict(Conflict<?> c) { 38 43 if (! resolvedConflicts.hasConflictForMy(c.getMy())) { 39 44 resolvedConflicts.add(c); … … 48 53 protected void reconstituteConflicts() { 49 54 OsmDataLayer editLayer = getLayer(); 50 for(Conflict c : resolvedConflicts) {55 for(Conflict<?> c : resolvedConflicts) { 51 56 if (!editLayer.getConflicts().hasConflictForMy(c.getMy())) { 52 57 editLayer.getConflicts().add(c); … … 60 65 61 66 if (! Main.map.mapView.hasLayer(getLayer())) { 62 logger.warning(tr("Can' t undo command ''{0}'' because layer ''{1}'' is not present any more",67 logger.warning(tr("Can''t undo command ''{0}'' because layer ''{1}'' is not present any more", 63 68 this.toString(), 64 69 getLayer().toString() … … 70 75 reconstituteConflicts(); 71 76 } 72 73 74 75 77 } -
trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java
r2163 r2198 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.util.ArrayList;7 6 import java.util.Collection; 7 import java.util.Collections; 8 import java.util.HashSet; 8 9 import java.util.List; 10 import java.util.Set; 9 11 import java.util.logging.Logger; 10 12 … … 15 17 import org.openstreetmap.josm.Main; 16 18 import org.openstreetmap.josm.data.conflict.ConflictCollection; 17 import org.openstreetmap.josm.data.osm. DataSet;19 import org.openstreetmap.josm.data.osm.BackreferencedDataSet; 18 20 import org.openstreetmap.josm.data.osm.Node; 19 21 import org.openstreetmap.josm.data.osm.OsmPrimitive; 20 22 import org.openstreetmap.josm.data.osm.Relation; 21 import org.openstreetmap.josm.data.osm.RelationMember;22 23 import org.openstreetmap.josm.data.osm.Way; 24 import org.openstreetmap.josm.gui.DefaultNameFormatter; 25 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 23 26 import org.openstreetmap.josm.tools.ImageProvider; 24 27 … … 38 41 static private final Logger logger = Logger.getLogger(PurgePrimitivesCommand.class.getName()); 39 42 40 /** 41 * Represents a pair of {@see OsmPrimitive} where the parent referrs to 42 * the child, either because a {@see Way} includes a {@see Node} or 43 * because a {@see Relation} refers to any other {@see OsmPrimitive} 44 * via a relation member. 45 * 46 */ 47 static class OsmParentChildPair { 48 private OsmPrimitive parent; 49 private OsmPrimitive child; 50 51 52 public OsmParentChildPair(OsmPrimitive parent, OsmPrimitive child) { 53 this.parent = parent; 54 this.child = child; 55 } 56 57 public OsmPrimitive getParent() { 58 return parent; 59 } 60 61 public OsmPrimitive getChild() { 62 return child; 63 } 64 65 @Override 66 public int hashCode() { 67 final int prime = 31; 68 int result = 1; 69 result = prime * result + ((child == null) ? 0 : child.hashCode()); 70 result = prime * result + ((parent == null) ? 0 : parent.hashCode()); 71 return result; 72 } 73 74 @Override 75 public boolean equals(Object obj) { 76 if (this == obj) 77 return true; 78 if (obj == null) 79 return false; 80 if (getClass() != obj.getClass()) 81 return false; 82 OsmParentChildPair other = (OsmParentChildPair) obj; 83 if (child == null) { 84 if (other.child != null) 85 return false; 86 } else if (child != other.child) 87 return false; 88 if (parent == null) { 89 if (other.parent != null) 90 return false; 91 } else if (parent != other.parent) 92 return false; 93 return true; 94 } 95 } 96 97 /** 98 * creates a list of all {@see OsmParentChildPair}s for a given {@see OsmPrimitive} 99 * as child and given set of parents. We don't use {@see CollectBackReferencesVisitor} 100 * because it seems quite inefficient. 101 * 102 * @param parents the set of potential parents 103 * @param child the child 104 * @return the list of {@see OsmParentChildPair} 105 */ 106 protected List<OsmParentChildPair> getParentChildPairs(List<OsmPrimitive> parents, OsmPrimitive child) { 107 ArrayList<OsmParentChildPair> pairs = new ArrayList<OsmParentChildPair>(); 108 for (OsmPrimitive parent : parents) { 109 if (parent instanceof Way) { 110 Way w = (Way)parent; 111 for (OsmPrimitive node : w.getNodes()) { 112 if (node == child) { 113 OsmParentChildPair pair = new OsmParentChildPair(parent, node); 114 if (! pairs.contains(pair)) { 115 pairs.add(pair); 116 } 117 } 118 } 119 } else if (parent instanceof Relation) { 120 Relation r = (Relation)parent; 121 for (RelationMember member : r.getMembers()) { 122 if (member.getMember() == child) { 123 OsmParentChildPair pair = new OsmParentChildPair(parent, member.getMember()); 124 if (! pairs.contains(pair)) { 125 pairs.add(pair); 126 } 127 } 128 } 129 } 130 } 131 return pairs; 132 } 133 134 /** the primitive to purge */ 135 private OsmPrimitive primitive; 43 /** the primitives to purge */ 44 private Collection<OsmPrimitive> toPurge; 136 45 137 46 /** the set of primitives to purge as consequence of purging 138 47 * {@see #primitive}, including {@see #primitive} 139 48 */ 140 private ArrayList<OsmPrimitive> purgedPrimitives; 141 142 /** the set of {@see OsmParentChildPair}. We keep a reference 143 * to this set for the {@see #fillModifiedData(Collection, Collection, Collection)} operation 144 */ 145 private ArrayList<OsmParentChildPair> pairs; 146 49 private Set<OsmPrimitive> purgedPrimitives; 50 51 private Set<OsmPrimitive> origVersionsOfTouchedPrimitives; 52 53 /** 54 * the data structure with child->parent references 55 */ 56 private BackreferencedDataSet backreferenceDataSet; 57 58 protected void init(Collection<OsmPrimitive> toPurge) { 59 this.toPurge = toPurge; 60 this.purgedPrimitives = new HashSet<OsmPrimitive>(); 61 this.origVersionsOfTouchedPrimitives = new HashSet<OsmPrimitive>(); 62 } 147 63 148 64 /** 149 65 * constructor 150 * @param node the node to undelete 66 * @param primitive the primitive to purge 67 * 151 68 */ 152 69 public PurgePrimitivesCommand(OsmPrimitive primitive) { 153 this.primitive = primitive; 154 purgedPrimitives = new ArrayList<OsmPrimitive>(); 155 pairs = new ArrayList<OsmParentChildPair>(); 156 } 157 158 @Override 159 public MutableTreeNode description() { 70 init(Collections.singleton(primitive)); 71 } 72 73 /** 74 * constructor 75 * @param layer the OSM data layer 76 * @param primitive the primitive to purge 77 * 78 */ 79 public PurgePrimitivesCommand(OsmDataLayer layer, OsmPrimitive primitive) { 80 super(layer); 81 init(Collections.singleton(primitive)); 82 } 83 84 /** 85 * constructor 86 * @param layer the OSM data layer 87 * @param primitives the primitives to purge 88 * 89 */ 90 public PurgePrimitivesCommand(OsmDataLayer layer, Collection<OsmPrimitive> primitives) { 91 super(layer); 92 init(primitives); 93 } 94 95 /** 96 * Replies a collection with the purged primitives 97 * 98 * @return a collection with the purged primitives 99 */ 100 public Collection<OsmPrimitive> getPurgedPrimitives() { 101 return purgedPrimitives; 102 } 103 104 protected MutableTreeNode getDescription(OsmPrimitive primitive) { 160 105 return new DefaultMutableTreeNode( 161 106 new JLabel( 162 tr("Purg ing 1 primitive"),107 tr("Purged object ''{0}''", primitive.getDisplayName(DefaultNameFormatter.getInstance())), 163 108 ImageProvider.get("data", "object"), 164 109 JLabel.HORIZONTAL … … 167 112 } 168 113 169 /** 170 * Purges an {@see OsmPrimitive} <code>toPurge</code> from a {@see DataSet}. 114 protected MutableTreeNode getDescription(Collection<OsmPrimitive> primitives) { 115 116 DefaultMutableTreeNode root = new DefaultMutableTreeNode( 117 tr("Purged {0} objects", primitives.size()) 118 ); 119 for (OsmPrimitive p : primitives) { 120 root.add(getDescription(p)); 121 } 122 return root; 123 } 124 125 @Override 126 public MutableTreeNode description() { 127 if (purgedPrimitives.size() == 1) 128 return getDescription(purgedPrimitives.iterator().next()); 129 else 130 return getDescription(purgedPrimitives); 131 } 132 133 /** 134 * Purges an {@see OsmPrimitive} <code>child</code> from a {@see DataSet}. 171 135 * 172 * @param toPurge the primitive to purge 173 * @param ds the dataset to purge from 136 * @param child the primitive to purge 174 137 * @param hive the hive of {@see OsmPrimitive}s we remember other {@see OsmPrimitive} 175 * we have to purge because we purge <code> toPurge</code>.138 * we have to purge because we purge <code>child</code>. 176 139 * 177 140 */ 178 protected void purge(OsmPrimitive toPurge, DataSet ds, ArrayList<OsmPrimitive> hive) { 179 ArrayList<OsmPrimitive> parents = new ArrayList<OsmPrimitive>(); 180 parents.addAll(getLayer().data.ways); 181 parents.addAll(getLayer().data.relations); 182 List<OsmParentChildPair> pairs = getParentChildPairs(parents, primitive); 183 hive.remove(toPurge); 184 for (OsmParentChildPair pair: pairs) { 185 if (pair.getParent() instanceof Way) { 186 Way w = (Way)pair.getParent(); 187 System.out.println(tr("removing reference from way {0}",w.getId())); 141 protected void removeReferecesToPrimitive(OsmPrimitive child, Set<OsmPrimitive> hive) { 142 hive.remove(child); 143 for (OsmPrimitive parent: this.backreferenceDataSet.getParents(child)) { 144 if (toPurge.contains(parent)) 145 // parent itself is to be purged. This method is going to be 146 // invoked for parent later 147 return; 148 if (parent instanceof Way) { 149 Way w = (Way)parent; 150 if (!origVersionsOfTouchedPrimitives.contains(w)) { 151 origVersionsOfTouchedPrimitives.add(w); 152 } 188 153 List<Node> wayNodes = w.getNodes(); 189 wayNodes.remove( primitive);154 wayNodes.remove(child); 190 155 w.setNodes(wayNodes); 191 // if a way ends up with less than two node we156 // if a way ends up with less than two nodes we 192 157 // remember it on the "hive" 193 158 // … … 195 160 System.out.println(tr("Warning: Purging way {0} because number of nodes dropped below 2. Current is {1}", 196 161 w.getId(),w.getNodesCount())); 197 if (!hive.contains(w)) { 198 hive.add(w); 199 } 162 hive.add(w); 200 163 } 201 } else if (pair.getParent() instanceof Relation) { 202 Relation r = (Relation)pair.getParent(); 203 System.out.println(tr("removing reference from relation {0}",r.getId())); 204 r.removeMembersFor(primitive); 164 } else if (parent instanceof Relation) { 165 Relation r = (Relation)parent; 166 if (!origVersionsOfTouchedPrimitives.contains(r)) { 167 origVersionsOfTouchedPrimitives.add(r); 168 } 169 System.out.println(tr("Removing reference from relation {0}",r.getId())); 170 r.removeMembersFor(child); 171 } else { 172 // should not happen. parent can't be a node 205 173 } 206 174 } … … 209 177 @Override 210 178 public boolean executeCommand() { 211 ArrayList<OsmPrimitive> hive = new ArrayList<OsmPrimitive>(); 179 if (backreferenceDataSet == null) { 180 backreferenceDataSet = new BackreferencedDataSet(getLayer().data); 181 backreferenceDataSet.build(); 182 } 183 HashSet<OsmPrimitive> hive = new HashSet<OsmPrimitive>(); 212 184 213 185 // iteratively purge the primitive and all primitives 214 // which violate invariants after they lo ose a reference to186 // which violate invariants after they lose a reference to 215 187 // the primitive (i.e. ways which end up with less than two 216 188 // nodes) 217 hive.add (primitive);189 hive.addAll(toPurge); 218 190 while(! hive.isEmpty()) { 219 OsmPrimitive toPurge = hive.get(0);220 purge(toPurge, getLayer().data, hive);221 if (toPurge instanceof Node) {222 getLayer().data.nodes.remove(toPurge);223 } else if (primitive instanceof Way) {224 getLayer().data.ways.remove(toPurge);225 } else if (primitive instanceof Relation) {226 getLayer().data.relations.remove(toPurge);191 OsmPrimitive p = hive.iterator().next(); 192 removeReferecesToPrimitive(p, hive); 193 getLayer().data.removePrimitive(p); 194 purgedPrimitives.add(p); 195 ConflictCollection conflicts = getLayer().getConflicts(); 196 if (conflicts.hasConflictForMy(p)) { 197 rememberConflict(conflicts.getConflictForMy(p)); 198 conflicts.remove(p); 227 199 } 228 purgedPrimitives.add(toPurge); 229 ConflictCollection conflicts = getLayer().getConflicts(); 230 if (conflicts.hasConflictForMy(toPurge)) { 231 rememberConflict(conflicts.getConflictForMy(toPurge)); 232 conflicts.remove(toPurge); 233 } 234 } 200 } 201 // we don't need this any more 202 backreferenceDataSet = null; 235 203 return super.executeCommand(); 236 204 } … … 239 207 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, 240 208 Collection<OsmPrimitive> added) { 241 for (OsmParentChildPair pair : pairs) { 242 modified.add(pair.getParent()); 243 } 244 // we don't need pairs anymore 245 pairs = null; 209 modified.addAll(origVersionsOfTouchedPrimitives); 246 210 } 247 211 … … 249 213 public void undoCommand() { 250 214 if (! Main.map.mapView.hasLayer(getLayer())) { 251 logger.warning(tr("Can' t undo command ''{0}'' because layer ''{1}'' is not present any more",215 logger.warning(tr("Can''t undo command ''{0}'' because layer ''{1}'' is not present any more", 252 216 this.toString(), 253 217 getLayer().toString() … … 263 227 } 264 228 reconstituteConflicts(); 265 // will restore the former references to the purged nodes 266 // 229 230 // will restore the primitives referring to one 231 // of the purged primitives 267 232 super.undoCommand(); 268 233 } 234 235 /** 236 * Use to inject a backreference data set used when the command 237 * is executed. 238 * 239 * @param ds the backreference data set 240 */ 241 public void setBackreferenceDataSet(BackreferencedDataSet ds) { 242 this.backreferenceDataSet = ds; 243 } 269 244 } -
trunk/src/org/openstreetmap/josm/data/conflict/Conflict.java
r1911 r2198 6 6 /** 7 7 * Represents a conflict between two {@see OsmPrimitive}s. It is represented as 8 * a pair if {@see OsmPrimitive}where one element of the pair has the role <em>my</em>8 * a pair of {@see OsmPrimitive}s where one element of the pair has the role <em>my</em> 9 9 * and the other has the role <em>their</em>. 10 10 * <ul> -
trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java
r2181 r2198 5 5 6 6 import java.util.Collection; 7 import java.util.HashSet; 7 8 9 import org.openstreetmap.josm.actions.upload.CyclicUploadDependencyException; 8 10 import org.openstreetmap.josm.data.APIDataSet; 9 11 import org.openstreetmap.josm.data.osm.Changeset; 10 12 import org.openstreetmap.josm.data.osm.DataSet; 11 13 import org.openstreetmap.josm.data.osm.OsmPrimitive; 14 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 15 import org.openstreetmap.josm.gui.DefaultNameFormatter; 12 16 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 13 17 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 14 18 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 15 import org.openstreetmap.josm.io.ChangesetProcessingType; 19 import org.openstreetmap.josm.io.OsmApi; 20 import org.openstreetmap.josm.io.OsmApiPrimitiveGoneException; 16 21 import org.openstreetmap.josm.io.OsmServerWriter; 22 import org.openstreetmap.josm.io.OsmTransferException; 17 23 18 24 /** … … 37 43 private Changeset changeset; 38 44 private boolean closeChangesetAfterUpload; 45 private Collection<OsmPrimitive> toUpload; 46 private HashSet<OsmPrimitive> processedPrimitives; 39 47 40 48 /** … … 42 50 * @param layer the layer. Must not be null. 43 51 * @param monitor a progress monitor. If monitor is null, uses {@see NullProgressMonitor#INSTANCE} 44 * @param changeset the changeset to be used if <code>changesetProcessingType</code> indicates that a new 45 * changeset is to be used 52 * @param changeset the changeset to be used 46 53 * @param closeChangesetAfterUpload true, if the changeset should be closed after the upload 47 54 * @throws IllegalArgumentException thrown, if layer is null … … 57 64 this.changeset = changeset; 58 65 this.closeChangesetAfterUpload = closeChangesetAfterUpload; 66 processedPrimitives = new HashSet<OsmPrimitive>(); 67 } 68 69 protected OsmPrimitive getPrimitive(OsmPrimitiveType type, long id) { 70 for (OsmPrimitive p: toUpload) { 71 if (OsmPrimitiveType.from(p).equals(type) && p.getId() == id) 72 return p; 73 } 74 return null; 75 } 76 77 /** 78 * Retries to recover the upload operation from an exception which was thrown because 79 * an uploaded primitive was already deleted on the server. 80 * 81 * @param e the exception throw by the API 82 * @param monitor a progress monitor 83 * @throws OsmTransferException thrown if we can't recover from the exception 84 */ 85 protected void recoverFromGoneOnServer(OsmApiPrimitiveGoneException e, ProgressMonitor monitor) throws OsmTransferException{ 86 if (!e.isKnownPrimitive()) throw e; 87 OsmPrimitive p = getPrimitive(e.getPrimitiveType(), e.getPrimitiveId()); 88 if (p == null) throw e; 89 if (p.isDeleted()) { 90 // we tried to delete an already deleted primitive. 91 // 92 System.out.println(tr("Warning: primitive ''{0}'' is already deleted on the server. Skipping this primitive and retrying to upload.", p.getDisplayName(DefaultNameFormatter.getInstance()))); 93 processedPrimitives.addAll(writer.getProcessedPrimitives()); 94 processedPrimitives.add(p); 95 toUpload.removeAll(processedPrimitives); 96 return; 97 } 98 // exception was thrown because we tried to *update* an already deleted 99 // primitive. We can't resolve this automatically. Re-throw exception, 100 // a conflict is going to be created later. 101 throw e; 59 102 } 60 103 … … 62 105 public void run() { 63 106 monitor.subTask(tr("Preparing primitives to upload ...")); 64 Collection<OsmPrimitive> toUpload = new APIDataSet(layer.data).getPrimitives(); 107 APIDataSet ds = new APIDataSet(layer.data); 108 try { 109 ds.adjustRelationUploadOrder(); 110 } catch(CyclicUploadDependencyException e) { 111 setLastException(e); 112 return; 113 } 114 toUpload = ds.getPrimitives(); 65 115 if (toUpload.isEmpty()) 66 116 return; 67 117 writer = new OsmServerWriter(); 68 118 try { 69 ProgressMonitor m = monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 70 if (isCancelled()) return; 71 writer.uploadOsm(layer.data.version, toUpload, changeset, closeChangesetAfterUpload, m); 119 while(true) { 120 try { 121 ProgressMonitor m = monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 122 if (isCancelled()) return; 123 writer.uploadOsm(layer.data.version, toUpload, changeset, m); 124 processedPrimitives.addAll(writer.getProcessedPrimitives()); 125 break; 126 } catch(OsmApiPrimitiveGoneException e) { 127 recoverFromGoneOnServer(e, monitor); 128 } 129 } 130 if (closeChangesetAfterUpload) { 131 if (changeset != null && changeset.getId() > 0) { 132 OsmApi.getOsmApi().closeChangeset(changeset, monitor.createSubTaskMonitor(0, false)); 133 } 134 } 72 135 } catch (Exception sxe) { 73 136 if (isCancelled()) { … … 80 143 if (isCancelled()) 81 144 return; 82 layer.cleanupAfterUpload( writer.getProcessedPrimitives());145 layer.cleanupAfterUpload(processedPrimitives); 83 146 DataSet.fireSelectionChanged(layer.data.getSelected()); 84 147 layer.fireDataChange(); 85 148 layer.onPostUploadToServer(); 149 150 // don't process exceptions remembered with setLastException(). 151 // Caller is supposed to deal with them. 86 152 } 87 153 88 154 @Override 89 155 public void cancel() { 90 // make sure the the softCancel operation is serialized with91 // blocks which can be interrupted.92 156 setCancelled(true); 93 157 if (writer != null) { -
trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
r2120 r2198 38 38 import org.openstreetmap.josm.Main; 39 39 import org.openstreetmap.josm.actions.RenameLayerAction; 40 import org.openstreetmap.josm.command.PurgePrimitivesCommand; 40 41 import org.openstreetmap.josm.data.conflict.Conflict; 41 42 import org.openstreetmap.josm.data.conflict.ConflictCollection; … … 45 46 import org.openstreetmap.josm.data.gpx.GpxTrack; 46 47 import org.openstreetmap.josm.data.gpx.WayPoint; 48 import org.openstreetmap.josm.data.osm.BackreferencedDataSet; 47 49 import org.openstreetmap.josm.data.osm.DataSet; 48 50 import org.openstreetmap.josm.data.osm.DataSource; … … 285 287 if (data.version == null) { 286 288 data.version = from.version; 287 } else { 288 if ("0.5".equals(data.version) ^ "0.5".equals(from.version)) { 289 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5")); 290 data.version = "0.5"; 291 } 289 } else if ("0.5".equals(data.version) ^ "0.5".equals(from.version)) { 290 System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5")); 291 data.version = "0.5"; 292 } 293 294 int numNewConflicts = 0; 295 for (Conflict<?> c : visitor.getConflicts()) { 296 if (!conflicts.hasConflict(c)) { 297 numNewConflicts++; 298 conflicts.add(c); 299 } 300 } 301 PurgePrimitivesCommand cmd = buildPurgeCommand(); 302 if (cmd != null) { 303 Main.main.undoRedo.add(cmd); 292 304 } 293 305 fireDataChange(); 294 306 // repaint to make sure new data is displayed properly. 295 307 Main.map.mapView.repaint(); 296 297 int numNewConflicts = 0; 298 for (Conflict c : visitor.getConflicts()) { 299 if (!conflicts.hasConflict(c)) { 300 numNewConflicts++; 301 conflicts.add(c); 302 } 303 } 308 warnNumNewConflicts( 309 numNewConflicts, 310 cmd == null ? 0 : cmd.getPurgedPrimitives().size() 311 ); 312 } 313 314 /** 315 * Warns the user about the number of detected conflicts 316 * 317 * @param numNewConflicts the number of detected conflicts 318 * @param numPurgedPrimitives the number of automatically purged objects 319 */ 320 protected void warnNumNewConflicts(int numNewConflicts, int numPurgedPrimitives) { 321 if (numNewConflicts == 0 && numPurgedPrimitives == 0) return; 322 323 String msg1 = trn( 324 "There was {0} conflict detected.", 325 "There were {0} conflicts detected.", 326 numNewConflicts, 327 numNewConflicts 328 ); 329 String msg2 = trn( 330 "{0} object has been purged from the local dataset because it is deleted on the server.", 331 "{0} objects have been purged from the local dataset because they are deleted on the server.", 332 numPurgedPrimitives, 333 numPurgedPrimitives 334 ); 335 StringBuffer sb = new StringBuffer(); 336 sb.append("<html>").append(msg1); 337 if (numPurgedPrimitives > 0) { 338 sb.append("<br>").append(msg2); 339 } 340 sb.append("</html>"); 304 341 if (numNewConflicts > 0) { 305 342 JOptionPane.showMessageDialog( 306 343 Main.parent, 307 tr("There were {0} conflicts during import.", numNewConflicts),308 tr(" Warning"),344 sb.toString(), 345 tr("Conflicts detected"), 309 346 JOptionPane.WARNING_MESSAGE 310 347 ); 311 348 } 349 } 350 351 /** 352 * Builds the purge command for primitives which can be purged automatically 353 * from the local dataset because they've been deleted on the 354 * server. 355 * 356 * @return the purge command. <code>null</code> if no primitives have to 357 * be purged 358 */ 359 protected PurgePrimitivesCommand buildPurgeCommand() { 360 BackreferencedDataSet ds = new BackreferencedDataSet(data); 361 ds.build(); 362 ArrayList<OsmPrimitive> toPurge = new ArrayList<OsmPrimitive>(); 363 conflictLoop: for (Conflict<?> c: conflicts) { 364 if (c.getMy().isDeleted() && !c.getTheir().isVisible()) { 365 // Local and server version of the primitive are deleted. We 366 // can purge it from the local dataset. 367 // 368 toPurge.add(c.getMy()); 369 } else if (!c.getMy().isModified() && ! c.getTheir().isVisible()) { 370 // We purge deleted *ways* and *relations* automatically if they are 371 // deleted on the server and if they aren't modified in the local 372 // dataset. 373 // 374 if (c.getMy() instanceof Way || c.getMy() instanceof Relation) { 375 toPurge.add(c.getMy()); 376 continue conflictLoop; 377 } 378 // We only purge nodes if they aren't part of a modified way. 379 // Otherwise the number of nodes of a modified way could drop 380 // below 2 and we would lose the modified data when the way 381 // gets purged. 382 // 383 for (OsmPrimitive parent: ds.getParents(c.getMy())) { 384 if (parent.isModified() && parent instanceof Way) { 385 continue conflictLoop; 386 } 387 } 388 toPurge.add(c.getMy()); 389 } 390 } 391 if (toPurge.isEmpty()) return null; 392 PurgePrimitivesCommand cmd = new PurgePrimitivesCommand(this, toPurge); 393 cmd.setBackreferenceDataSet(ds); 394 return cmd; 312 395 } 313 396 … … 580 663 */ 581 664 public void onPostUploadToServer() { 582 setRequiresUploadToServer( false);665 setRequiresUploadToServer(data.isModified()); 583 666 // keep requiresSaveToDisk unchanged 584 667 } -
trunk/src/org/openstreetmap/josm/io/OsmApi.java
r2181 r2198 398 398 * 399 399 * @param list the list of changed OSM Primitives 400 * @param monitor the progress monitor 400 401 * @return list of processed primitives 401 402 * @throws OsmTransferException if something is wrong 402 403 */ 403 public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list, ProgressMonitor progressMonitor) throws OsmTransferException {404 public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list, ProgressMonitor monitor) throws OsmTransferException { 404 405 try { 405 progressMonitor.beginTask("", list.size() * 2);406 monitor.beginTask("", list.size() * 2); 406 407 if (changeset == null) 407 408 throw new OsmTransferException(tr("No changeset present for diff upload.")); 408 409 409 initialize( progressMonitor);410 initialize(monitor); 410 411 final ArrayList<OsmPrimitive> processed = new ArrayList<OsmPrimitive>(); 411 412 412 413 CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset, OsmApi.this); 413 414 414 progressMonitor.subTask(tr("Preparing..."));415 monitor.subTask(tr("Preparing...")); 415 416 for (OsmPrimitive osm : list) { 416 417 osm.visit(duv); 417 progressMonitor.worked(1);418 monitor.worked(1); 418 419 } 419 progressMonitor.indeterminateSubTask(tr("Uploading..."));420 monitor.indeterminateSubTask(tr("Uploading...")); 420 421 421 422 String diff = duv.getDocument(); 422 String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff, progressMonitor);423 String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff,monitor); 423 424 DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), 424 progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));425 monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)); 425 426 return processed; 426 427 } catch(OsmTransferException e) { … … 429 430 throw new OsmTransferException(e); 430 431 } finally { 431 progressMonitor.finishTask(); 432 } 433 } 434 435 432 monitor.finishTask(); 433 } 434 } 436 435 437 436 private void sleepAndListen(int retry, ProgressMonitor monitor) throws OsmTransferCancelledException { … … 548 547 activeConnection.disconnect(); 549 548 550 if (retCode != 200) 551 throw new OsmApiException( 552 retCode, 553 errorHeader == null? null : errorHeader.trim(), 554 responseBody == null ? null : responseBody.toString().trim() 555 ); 556 549 errorHeader = errorHeader == null? null : errorHeader.trim(); 550 String errorBody = responseBody == null ? null : responseBody.toString().trim(); 551 switch(retCode) { 552 case HttpURLConnection.HTTP_OK: 553 break; // do nothing 554 case HttpURLConnection.HTTP_GONE: 555 throw new OsmApiPrimitiveGoneException(errorHeader, errorBody); 556 default: 557 throw new OsmApiException(retCode, errorHeader, errorBody); 558 559 } 557 560 return responseBody.toString(); 558 561 } catch (UnknownHostException e) { -
trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
r2181 r2198 13 13 import org.openstreetmap.josm.data.osm.OsmPrimitive; 14 14 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 15 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 15 16 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 16 17 … … 111 112 */ 112 113 protected void uploadChangesAsDiffUpload(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException { 113 // upload everything in one changeset114 //115 114 try { 116 115 progressMonitor.beginTask(tr("Starting to upload in one request ...")); … … 130 129 * @param apiVersion version of the data set 131 130 * @param primitives list of objects to send 132 */ 133 public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, boolean closeChangesetAfterUpload, ProgressMonitor progressMonitor) throws OsmTransferException { 131 * @param changeset the changeset the data is uploaded to. Must not be null. 132 * @param monitor the progress monitor. If null, assumes {@see NullProgressMonitor#INSTANCE} 133 * @throws IllegalArgumentException thrown if changeset is null 134 * @throws OsmTransferException thrown if something goes wrong 135 */ 136 public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, ProgressMonitor monitor) throws OsmTransferException { 137 if (changeset == null) 138 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "changeset")); 134 139 processed = new LinkedList<OsmPrimitive>(); 135 progressMonitor.beginTask(tr("Uploading data ..."));136 api.initialize(progressMonitor);140 monitor = monitor == null ? NullProgressMonitor.INSTANCE : monitor; 141 monitor.beginTask(tr("Uploading data ...")); 137 142 try { 143 api.initialize(monitor); 138 144 // check whether we can use diff upload 139 145 // 140 boolean ca sUseDiffUploads= api.hasSupportForDiffUploads();146 boolean canUseDiffUpload = api.hasSupportForDiffUploads(); 141 147 if (apiVersion == null) { 142 148 System.out.println(tr("WARNING: no API version defined for data to upload. Falling back to version 0.6")); … … 144 150 } 145 151 boolean useDiffUpload = Main.pref.getBoolean("osm-server.atomic-upload", apiVersion.compareTo("0.6")>=0); 146 if (useDiffUpload && ! ca sUseDiffUploads) {152 if (useDiffUpload && ! canUseDiffUpload) { 147 153 System.out.println(tr("WARNING: preference ''{0}'' or API version ''{1}'' of dataset requires to use diff uploads, but API is not able to handle them. Ignoring diff upload.", "osm-server.atomic-upload", apiVersion)); 148 154 useDiffUpload = false; 149 155 } 150 if (changeset == null) {151 changeset = new Changeset();152 }153 156 if (changeset.getId() == 0) { 154 api.openChangeset(changeset, progressMonitor.createSubTaskMonitor(0, false));157 api.openChangeset(changeset,monitor.createSubTaskMonitor(0, false)); 155 158 } else { 156 api.updateChangeset(changeset, progressMonitor.createSubTaskMonitor(0, false));159 api.updateChangeset(changeset,monitor.createSubTaskMonitor(0, false)); 157 160 } 158 161 api.setChangeset(changeset); 159 162 if (useDiffUpload) { 160 uploadChangesAsDiffUpload(primitives, progressMonitor.createSubTaskMonitor(0,false));163 uploadChangesAsDiffUpload(primitives,monitor.createSubTaskMonitor(0,false)); 161 164 } else { 162 uploadChangesIndividually(primitives, progressMonitor.createSubTaskMonitor(0,false));165 uploadChangesIndividually(primitives,monitor.createSubTaskMonitor(0,false)); 163 166 } 164 167 } catch(OsmTransferException e) { … … 167 170 throw new OsmTransferException(e); 168 171 } finally { 169 try { 170 if (closeChangesetAfterUpload && api.getChangeset() != null && api.getChangeset().getId() > 0) { 171 api.closeChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false)); 172 api.setChangeset(null); 173 } 174 } catch (Exception ee) { 175 OsmChangesetCloseException closeException = new OsmChangesetCloseException(ee); 176 closeException.setChangeset(api.getChangeset()); 177 throw closeException; 178 } finally { 179 progressMonitor.finishTask(); 180 } 172 monitor.finishTask(); 173 api.setChangeset(null); 181 174 } 182 175 }
Note:
See TracChangeset
for help on using the changeset viewer.