Ticket #23399: 23399.2.patch
| File 23399.2.patch, 7.8 KB (added by , 2 years ago) |
|---|
-
core/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
Subject: [PATCH] Fix #23399: Simplify way crashes This was primarily caused by selection updates occurring while the commands were generated. The fixes could be any of the following: * Synchronized collections * New list * Avoiding selection updates The last option was taken for the following reasons: * Avoiding a StackOverflow exception when many ways are being simplified * Reduced memory allocations (>11GB to <250MB) * Reduced CPU cycles --- IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/core/src/org/openstreetmap/josm/actions/SimplifyWayAction.java b/core/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
a b 56 56 import org.openstreetmap.josm.tools.GBC; 57 57 import org.openstreetmap.josm.tools.ImageProvider; 58 58 import org.openstreetmap.josm.tools.Shortcut; 59 import org.openstreetmap.josm.tools.StreamUtils;60 59 61 60 /** 62 61 * Delete unnecessary nodes from a way … … 340 339 * @since 16566 (private) 341 340 */ 342 341 private static SequenceCommand buildSimplifyWaysCommand(List<Way> ways, double threshold) { 343 Collection<Command> allCommands = ways.stream() 344 .map(way -> createSimplifyCommand(way, threshold)) 345 .filter(Objects::nonNull) 346 .collect(StreamUtils.toUnmodifiableList()); 342 List<Command> allCommands = new ArrayList<>(ways.size()); 343 for (Way way : ways) { 344 Command command = createSimplifyCommand(way, threshold, false); 345 if (command != null) { 346 allCommands.add(command); 347 } 348 } 347 349 if (allCommands.isEmpty()) 348 350 return null; 351 final List<OsmPrimitive> deletedPrimitives = allCommands.stream() 352 .map(Command::getChildren) 353 .flatMap(Collection::stream) 354 .filter(DeleteCommand.class::isInstance) 355 .map(DeleteCommand.class::cast) 356 .map(DeleteCommand::getParticipatingPrimitives) 357 .flatMap(Collection::stream) 358 .collect(Collectors.toList()); 359 allCommands.get(0).getAffectedDataSet().clearSelection(deletedPrimitives); 349 360 return new SequenceCommand( 350 361 trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()), 351 allCommands);362 Collections.unmodifiableList(allCommands)); 352 363 } 353 364 354 365 /** … … 371 382 * @since 15419 372 383 */ 373 384 public static SequenceCommand createSimplifyCommand(Way w, double threshold) { 385 return createSimplifyCommand(w, threshold, true); 386 } 387 388 /** 389 * Creates the SequenceCommand to simplify a way with a given threshold. 390 * 391 * @param w the way to simplify 392 * @param threshold the max error threshold 393 * @param deselect {@code true} if we want to deselect the deleted nodes 394 * @return The sequence of commands to run 395 */ 396 private static SequenceCommand createSimplifyCommand(Way w, double threshold, boolean deselect) { 374 397 int lower = 0; 375 398 int i = 0; 376 399 … … 417 440 Collection<Command> cmds = new LinkedList<>(); 418 441 cmds.add(new ChangeNodesCommand(w, newNodes)); 419 442 cmds.add(new DeleteCommand(w.getDataSet(), delNodes)); 420 w.getDataSet().clearSelection(delNodes); 443 if (deselect) { 444 w.getDataSet().clearSelection(delNodes); 445 } 421 446 return new SequenceCommand( 422 447 trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds); 423 448 } … … 564 589 565 590 @Override 566 591 public void selectionChanged(SelectionChangeEvent event) { 567 updateWayList(event.getSource());592 if (false) updateWayList(event.getSource()); 568 593 } 569 594 570 595 void updateWayList(DataSet dataSet) { -
core/test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/core/test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java b/core/test/unit/org/openstreetmap/josm/actions/SimplifyWayActionTest.java
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.actions; 3 3 4 import static org.junit.jupiter.api.Assertions.assertAll; 5 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 6 import static org.junit.jupiter.api.Assertions.assertEquals; 7 import static org.junit.jupiter.api.Assertions.assertFalse; 5 8 import static org.junit.jupiter.api.Assertions.assertNotNull; 9 import static org.junit.jupiter.api.Assumptions.assumeTrue; 6 10 11 import java.awt.GraphicsEnvironment; 7 12 import java.io.IOException; 8 13 import java.nio.file.Files; 9 14 import java.nio.file.Paths; … … 24 29 import org.openstreetmap.josm.data.osm.DataSet; 25 30 import org.openstreetmap.josm.data.osm.Node; 26 31 import org.openstreetmap.josm.data.osm.Way; 32 import org.openstreetmap.josm.gui.ExtendedDialog; 27 33 import org.openstreetmap.josm.gui.MainApplication; 34 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 35 import org.openstreetmap.josm.gui.util.GuiHelper; 28 36 import org.openstreetmap.josm.io.IllegalDataException; 29 37 import org.openstreetmap.josm.io.OsmReader; 30 38 import org.openstreetmap.josm.testutils.annotations.Main; 31 39 import org.openstreetmap.josm.testutils.annotations.Projection; 40 import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker; 41 import org.openstreetmap.josm.testutils.mockers.HelpAwareOptionPaneMocker; 32 42 import org.openstreetmap.josm.tools.Utils; 33 43 34 44 /** … … 99 109 assertEquals(1, deleteCommands.size()); 100 110 assertEquals(Collections.singleton(n1), deleteCommands.iterator().next().getParticipatingPrimitives()); 101 111 } 112 113 /** 114 * Non-regression test for #23399 115 */ 116 @Test 117 void testNonRegression23399() { 118 TestUtils.assumeWorkingJMockit(); 119 new ExtendedDialogMocker(Collections.singletonMap("Simplify way", "Simplify")) { 120 @Override 121 protected String getString(ExtendedDialog instance) { 122 return instance.getTitle(); 123 } 124 }; 125 new HelpAwareOptionPaneMocker(Collections.singletonMap("The selection contains 1 000 ways. Are you sure you want to simplify them all?", "Yes")); 126 final ArrayList<Way> ways = new ArrayList<>(1000); 127 final DataSet ds = new DataSet(); 128 for (int i = 0; i < 1000; i++) { 129 final Way way = TestUtils.newWay("", new Node(new LatLon(0, 0)), new Node(new LatLon(0, 0.001)), 130 new Node(new LatLon(0, 0.002))); 131 ways.add(way); 132 ds.addPrimitiveRecursive(way); 133 } 134 MainApplication.getLayerManager().addLayer(new OsmDataLayer(ds, "SimplifyWayActionTest#testNonRegression23399", null)); 135 GuiHelper.runInEDTAndWait(() -> ds.setSelected(ds.allPrimitives())); 136 assertEquals(ds.allPrimitives().size(), ds.getAllSelected().size()); 137 assertDoesNotThrow(() -> GuiHelper.runInEDTAndWaitWithException(() -> action.actionPerformed(null))); 138 assertAll(ways.stream().map(way -> () -> assertEquals(2, way.getNodesCount()))); 139 assertAll(ds.getAllSelected().stream().map(p -> () -> assertFalse(p.isDeleted()))); 140 assertEquals(3000, ds.getAllSelected().size()); 141 } 102 142 }
