Index: trunk/src/org/openstreetmap/josm/actions/PurgeAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/PurgeAction.java	(revision 12687)
+++ trunk/src/org/openstreetmap/josm/actions/PurgeAction.java	(revision 12688)
@@ -13,7 +13,5 @@
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 import javax.swing.AbstractAction;
@@ -32,9 +30,5 @@
 import org.openstreetmap.josm.command.PurgeCommand;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -64,13 +58,4 @@
     protected boolean modified;
 
-    protected transient Set<OsmPrimitive> toPurge;
-    /**
-     * finally, contains all objects that are purged
-     */
-    protected transient Set<OsmPrimitive> toPurgeChecked;
-    /**
-     * Subset of toPurgeChecked. Marks primitives that remain in the dataset, but incomplete.
-     */
-    protected transient Set<OsmPrimitive> makeIncomplete;
     /**
      * Subset of toPurgeChecked. Those that have not been in the selection.
@@ -82,17 +67,8 @@
      */
     public PurgeAction() {
-        this(true);
-    }
-
-    /**
-     * Constructs a new {@code PurgeAction} with optional shortcut.
-     * @param addShortcut controls whether the shortcut should be registered or not, as for toolbar registration
-     * @since 11611
-     */
-    public PurgeAction(boolean addShortcut) {
         /* translator note: other expressions for "purge" might be "forget", "clean", "obliterate", "prune" */
-        super(tr("Purge..."), "purge", tr("Forget objects but do not delete them on server when uploading."), addShortcut ?
-                Shortcut.registerShortcut("system:purge", tr("Edit: {0}", tr("Purge")), KeyEvent.VK_P, Shortcut.CTRL_SHIFT)
-                : null, addShortcut);
+        super(tr("Purge..."), "purge", tr("Forget objects but do not delete them on server when uploading."),
+                Shortcut.registerShortcut("system:purge", tr("Edit: {0}", tr("Purge")), KeyEvent.VK_P, Shortcut.CTRL_SHIFT),
+                true);
         putValue("help", HelpUtil.ht("/Action/Purge"));
     }
@@ -141,110 +117,8 @@
     public PurgeCommand getPurgeCommand(Collection<OsmPrimitive> sel) {
         layer = getLayerManager().getEditLayer();
-
-        toPurge = new HashSet<>(sel);
         toPurgeAdditionally = new ArrayList<>();
-        toPurgeChecked = new HashSet<>();
-
-        // Add referrer, unless the object to purge is not new and the parent is a relation
-        Set<OsmPrimitive> toPurgeRecursive = new HashSet<>();
-        while (!toPurge.isEmpty()) {
-
-            for (OsmPrimitive osm: toPurge) {
-                for (OsmPrimitive parent: osm.getReferrers()) {
-                    if (toPurge.contains(parent) || toPurgeChecked.contains(parent) || toPurgeRecursive.contains(parent)) {
-                        continue;
-                    }
-                    if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) {
-                        toPurgeAdditionally.add(parent);
-                        toPurgeRecursive.add(parent);
-                    }
-                }
-                toPurgeChecked.add(osm);
-            }
-            toPurge = toPurgeRecursive;
-            toPurgeRecursive = new HashSet<>();
-        }
-
-        makeIncomplete = new HashSet<>();
-
-        // Find the objects that will be incomplete after purging.
-        // At this point, all parents of new to-be-purged primitives are
-        // also to-be-purged and
-        // all parents of not-new to-be-purged primitives are either
-        // to-be-purged or of type relation.
-        TOP:
-            for (OsmPrimitive child : toPurgeChecked) {
-                if (child.isNew()) {
-                    continue;
-                }
-                for (OsmPrimitive parent : child.getReferrers()) {
-                    if (parent instanceof Relation && !toPurgeChecked.contains(parent)) {
-                        makeIncomplete.add(child);
-                        continue TOP;
-                    }
-                }
-            }
-
-        // Add untagged way nodes. Do not add nodes that have other referrers not yet to-be-purged.
-        if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) {
-            Set<OsmPrimitive> wayNodes = new HashSet<>();
-            for (OsmPrimitive osm : toPurgeChecked) {
-                if (osm instanceof Way) {
-                    Way w = (Way) osm;
-                    NODE:
-                        for (Node n : w.getNodes()) {
-                            if (n.isTagged() || toPurgeChecked.contains(n)) {
-                                continue;
-                            }
-                            for (OsmPrimitive ref : n.getReferrers()) {
-                                if (ref != w && !toPurgeChecked.contains(ref)) {
-                                    continue NODE;
-                                }
-                            }
-                            wayNodes.add(n);
-                        }
-                }
-            }
-            toPurgeChecked.addAll(wayNodes);
-            toPurgeAdditionally.addAll(wayNodes);
-        }
-
-        if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) {
-            Set<Relation> relSet = new HashSet<>();
-            for (OsmPrimitive osm : toPurgeChecked) {
-                for (OsmPrimitive parent : osm.getReferrers()) {
-                    if (parent instanceof Relation
-                            && !(toPurgeChecked.contains(parent))
-                            && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) {
-                        relSet.add((Relation) parent);
-                    }
-                }
-            }
-
-            // Add higher level relations (list gets extended while looping over it)
-            List<Relation> relLst = new ArrayList<>(relSet);
-            for (int i = 0; i < relLst.size(); ++i) { // foreach loop not applicable since list gets extended while looping over it
-                for (OsmPrimitive parent : relLst.get(i).getReferrers()) {
-                    if (!(toPurgeChecked.contains(parent))
-                            && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) {
-                        relLst.add((Relation) parent);
-                    }
-                }
-            }
-            relSet = new HashSet<>(relLst);
-            toPurgeChecked.addAll(relSet);
-            toPurgeAdditionally.addAll(relSet);
-        }
-
-        modified = false;
-        for (OsmPrimitive osm : toPurgeChecked) {
-            if (osm.isModified()) {
-                modified = true;
-                break;
-            }
-        }
-
-        return layer != null ? new PurgeCommand(layer, toPurgeChecked, makeIncomplete) :
-            new PurgeCommand(toPurgeChecked.iterator().next().getDataSet(), toPurgeChecked, makeIncomplete);
+        PurgeCommand cmd = PurgeCommand.build(layer, sel, toPurgeAdditionally);
+        modified = cmd.getParticipatingPrimitives().stream().anyMatch(OsmPrimitive::isModified);
+        return cmd;
     }
 
@@ -323,12 +197,3 @@
         setEnabled(selection != null && !selection.isEmpty());
     }
-
-    private static boolean hasOnlyIncompleteMembers(
-            Relation r, Collection<OsmPrimitive> toPurge, Collection<? extends OsmPrimitive> moreToPurge) {
-        for (RelationMember m : r.getMembers()) {
-            if (!m.getMember().isIncomplete() && !toPurge.contains(m.getMember()) && !moreToPurge.contains(m.getMember()))
-                return false;
-        }
-        return true;
-    }
 }
Index: trunk/src/org/openstreetmap/josm/command/PurgeCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/PurgeCommand.java	(revision 12687)
+++ trunk/src/org/openstreetmap/josm/command/PurgeCommand.java	(revision 12688)
@@ -16,4 +16,5 @@
 import javax.swing.Icon;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.conflict.Conflict;
 import org.openstreetmap.josm.data.conflict.ConflictCollection;
@@ -26,4 +27,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationData;
+import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Storage;
 import org.openstreetmap.josm.data.osm.Way;
@@ -309,3 +311,127 @@
                 Objects.equals(purgedConflicts, that.purgedConflicts);
     }
+
+    /**
+     * Creates a new {@code PurgeCommand} to purge selected OSM primitives.
+     * @param layer optional osm data layer, can be null
+     * @param sel selected OSM primitives
+     * @param toPurgeAdditionally optional list that will be filled with primitives to be purged that have not been in the selection
+     * @return command to purge selected OSM primitives
+     * @since 12688
+     */
+    public static PurgeCommand build(OsmDataLayer layer, Collection<OsmPrimitive> sel, List<OsmPrimitive> toPurgeAdditionally) {
+        Set<OsmPrimitive> toPurge = new HashSet<>(sel);
+        // finally, contains all objects that are purged
+        Set<OsmPrimitive> toPurgeChecked = new HashSet<>();
+
+        // Add referrer, unless the object to purge is not new and the parent is a relation
+        Set<OsmPrimitive> toPurgeRecursive = new HashSet<>();
+        while (!toPurge.isEmpty()) {
+
+            for (OsmPrimitive osm: toPurge) {
+                for (OsmPrimitive parent: osm.getReferrers()) {
+                    if (toPurge.contains(parent) || toPurgeChecked.contains(parent) || toPurgeRecursive.contains(parent)) {
+                        continue;
+                    }
+                    if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) {
+                        if (toPurgeAdditionally != null) {
+                            toPurgeAdditionally.add(parent);
+                        }
+                        toPurgeRecursive.add(parent);
+                    }
+                }
+                toPurgeChecked.add(osm);
+            }
+            toPurge = toPurgeRecursive;
+            toPurgeRecursive = new HashSet<>();
+        }
+
+        // Subset of toPurgeChecked. Marks primitives that remain in the dataset, but incomplete.
+        Set<OsmPrimitive> makeIncomplete = new HashSet<>();
+
+        // Find the objects that will be incomplete after purging.
+        // At this point, all parents of new to-be-purged primitives are
+        // also to-be-purged and
+        // all parents of not-new to-be-purged primitives are either
+        // to-be-purged or of type relation.
+        TOP:
+            for (OsmPrimitive child : toPurgeChecked) {
+                if (child.isNew()) {
+                    continue;
+                }
+                for (OsmPrimitive parent : child.getReferrers()) {
+                    if (parent instanceof Relation && !toPurgeChecked.contains(parent)) {
+                        makeIncomplete.add(child);
+                        continue TOP;
+                    }
+                }
+            }
+
+        // Add untagged way nodes. Do not add nodes that have other referrers not yet to-be-purged.
+        if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) {
+            Set<OsmPrimitive> wayNodes = new HashSet<>();
+            for (OsmPrimitive osm : toPurgeChecked) {
+                if (osm instanceof Way) {
+                    Way w = (Way) osm;
+                    NODE:
+                        for (Node n : w.getNodes()) {
+                            if (n.isTagged() || toPurgeChecked.contains(n)) {
+                                continue;
+                            }
+                            for (OsmPrimitive ref : n.getReferrers()) {
+                                if (ref != w && !toPurgeChecked.contains(ref)) {
+                                    continue NODE;
+                                }
+                            }
+                            wayNodes.add(n);
+                        }
+                }
+            }
+            toPurgeChecked.addAll(wayNodes);
+            if (toPurgeAdditionally != null) {
+                toPurgeAdditionally.addAll(wayNodes);
+            }
+        }
+
+        if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) {
+            Set<Relation> relSet = new HashSet<>();
+            for (OsmPrimitive osm : toPurgeChecked) {
+                for (OsmPrimitive parent : osm.getReferrers()) {
+                    if (parent instanceof Relation
+                            && !(toPurgeChecked.contains(parent))
+                            && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) {
+                        relSet.add((Relation) parent);
+                    }
+                }
+            }
+
+            // Add higher level relations (list gets extended while looping over it)
+            List<Relation> relLst = new ArrayList<>(relSet);
+            for (int i = 0; i < relLst.size(); ++i) { // foreach loop not applicable since list gets extended while looping over it
+                for (OsmPrimitive parent : relLst.get(i).getReferrers()) {
+                    if (!(toPurgeChecked.contains(parent))
+                            && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) {
+                        relLst.add((Relation) parent);
+                    }
+                }
+            }
+            relSet = new HashSet<>(relLst);
+            toPurgeChecked.addAll(relSet);
+            if (toPurgeAdditionally != null) {
+                toPurgeAdditionally.addAll(relSet);
+            }
+        }
+
+        return layer != null ? new PurgeCommand(layer, toPurgeChecked, makeIncomplete)
+                : new PurgeCommand(toPurgeChecked.iterator().next().getDataSet(), toPurgeChecked, makeIncomplete);
+    }
+
+    private static boolean hasOnlyIncompleteMembers(
+            Relation r, Collection<OsmPrimitive> toPurge, Collection<? extends OsmPrimitive> moreToPurge) {
+        for (RelationMember m : r.getMembers()) {
+            if (!m.getMember().isIncomplete() && !toPurge.contains(m.getMember()) && !moreToPurge.contains(m.getMember()))
+                return false;
+        }
+        return true;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(revision 12687)
+++ trunk/src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(revision 12688)
@@ -21,5 +21,5 @@
 import org.openstreetmap.josm.actions.JoinAreasAction.JoinAreasResult;
 import org.openstreetmap.josm.actions.JoinAreasAction.Multipolygon;
-import org.openstreetmap.josm.actions.PurgeAction;
+import org.openstreetmap.josm.command.PurgeCommand;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -104,5 +104,5 @@
         }
         // Purge all other ways and relations so dataset only contains lefthand traffic data
-        new PurgeAction(false).getPurgeCommand(toPurge).executeCommand();
+        PurgeCommand.build(null, toPurge, null).executeCommand();
         // Combine adjacent countries into a single polygon
         Collection<Way> optimizedWays = new ArrayList<>();
