Subject: [PATCH] Fix #22808: Undoing "Paste" for ways of a route relation is very slow
---
Index: src/org/openstreetmap/josm/command/AddPrimitivesCommand.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/command/AddPrimitivesCommand.java b/src/org/openstreetmap/josm/command/AddPrimitivesCommand.java
--- a/src/org/openstreetmap/josm/command/AddPrimitivesCommand.java	(revision 18715)
+++ b/src/org/openstreetmap/josm/command/AddPrimitivesCommand.java	(date 1682440147616)
@@ -17,6 +17,7 @@
 import org.openstreetmap.josm.data.osm.NodeData;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.PrimitiveData;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 
 /**
@@ -127,16 +128,20 @@
             createdPrimitives = PurgeCommand.topoSort(createdPrimitives);
         }
         // reversed order, see #14620
-        for (int i = createdPrimitives.size() - 1; i >= 0; i--) {
-            OsmPrimitive osm = createdPrimitives.get(i);
-            Optional<PrimitiveData> previous = preExistingData.stream()
-                    .filter(pd -> pd.getPrimitiveId().equals(osm.getPrimitiveId())).findAny();
-            if (previous.isPresent()) {
-                osm.load(previous.get());
-            } else {
-                ds.removePrimitive(osm);
-            }
-        }
+        final List<PrimitiveId> toRemove = new ArrayList<>(this.createdPrimitives.size());
+        ds.update(() -> {
+                    for (int i = createdPrimitives.size() - 1; i >= 0; i--) {
+                        OsmPrimitive osm = createdPrimitives.get(i);
+                        Optional<PrimitiveData> previous = preExistingData.stream()
+                                .filter(pd -> pd.getPrimitiveId().equals(osm.getPrimitiveId())).findAny();
+                        if (previous.isPresent()) {
+                            osm.load(previous.get());
+                        } else {
+                            toRemove.add(osm);
+                        }
+                    }
+                });
+        ds.removePrimitives(toRemove);
     }
 
     @Override
Index: src/org/openstreetmap/josm/data/osm/DataSet.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/org/openstreetmap/josm/data/osm/DataSet.java b/src/org/openstreetmap/josm/data/osm/DataSet.java
--- a/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 18715)
+++ b/src/org/openstreetmap/josm/data/osm/DataSet.java	(date 1682440452192)
@@ -5,6 +5,7 @@
 
 import java.awt.geom.Area;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -564,11 +565,65 @@
         });
     }
 
+    /**
+     * Removes primitives from the dataset. This method only removes the
+     * primitives form the respective collection of primitives managed
+     * by this dataset, i.e. from {@code store.nodes}, {@code store.ways}, or
+     * {@code store.relations}. References from other primitives to this
+     * primitive are left unchanged.
+     *
+     * @param primitiveIds the ids of the primitive
+     * @throws IllegalStateException if the dataset is read-only
+     * @since xxx
+     */
+    public void removePrimitives(PrimitiveId... primitiveIds) {
+        this.removePrimitives(Arrays.asList(primitiveIds));
+    }
+
+    /**
+     * Removes primitives from the dataset. This method only removes the
+     * primitives form the respective collection of primitives managed
+     * by this dataset, i.e. from {@code store.nodes}, {@code store.ways}, or
+     * {@code store.relations}. References from other primitives to this
+     * primitive are left unchanged.
+     *
+     * @param primitiveIds the ids of the primitive
+     * @throws IllegalStateException if the dataset is read-only
+     * @since xxx
+     */
+    public void removePrimitives(Collection<PrimitiveId> primitiveIds) {
+        checkModifiable();
+        final List<PrimitiveId> selected = new ArrayList<>();
+        update(() -> {
+            clearSelection(primitiveIds);
+            final List<OsmPrimitive> removed = new ArrayList<>(primitiveIds.size());
+            for (PrimitiveId primitiveId : primitiveIds) {
+                OsmPrimitive primitive = this.getPrimitiveByIdChecked(primitiveId);
+                if (primitive == null) {
+                    continue;
+                } else if (primitive.isSelected()) {
+                    selected.add(primitive);
+                }
+                this.removePrimitiveFromStorage(primitive);
+                removed.add(primitive);
+            }
+            firePrimitivesRemoved(removed, false);
+        });
+        if (!selected.isEmpty()) {
+            throw new DataIntegrityProblemException("Primitives were re-selected by a selection listener: "
+            + selected.stream().map(PrimitiveId::toString).collect(Collectors.joining(", ")));
+        }
+    }
+
     private void removePrimitiveImpl(OsmPrimitive primitive) {
         clearSelection(primitive.getPrimitiveId());
         if (primitive.isSelected()) {
             throw new DataIntegrityProblemException("Primitive was re-selected by a selection listener: " + primitive);
         }
+        this.removePrimitiveFromStorage(primitive);
+    }
+
+    private void removePrimitiveFromStorage(OsmPrimitive primitive) {
         store.removePrimitive(primitive);
         allPrimitives.remove(primitive);
         primitive.setDataset(null);
