Changeset 1670 in josm for trunk/src/org/openstreetmap/josm/actions/UploadAction.java
- Timestamp:
- 15.06.2009 20:22:46 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/UploadAction.java
r1668 r1670 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.EventQueue;7 6 import java.awt.GridBagLayout; 8 7 import java.awt.event.ActionEvent; … … 13 12 import java.util.LinkedList; 14 13 import java.util.List; 15 import java.util.concurrent.ExecutionException; 16 import java.util.concurrent.FutureTask; 14 import java.util.logging.Logger; 17 15 import java.util.regex.Matcher; 18 16 import java.util.regex.Pattern; … … 23 21 import javax.swing.JPanel; 24 22 import javax.swing.JScrollPane; 25 import javax.swing.SwingUtilities;26 23 27 24 import org.openstreetmap.josm.Main; 28 25 import org.openstreetmap.josm.data.osm.OsmPrimitive; 29 import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;30 26 import org.openstreetmap.josm.gui.ExtendedDialog; 31 27 import org.openstreetmap.josm.gui.OsmPrimitivRenderer; 32 28 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 33 29 import org.openstreetmap.josm.gui.historycombobox.SuggestingJHistoryComboBox; 34 import org.openstreetmap.josm.io.DiffResultReader;35 30 import org.openstreetmap.josm.io.OsmApi; 36 31 import org.openstreetmap.josm.io.OsmApiException; … … 47 42 * An dialog is displayed asking the user to specify a rectangle to grab. 48 43 * The url and account settings from the preferences are used. 44 * 45 * If the upload fails this action offers various options to resolve conflicts. 49 46 * 50 47 * @author imi 51 48 */ 52 49 public class UploadAction extends JosmAction { 50 static private Logger logger = Logger.getLogger(UploadAction.class.getName()); 53 51 54 52 public static final String HISTORY_KEY = "upload.comment.history"; … … 215 213 return; 216 214 } 217 System.out.println("got exception: " + sxe.toString());218 215 uploadFailed = true; 219 216 lastException = sxe; … … 230 227 server.disconnectActiveConnection(); 231 228 uploadCancelled = true; 232 } ;233 234 } 229 } 230 } 231 235 232 Main.worker.execute(new UploadDiffTask()); 236 233 } 237 234 238 public void handleFailedUpload(Exception e) { 235 /** 236 * Synchronizes the local state of an {@see OsmPrimitive} with its state on the 237 * server. The method uses an individual GET for the primitive. 238 * 239 * @param id the primitive ID 240 */ 241 protected void synchronizePrimitive(final String id) { 242 243 /** 244 * The asynchronous task to update a a specific id 245 * 246 */ 247 class UpdatePrimitiveTask extends PleaseWaitRunnable { 248 249 private boolean uploadCancelled = false; 250 private boolean uploadFailed = false; 251 private Exception lastException = null; 252 253 public UpdatePrimitiveTask() { 254 super(tr("Updating primitive"),false /* don't ignore exceptions */); 255 } 256 257 @Override protected void realRun() throws SAXException, IOException { 258 try { 259 UpdateSelectionAction act = new UpdateSelectionAction(); 260 act.updatePrimitive(Long.parseLong(id)); 261 } catch (Exception sxe) { 262 if (uploadCancelled) { 263 System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + sxe.toString()); 264 return; 265 } 266 uploadFailed = true; 267 lastException = sxe; 268 } 269 } 270 271 @Override protected void finish() { 272 if (uploadFailed) { 273 handleFailedUpload(lastException); 274 } 275 } 276 277 @Override protected void cancel() { 278 OsmApi.getOsmApi().cancel(); 279 uploadCancelled = true; 280 } 281 } 282 283 Main.worker.execute(new UpdatePrimitiveTask()); 284 } 285 286 /** 287 * Synchronizes the local state of the dataset with the state on the server. 288 * 289 * Reuses the functionality of {@see UpdateDataAction}. 290 * 291 * @see UpdateDataAction#actionPerformed(ActionEvent) 292 */ 293 protected void synchronizeDataSet() { 294 UpdateDataAction act = new UpdateDataAction(); 295 act.actionPerformed(new ActionEvent(this,0,"")); 296 } 297 298 /** 299 * Handles the case that a conflict in a specific {@see OsmPrimitive} was detected while 300 * uploading 301 * 302 * @param primitiveType the type of the primitive, either <code>node</code>, <code>way</code> or 303 * <code>relation</code> 304 * @param id the id of the primitive 305 * @param serverVersion the version of the primitive on the server 306 * @param myVersion the version of the primitive in the local dataset 307 */ 308 protected void handleUploadConflictForKnownConflict(String primitiveType, String id, String serverVersion, String myVersion) { 309 Object[] options = new Object[] { 310 tr("Synchronize {0} {1} only", tr(primitiveType), id), 311 tr("Synchronize entire dataset"), 312 tr("Cancel") 313 }; 314 Object defaultOption = options[0]; 315 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>" 316 + "of your nodes, ways, or relations.<br>" 317 + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>" 318 + "the server has version {2}, your version is {3}.<br>" 319 + "<br>" 320 + "Click <strong>{4}</strong> to synchronize the conflicting primitive only.<br>" 321 + "Click <strong>{5}</strong> to synchronize the entire local dataset with the server.<br>" 322 + "Click <strong>{6}</strong> to abort and continue editing.<br></html>", 323 tr(primitiveType), id, serverVersion, myVersion, 324 options[0], options[1], options[2] 325 ); 326 int optionsType = JOptionPane.YES_NO_CANCEL_OPTION; 327 int ret = JOptionPane.showOptionDialog( 328 null, 329 msg, 330 tr("Conflict detected"), 331 optionsType, 332 JOptionPane.ERROR_MESSAGE, 333 null, 334 options, 335 defaultOption 336 ); 337 switch(ret) { 338 case JOptionPane.CLOSED_OPTION: return; 339 case JOptionPane.CANCEL_OPTION: return; 340 case 0: synchronizePrimitive(id); break; 341 case 1: synchronizeDataSet(); break; 342 default: 343 // should not happen 344 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret)); 345 } 346 } 347 348 /** 349 * Handles the case that a conflict was detected while uploading where we don't 350 * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason) 351 * 352 */ 353 protected void handleUploadConflictForUnknownConflict() { 354 Object[] options = new Object[] { 355 tr("Synchronize entire dataset"), 356 tr("Cancel") 357 }; 358 Object defaultOption = options[0]; 359 String msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>" 360 + "of your nodes, ways, or relations.<br>" 361 + "<br>" 362 + "Click <strong>{0}</strong> to synchronize the entire local dataset with the server.<br>" 363 + "Click <strong>{1}</strong> to abort and continue editing.<br></html>", 364 options[0], options[1] 365 ); 366 int optionsType = JOptionPane.YES_NO_OPTION; 367 int ret = JOptionPane.showOptionDialog( 368 null, 369 msg, 370 tr("Conflict detected"), 371 optionsType, 372 JOptionPane.ERROR_MESSAGE, 373 null, 374 options, 375 defaultOption 376 ); 377 switch(ret) { 378 case JOptionPane.CLOSED_OPTION: return; 379 case 1: return; 380 case 0: synchronizeDataSet(); break; 381 default: 382 // should not happen 383 throw new IllegalStateException(tr("unexpected return value. Got {0}", ret)); 384 } 385 } 386 387 /** 388 * handles an upload conflict, i.e. an error indicated by a HTTP return code 409. 389 * 390 * @param e the exception 391 */ 392 protected void handleUploadConflict(OsmApiException e) { 393 String pattern = "Version mismatch: Provided (\\d+), server had: (\\d+) of (\\S+) (\\d+)"; 394 Pattern p = Pattern.compile(pattern); 395 Matcher m = p.matcher(e.getErrorHeader()); 396 if (m.matches()) { 397 handleUploadConflictForKnownConflict(m.group(3), m.group(4), m.group(2),m.group(1)); 398 } else { 399 logger.warning(tr("Warning: error header \"{0}\" did not match expected pattern \"{1}\"", e.getErrorHeader(),pattern)); 400 handleUploadConflictForUnknownConflict(); 401 } 402 } 403 404 /** 405 * Handles an upload error due to a violated precondition, i.e. a HTTP return code 412 406 * 407 * @param e the exception 408 */ 409 protected void handlePreconditionFailed(OsmApiException e) { 410 JOptionPane.showMessageDialog( 411 Main.parent, 412 tr("<html>Uploading to the server <strong>failed</strong> because your current<br>" 413 +"dataset violates a precondition.<br>" 414 +"The error message is:<br>" 415 + "{0}" 416 + "</html>", 417 e.getMessage() 418 ), 419 tr("Precondition violation"), 420 JOptionPane.ERROR_MESSAGE 421 ); 422 e.printStackTrace(); 423 } 424 425 426 /** 427 * Handles an error due to a delete request on an already deleted 428 * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we know what 429 * {@see OsmPrimitive} is responsible for the error. 430 * 431 * Reuses functionality of the {@see UpdateSelectionAction} to resolve 432 * conflicts due to mismatches in the deleted state. 433 * 434 * @param primitiveType the type of the primitive 435 * @param id the id of the primitive 436 * 437 * @see UpdateSelectionAction#handlePrimitiveGoneException(long) 438 */ 439 protected void handleGoneForKnownPrimitive(String primitiveType, String id) { 440 UpdateSelectionAction act = new UpdateSelectionAction(); 441 act.handlePrimitiveGoneException(Long.parseLong(id)); 442 } 443 444 /** 445 * handles the case of an error due to a delete request on an already deleted 446 * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which 447 * {@see OsmPrimitive} is causing the error. 448 * 449 * @param e the exception 450 */ 451 protected void handleGoneForUnknownPrimitive(OsmApiException e) { 452 String msg = tr("<html>Uploading <strong>failed</strong> because a primitive you tried to<br>" 453 + "delete on the server is already deleted.<br>" 454 + "<br>" 455 + "The error message is:<br>" 456 + "{0}" 457 + "</html>", 458 e.getMessage() 459 ); 460 JOptionPane.showMessageDialog( 461 Main.parent, 462 msg, 463 tr("Primitive already deleted"), 464 JOptionPane.ERROR_MESSAGE 465 ); 466 467 } 468 469 /** 470 * Handles an error which is caused by a delete request for an already deleted 471 * {@see OsmPrimitive} on the server, i.e. a HTTP response code of 410. 472 * Note that an <strong>update</strong> on an already deleted object results 473 * in a 409, not a 410. 474 * 475 * @param e the exception 476 */ 477 protected void handleGone(OsmApiException e) { 478 String pattern = "The (\\S+) with the id (\\d+) has already been deleted"; 479 Pattern p = Pattern.compile(pattern); 480 Matcher m = p.matcher(e.getErrorHeader()); 481 if (m.matches()) { 482 handleGoneForKnownPrimitive(m.group(1), m.group(2)); 483 } else { 484 logger.warning(tr("Error header \"{0}\" doesn't match expected pattern \"{1}\"",e.getErrorHeader(), pattern)); 485 handleGoneForUnknownPrimitive(e); 486 } 487 } 488 489 490 /** 491 * error handler for any exception thrown during upload 492 * 493 * @param e the exception 494 */ 495 protected void handleFailedUpload(Exception e) { 496 // API initialization failed. Notify the user and return. 497 // 239 498 if (e instanceof OsmApiInitializationException) { 240 handleOsmApiInitializationException( e);499 handleOsmApiInitializationException((OsmApiInitializationException)e); 241 500 return; 242 501 } 502 243 503 if (e instanceof OsmApiException) { 244 504 OsmApiException ex = (OsmApiException)e; 505 // There was an upload conflict. Let the user decide whether 506 // and how to resolve it 507 // 245 508 if(ex.getResponseCode() == HttpURLConnection.HTTP_CONFLICT) { 246 Pattern p = Pattern.compile("Version mismatch: Provided (\\d+), server had: (\\d+) of (\\S+) (\\d+)"); 247 Matcher m = p.matcher(ex.getErrorHeader()); 248 String msg; 249 if (m.matches()) { 250 msg = tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>" 251 + "of your nodes, ways or relations.<br>" 252 + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>" 253 + "the server has version {2}, your version is {3}.<br>" 254 + "Please synchronize your local dataset using <br>" 255 + "<strong>File->Update Data</strong>, resolve<br>" 256 + "any conflicts and try to upload again.</html>", 257 m.group(3),m.group(4), m.group(2), m.group(1) 258 ); 259 } else { 260 msg = tr("<html>Uploading failed because the server has a newer version of one<br>" 261 + "of your nodes, ways or relations.<br>" 262 + "Please synchronize your local dataset using <br>" 263 + "<strong>File->Update Data</strong>, resolve<br>" 264 + "any conflicts and try to upload again.</html>" 265 ); 266 } 267 JOptionPane.showMessageDialog( 268 null, 269 msg, 270 tr("Upload to OSM API failed"), 271 JOptionPane.WARNING_MESSAGE 272 ); 509 handleUploadConflict(ex); 273 510 return; 274 511 } 275 } 512 // There was a precondition failed. Notify the user. 513 // 514 else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) { 515 handlePreconditionFailed(ex); 516 return; 517 } 518 // Tried to delete an already deleted primitive? Let the user 519 // decide whether and how to resolve this conflict. 520 // 521 else if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) { 522 handleGone(ex); 523 return; 524 } 525 } 526 527 // For any other exception just notify the user 528 // 529 String msg = e.getMessage().substring(0,Math.min(80, e.getMessage().length())); 530 if (msg.length() < e.getMessage().length()) { 531 msg += " ..."; 532 } 533 e.printStackTrace(); 276 534 JOptionPane.showMessageDialog( 277 535 null, 278 e.getMessage(),536 msg, 279 537 tr("Upload to OSM API failed"), 280 538 JOptionPane.ERROR_MESSAGE 281 539 ); 282 } 283 284 protected void handleOsmApiInitializationException(Exception e) { 540 541 } 542 543 /** 544 * handles an exception caught during OSM API initialization 545 * 546 * @param e the exception 547 */ 548 protected void handleOsmApiInitializationException(OsmApiInitializationException e) { 285 549 JOptionPane.showMessageDialog( 286 null,550 Main.parent, 287 551 tr( "Failed to initialize communication with the OSM server {0}.\n" 288 552 + "Check the server URL in your preferences and your internet connection.",
Note: See TracChangeset
for help on using the changeset viewer.
