Ticket #9446: 9446.patch
File 9446.patch, 28.6 KB (added by , 2 years ago) |
---|
-
src/org/openstreetmap/josm/actions/UploadAction.java
diff --git a/src/org/openstreetmap/josm/actions/UploadAction.java b/src/org/openstreetmap/josm/actions/UploadAction.java index 4c80c15f92..6e7771b258 100644
a b import java.util.LinkedList; 11 11 import java.util.List; 12 12 import java.util.Map; 13 13 import java.util.Optional; 14 import java.util.concurrent.CompletableFuture; 15 import java.util.concurrent.ExecutionException; 16 import java.util.concurrent.Future; 17 import java.util.function.Consumer; 14 18 15 19 import javax.swing.JOptionPane; 16 20 … … import org.openstreetmap.josm.io.ChangesetUpdater; 36 40 import org.openstreetmap.josm.io.UploadStrategySpecification; 37 41 import org.openstreetmap.josm.spi.preferences.Config; 38 42 import org.openstreetmap.josm.tools.ImageProvider; 43 import org.openstreetmap.josm.tools.JosmRuntimeException; 39 44 import org.openstreetmap.josm.tools.Logging; 40 45 import org.openstreetmap.josm.tools.Shortcut; 41 46 import org.openstreetmap.josm.tools.Utils; … … public class UploadAction extends AbstractUploadAction { 203 208 * @return true, if the preconditions are met; false, otherwise 204 209 */ 205 210 public static boolean checkPreUploadConditions(AbstractModifiableLayer layer, APIDataSet apiData) { 211 try { 212 return checkPreUploadConditionsAsync(layer, apiData, null).get(); 213 } catch (InterruptedException e) { 214 Thread.currentThread().interrupt(); 215 throw new JosmRuntimeException(e); 216 } catch (ExecutionException e) { 217 throw new JosmRuntimeException(e); 218 } 219 } 220 221 /** 222 * Check whether the preconditions are met to upload data in <code>apiData</code>. 223 * Makes sure upload is allowed, primitives in <code>apiData</code> don't participate in conflicts and 224 * runs the installed {@link UploadHook}s. 225 * 226 * @param layer the source layer of the data to be uploaded 227 * @param apiData the data to be uploaded 228 * @param onFinish {@code true} if the preconditions are met; {@code false}, otherwise 229 * @return A future that completes when the checks are finished 230 */ 231 public static Future<Boolean> checkPreUploadConditionsAsync(AbstractModifiableLayer layer, APIDataSet apiData, Consumer<Boolean> onFinish) { 232 final CompletableFuture<Boolean> future = new CompletableFuture<>(); 233 if (onFinish != null) { 234 future.thenAccept(onFinish); 235 } 206 236 if (layer.isUploadDiscouraged() && warnUploadDiscouraged(layer)) { 207 return false;237 future.complete(false); 208 238 } 209 239 if (layer instanceof OsmDataLayer) { 210 240 OsmDataLayer osmLayer = (OsmDataLayer) layer; 211 241 ConflictCollection conflicts = osmLayer.getConflicts(); 212 242 if (apiData.participatesInConflict(conflicts)) { 213 243 alertUnresolvedConflicts(osmLayer); 214 return false; 244 if (!future.isDone()) { 245 future.complete(false); 246 } 215 247 } 216 248 } 217 249 // Call all upload hooks in sequence. 218 // FIXME: this should become an asynchronous task 219 // 220 if (apiData != null) { 221 return UPLOAD_HOOKS.stream().allMatch(hook -> hook.checkUpload(apiData)); 250 if (!future.isDone()) { 251 MainApplication.worker.execute(() -> { 252 boolean hooks = true; 253 if (apiData != null) { 254 hooks = UPLOAD_HOOKS.stream().allMatch(hook -> hook.checkUpload(apiData)); 255 } 256 future.complete(hooks); 257 }); 222 258 } 223 224 return true; 259 return future; 225 260 } 226 261 227 262 /** … … public class UploadAction extends AbstractUploadAction { 235 270 new Notification(tr("No changes to upload.")).show(); 236 271 return; 237 272 } 238 if (!checkPreUploadConditions(layer, apiData)) 239 return; 273 checkPreUploadConditionsAsync(layer, apiData, passed -> GuiHelper.runInEDT(() -> { 274 if (Boolean.TRUE.equals(passed)) { 275 realUploadData(layer, apiData); 276 } else { 277 new Notification(tr("One of the upload verification processes failed")).show(); 278 } 279 })); 280 } 281 282 /** 283 * Uploads data to the OSM API. 284 * 285 * @param layer the source layer for the data to upload 286 * @param apiData the primitives to be added, updated, or deleted 287 */ 288 private static void realUploadData(final OsmDataLayer layer, final APIDataSet apiData) { 240 289 241 290 ChangesetUpdater.check(); 242 291 -
src/org/openstreetmap/josm/actions/upload/ValidateUploadHook.java
diff --git a/src/org/openstreetmap/josm/actions/upload/ValidateUploadHook.java b/src/org/openstreetmap/josm/actions/upload/ValidateUploadHook.java index 06d704c75c..091b4db44b 100644
a b import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 6 import java.awt.Dimension; 7 7 import java.awt.GridBagLayout; 8 import java.util.ArrayList;9 8 import java.util.Collection; 10 9 import java.util.List; 10 import java.util.concurrent.atomic.AtomicBoolean; 11 11 12 12 import javax.swing.JPanel; 13 13 import javax.swing.JScrollPane; 14 14 15 15 import org.openstreetmap.josm.data.APIDataSet; 16 16 import org.openstreetmap.josm.data.osm.OsmPrimitive; 17 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;18 17 import org.openstreetmap.josm.data.validation.OsmValidator; 19 import org.openstreetmap.josm.data.validation.Severity;20 import org.openstreetmap.josm.data.validation.Test;21 18 import org.openstreetmap.josm.data.validation.TestError; 19 import org.openstreetmap.josm.data.validation.ValidationTask; 22 20 import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor; 23 21 import org.openstreetmap.josm.gui.ExtendedDialog; 24 22 import org.openstreetmap.josm.gui.MainApplication; 25 import org.openstreetmap.josm.gui.MapFrame;26 23 import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel; 27 import org.openstreetmap.josm.gui.layer.OsmDataLayer;28 24 import org.openstreetmap.josm.gui.layer.ValidatorLayer; 29 25 import org.openstreetmap.josm.gui.util.GuiHelper; 30 26 import org.openstreetmap.josm.gui.widgets.HtmlPanel; … … public class ValidateUploadHook implements UploadHook { 48 44 */ 49 45 @Override 50 46 public boolean checkUpload(APIDataSet apiDataSet) { 51 52 OsmValidator.initializeTests(); 53 Collection<Test> tests = OsmValidator.getEnabledTests(true); 54 if (tests.isEmpty()) 55 return true; 56 47 AtomicBoolean returnCode = new AtomicBoolean(); 57 48 AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor(); 58 49 v.visit(apiDataSet.getPrimitivesToAdd()); 59 Collection<OsmPrimitive> selection = v.visit(apiDataSet.getPrimitivesToUpdate()); 60 61 List<TestError> errors = new ArrayList<>(30); 62 for (Test test : tests) { 63 test.setBeforeUpload(true); 64 test.setPartialSelection(true); 65 test.startTest(null); 66 test.visit(selection); 67 test.endTest(); 68 if (ValidatorPrefHelper.PREF_OTHER.get() && ValidatorPrefHelper.PREF_OTHER_UPLOAD.get()) { 69 errors.addAll(test.getErrors()); 50 Collection<OsmPrimitive> visited = v.visit(apiDataSet.getPrimitivesToUpdate()); 51 OsmValidator.initializeTests(); 52 new ValidationTask(errors -> { 53 if (errors.stream().allMatch(TestError::isIgnored)) { 54 returnCode.set(true); 70 55 } else { 71 for (TestError e : test.getErrors()) { 72 if (e.getSeverity() != Severity.OTHER) { 73 errors.add(e); 74 } 75 } 56 // Unfortunately, the progress monitor is not "finished" until after `finish` is called, so we will 57 // have a ProgressMonitor open behind the error screen. Fortunately, the error screen appears in front 58 // of the progress monitor. 59 GuiHelper.runInEDTAndWait(() -> returnCode.set(displayErrorScreen(errors))); 76 60 } 77 test.clear(); 78 test.setBeforeUpload(false); 79 } 80 81 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) { 82 errors.forEach(TestError::updateIgnored); 83 } 84 85 OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 86 if (editLayer != null) { 87 editLayer.validationErrors.clear(); 88 editLayer.validationErrors.addAll(errors); 89 } 90 MapFrame map = MainApplication.getMap(); 91 if (map != null) { 92 map.validatorDialog.tree.setErrors(errors); 93 } 94 if (errors.stream().allMatch(TestError::isIgnored)) 95 return true; 61 }, null, OsmValidator.getEnabledTests(true), visited, null, true).run(); 96 62 97 return displayErrorScreen(errors);63 return returnCode.get(); 98 64 } 99 65 100 66 /** -
src/org/openstreetmap/josm/data/validation/ValidationTask.java
diff --git a/src/org/openstreetmap/josm/data/validation/ValidationTask.java b/src/org/openstreetmap/josm/data/validation/ValidationTask.java index b6e5d161eb..21eb1d3ca9 100644
a b package org.openstreetmap.josm.data.validation; 3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.GraphicsEnvironment; 6 7 import java.util.ArrayList; 7 8 import java.util.Collection; 8 9 import java.util.List; 10 import java.util.function.BiConsumer; 11 import java.util.function.Consumer; 9 12 10 13 import javax.swing.JOptionPane; 11 14 … … import org.openstreetmap.josm.tools.Utils; 25 28 * Asynchronous task for running a collection of tests against a collection of primitives 26 29 */ 27 30 public class ValidationTask extends PleaseWaitRunnable { 31 private final Consumer<List<TestError>> onFinish; 28 32 private Collection<Test> tests; 29 33 private final Collection<OsmPrimitive> validatedPrimitives; 30 34 private final Collection<OsmPrimitive> formerValidatedPrimitives; 35 private final boolean beforeUpload; 31 36 private boolean canceled; 32 37 private List<TestError> errors; 38 private BiConsumer<ValidationTask, Test> testConsumer; 33 39 34 40 /** 35 41 * Constructs a new {@code ValidationTask} … … public class ValidationTask extends PleaseWaitRunnable { 44 50 this(new PleaseWaitProgressMonitor(tr("Validating")), tests, validatedPrimitives, formerValidatedPrimitives); 45 51 } 46 52 47 protected ValidationTask(ProgressMonitor progressMonitor, 48 Collection<Test> tests, 49 Collection<OsmPrimitive> validatedPrimitives, 50 Collection<OsmPrimitive> formerValidatedPrimitives) { 51 super(tr("Validating"), progressMonitor, false /*don't ignore exceptions */); 53 /** 54 * Constructs a new {@code ValidationTask} 55 * 56 * @param onFinish called when the tests are finished 57 * @param progressMonitor the progress monitor to update with test progress 58 * @param tests the tests to run 59 * @param validatedPrimitives the collection of primitives to validate. 60 * @param formerValidatedPrimitives the last collection of primitives being validates. May be null. 61 * @param beforeUpload {@code true} if this is being run prior to upload 62 * @since xxx 63 */ 64 public ValidationTask(Consumer<List<TestError>> onFinish, 65 ProgressMonitor progressMonitor, 66 Collection<Test> tests, 67 Collection<OsmPrimitive> validatedPrimitives, 68 Collection<OsmPrimitive> formerValidatedPrimitives, 69 boolean beforeUpload) { 70 super(tr("Validating"), 71 progressMonitor != null ? progressMonitor : new PleaseWaitProgressMonitor(tr("Validating")), 72 false /*don't ignore exceptions */); 73 this.onFinish = onFinish; 52 74 this.validatedPrimitives = validatedPrimitives; 53 75 this.formerValidatedPrimitives = formerValidatedPrimitives; 54 76 this.tests = tests; 77 this.beforeUpload = beforeUpload; 78 } 79 80 protected ValidationTask(ProgressMonitor progressMonitor, 81 Collection<Test> tests, 82 Collection<OsmPrimitive> validatedPrimitives, 83 Collection<OsmPrimitive> formerValidatedPrimitives) { 84 this(null, progressMonitor, tests, validatedPrimitives, formerValidatedPrimitives, false); 55 85 } 56 86 57 87 @Override … … public class ValidationTask extends PleaseWaitRunnable { 63 93 protected void finish() { 64 94 if (canceled) return; 65 95 66 // update GUI on Swing EDT 67 GuiHelper.runInEDT(() -> { 68 MapFrame map = MainApplication.getMap(); 69 map.validatorDialog.unfurlDialog(); 70 map.validatorDialog.tree.setErrors(errors); 71 //FIXME: nicer way to find / invalidate the corresponding error layer 72 MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate); 73 if (!errors.isEmpty()) { 74 OsmValidator.initializeErrorLayer(); 75 } 76 }); 96 if (!GraphicsEnvironment.isHeadless() && MainApplication.getMap() != null) { 97 // update GUI on Swing EDT 98 GuiHelper.runInEDT(() -> { 99 MapFrame map = MainApplication.getMap(); 100 map.validatorDialog.unfurlDialog(); 101 map.validatorDialog.tree.setErrors(errors); 102 //FIXME: nicer way to find / invalidate the corresponding error layer 103 MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate); 104 if (!errors.isEmpty()) { 105 OsmValidator.initializeErrorLayer(); 106 } 107 }); 108 } 109 if (this.onFinish != null) { 110 this.onFinish.accept(this.errors); 111 } 77 112 } 78 113 79 114 @Override … … public class ValidationTask extends PleaseWaitRunnable { 88 123 return; 89 124 testCounter++; 90 125 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName())); 91 test.setBeforeUpload( false);126 test.setBeforeUpload(this.beforeUpload); 92 127 test.setPartialSelection(formerValidatedPrimitives != null); 93 128 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false)); 94 129 test.visit(validatedPrimitives); 95 130 test.endTest(); 96 131 errors.addAll(test.getErrors()); 132 if (this.testConsumer != null) { 133 this.testConsumer.accept(this, test); 134 } 97 135 test.clear(); 136 test.setBeforeUpload(false); 98 137 } 99 138 tests = null; 100 139 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) { … … public class ValidationTask extends PleaseWaitRunnable { 124 163 public List<TestError> getErrors() { 125 164 return errors; 126 165 } 166 167 /** 168 * A test consumer to avoid filling up memory. A test consumer <i>may</i> remove tests it has consumed. 169 * @param testConsumer The consumer which takes a {@link ValidationTask} ({@code this}) and the test that finished. 170 * @since xxx 171 */ 172 public void setTestConsumer(BiConsumer<ValidationTask, Test> testConsumer) { 173 this.testConsumer = testConsumer; 174 } 127 175 } -
src/org/openstreetmap/josm/data/validation/ValidatorCLI.java
diff --git a/src/org/openstreetmap/josm/data/validation/ValidatorCLI.java b/src/org/openstreetmap/josm/data/validation/ValidatorCLI.java index cd4164c37f..0e7c787e7d 100644
a b import java.io.InputStream; 10 10 import java.io.OutputStream; 11 11 import java.nio.charset.StandardCharsets; 12 12 import java.nio.file.Files; 13 import java.nio.file.Path; 13 14 import java.nio.file.Paths; 14 15 import java.util.ArrayList; 15 16 import java.util.Arrays; … … import java.util.function.Supplier; 25 26 import java.util.logging.Level; 26 27 import java.util.stream.Collectors; 27 28 29 import javax.json.JsonObject; 30 28 31 import org.apache.commons.compress.utils.FileNameUtils; 29 32 import org.openstreetmap.josm.actions.ExtensionFileFilter; 30 33 import org.openstreetmap.josm.cli.CLIModule; … … public class ValidatorCLI implements CLIModule { 174 177 fileMonitor.beginTask(tr("Processing files..."), this.input.size()); 175 178 for (String inputFile : this.input) { 176 179 if (inputFile.endsWith(".validator.mapcss")) { 177 this.processValidatorFile(inputFile);180 processValidatorFile(inputFile); 178 181 } else if (inputFile.endsWith(".mapcss")) { 179 this.processMapcssFile(inputFile);182 processMapcssFile(inputFile); 180 183 } else { 181 184 this.processFile(inputFile); 182 185 } … … public class ValidatorCLI implements CLIModule { 195 198 * @param inputFile The mapcss file to validate 196 199 * @throws ParseException if the file does not match the mapcss syntax 197 200 */ 198 private void processMapcssFile(final String inputFile) throws ParseException {201 private static void processMapcssFile(final String inputFile) throws ParseException { 199 202 final MapCSSStyleSource styleSource = new MapCSSStyleSource(new File(inputFile).toURI().getPath(), inputFile, inputFile); 200 203 styleSource.loadStyleSource(); 201 204 if (!styleSource.getErrors().isEmpty()) { … … public class ValidatorCLI implements CLIModule { 212 215 * @throws IOException if there is a problem reading the file 213 216 * @throws ParseException if the file does not match the validator mapcss syntax 214 217 */ 215 private void processValidatorFile(final String inputFile) throws ParseException, IOException {218 private static void processValidatorFile(final String inputFile) throws ParseException, IOException { 216 219 // Check asserts 217 220 Config.getPref().putBoolean("validator.check_assert_local_rules", true); 218 221 final MapCSSTagChecker mapCSSTagChecker = new MapCSSTagChecker(); … … public class ValidatorCLI implements CLIModule { 254 257 OsmDataLayer dataLayer = null; 255 258 try { 256 259 Logging.info(task); 257 OsmValidator.initializeTests();258 260 dataLayer = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class) 259 261 .stream().filter(layer -> inputFileFile.equals(layer.getAssociatedFile())) 260 262 .findFirst().orElseThrow(() -> new JosmRuntimeException(tr("Could not find a layer for {0}", inputFile))); … … public class ValidatorCLI implements CLIModule { 267 269 } 268 270 } 269 271 } 270 Collection<Test> tests = OsmValidator.getEnabledTests(false);271 if ( Files.isRegularFile(Paths.get(outputFile)) && !Files.deleteIfExists(Paths.get(outputFile))) {272 Path path = Paths.get(outputFile); 273 if (path.toFile().isFile() && !Files.deleteIfExists(path)) { 272 274 Logging.error("Could not delete {0}, attempting to append", outputFile); 273 275 } 274 276 GeoJSONMapRouletteWriter geoJSONMapRouletteWriter = new GeoJSONMapRouletteWriter(dataSet); 275 try (OutputStream fileOutputStream = Files.newOutputStream(Paths.get(outputFile))) { 276 tests.parallelStream().forEach(test -> runTest(test, geoJSONMapRouletteWriter, fileOutputStream, dataSet)); 277 OsmValidator.initializeTests(); 278 279 try (OutputStream fileOutputStream = Files.newOutputStream(path)) { 280 // The first writeErrors catches anything that was written, for whatever reason. This is probably never 281 // going to be called. 282 ValidationTask validationTask = new ValidationTask(errors -> writeErrors(geoJSONMapRouletteWriter, fileOutputStream, errors), 283 progressMonitorFactory.get(), OsmValidator.getEnabledTests(false), 284 dataSet.allPrimitives(), Collections.emptyList(), false); 285 // This avoids keeping errors in memory 286 validationTask.setTestConsumer((t, test) -> { 287 writeErrors(geoJSONMapRouletteWriter, fileOutputStream, test.getErrors()); 288 t.getErrors().removeIf(test.getErrors()::contains); 289 }); 290 validationTask.run(); 277 291 } 278 292 } finally { 279 293 if (dataLayer != null) { … … public class ValidatorCLI implements CLIModule { 283 297 } 284 298 } 285 299 300 private void writeErrors(GeoJSONMapRouletteWriter geoJSONMapRouletteWriter, OutputStream fileOutputStream, 301 Collection<TestError> errors) { 302 for (TestError error : errors) { 303 Optional<JsonObject> object = geoJSONMapRouletteWriter.write(error); 304 if (object.isPresent()) { 305 try { 306 writeToFile(fileOutputStream, object.get().toString().getBytes(StandardCharsets.UTF_8)); 307 } catch (IOException e) { 308 throw new JosmRuntimeException(e); 309 } 310 } 311 } 312 } 313 286 314 /** 287 315 * Get the default output name 288 316 * @param inputString The input file … … public class ValidatorCLI implements CLIModule { 299 327 return FileNameUtils.getBaseName(FileNameUtils.getBaseName(inputString)) + ".geojson"; 300 328 } 301 329 302 /**303 * Run a test304 * @param test The test to run305 * @param geoJSONMapRouletteWriter The object to use to create challenges306 * @param fileOutputStream The location to write data to307 * @param dataSet The dataset to check308 */309 private void runTest(final Test test, final GeoJSONMapRouletteWriter geoJSONMapRouletteWriter,310 final OutputStream fileOutputStream, DataSet dataSet) {311 test.startTest(progressMonitorFactory.get());312 test.visit(dataSet.allPrimitives());313 test.endTest();314 test.getErrors().stream().map(geoJSONMapRouletteWriter::write)315 .filter(Optional::isPresent).map(Optional::get)316 .map(jsonObject -> jsonObject.toString().getBytes(StandardCharsets.UTF_8)).forEach(bytes -> {317 try {318 writeToFile(fileOutputStream, bytes);319 } catch (IOException e) {320 throw new JosmRuntimeException(e);321 }322 });323 test.clear();324 }325 326 330 /** 327 331 * Write to a file. Synchronized to avoid writing to the same file in different threads. 328 332 * -
src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java
diff --git a/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java b/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java index 9b4ffa7d9b..7718a4e272 100644
a b public class SaveLayersDialog extends JDialog implements TableModelListener { 553 553 for (final SaveLayerInfo layerInfo: toUpload) { 554 554 AbstractModifiableLayer layer = layerInfo.getLayer(); 555 555 if (canceled) { 556 model.setUploadState(layer, UploadOrSaveState.CANCELED);556 GuiHelper.runInEDTAndWait(() -> model.setUploadState(layer, UploadOrSaveState.CANCELED)); 557 557 continue; 558 558 } 559 monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName()));559 GuiHelper.runInEDTAndWait(() -> monitor.subTask(tr("Preparing layer ''{0}'' for upload ...", layerInfo.getName()))); 560 560 561 // checkPreUploadConditions must not be run in the EDT to avoid deadlocks 561 562 if (!UploadAction.checkPreUploadConditions(layer)) { 562 model.setUploadState(layer, UploadOrSaveState.FAILED);563 GuiHelper.runInEDTAndWait(() -> model.setUploadState(layer, UploadOrSaveState.FAILED)); 563 564 continue; 564 565 } 565 566 566 AbstractUploadDialog dialog = layer.getUploadDialog(); 567 if (dialog != null) { 568 dialog.setVisible(true); 569 if (dialog.isCanceled()) { 570 model.setUploadState(layer, UploadOrSaveState.CANCELED); 571 continue; 572 } 573 dialog.rememberUserInput(); 574 } 567 GuiHelper.runInEDTAndWait(() -> uploadLayersUploadModelStateOnFinish(layer)); 568 currentTask = null; 569 } 570 } 575 571 576 currentTask = layer.createUploadTask(monitor); 577 if (currentTask == null) { 578 model.setUploadState(layer, UploadOrSaveState.FAILED); 579 continue; 580 } 581 Future<?> currentFuture = worker.submit(currentTask); 582 try { 583 // wait for the asynchronous task to complete 584 currentFuture.get(); 585 } catch (CancellationException e) { 586 Logging.trace(e); 587 model.setUploadState(layer, UploadOrSaveState.CANCELED); 588 } catch (InterruptedException | ExecutionException e) { 589 Logging.error(e); 590 model.setUploadState(layer, UploadOrSaveState.FAILED); 591 ExceptionDialogUtil.explainException(e); 592 } 593 if (currentTask.isCanceled()) { 572 /** 573 * Update the {@link #model} state on upload finish 574 * @param layer The layer that has been saved 575 */ 576 private void uploadLayersUploadModelStateOnFinish(AbstractModifiableLayer layer) { 577 AbstractUploadDialog dialog = layer.getUploadDialog(); 578 if (dialog != null) { 579 dialog.setVisible(true); 580 if (dialog.isCanceled()) { 594 581 model.setUploadState(layer, UploadOrSaveState.CANCELED); 595 } else if (currentTask.isFailed()) { 596 Logging.error(currentTask.getLastException()); 597 ExceptionDialogUtil.explainException(currentTask.getLastException()); 598 model.setUploadState(layer, UploadOrSaveState.FAILED); 599 } else { 600 model.setUploadState(layer, UploadOrSaveState.OK); 582 return; 601 583 } 602 currentTask = null; 584 dialog.rememberUserInput(); 585 } 586 587 currentTask = layer.createUploadTask(monitor); 588 if (currentTask == null) { 589 model.setUploadState(layer, UploadOrSaveState.FAILED); 590 return; 591 } 592 Future<?> currentFuture = worker.submit(currentTask); 593 try { 594 // wait for the asynchronous task to complete 595 currentFuture.get(); 596 } catch (CancellationException e) { 597 Logging.trace(e); 598 model.setUploadState(layer, UploadOrSaveState.CANCELED); 599 } catch (InterruptedException | ExecutionException e) { 600 Logging.error(e); 601 model.setUploadState(layer, UploadOrSaveState.FAILED); 602 ExceptionDialogUtil.explainException(e); 603 } 604 if (currentTask.isCanceled()) { 605 model.setUploadState(layer, UploadOrSaveState.CANCELED); 606 } else if (currentTask.isFailed()) { 607 Logging.error(currentTask.getLastException()); 608 ExceptionDialogUtil.explainException(currentTask.getLastException()); 609 model.setUploadState(layer, UploadOrSaveState.FAILED); 610 } else { 611 model.setUploadState(layer, UploadOrSaveState.OK); 603 612 } 604 613 } 605 614 … … public class SaveLayersDialog extends JDialog implements TableModelListener { 669 678 670 679 @Override 671 680 public void run() { 681 GuiHelper.runInEDTAndWait(() -> model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING)); 682 // We very specifically do not want to block the EDT or the worker thread when validating 683 List<SaveLayerInfo> toUpload = model.getLayersToUpload(); 684 if (!toUpload.isEmpty()) { 685 uploadLayers(toUpload); 686 } 672 687 GuiHelper.runInEDTAndWait(() -> { 673 model.setMode(SaveLayersModel.Mode.UPLOADING_AND_SAVING);674 List<SaveLayerInfo> toUpload = model.getLayersToUpload();675 if (!toUpload.isEmpty()) {676 uploadLayers(toUpload);677 }678 688 List<SaveLayerInfo> toSave = model.getLayersToSave(); 679 689 if (!toSave.isEmpty()) { 680 690 saveLayers(toSave);