Index: .classpath
===================================================================
--- .classpath	(revision 16248)
+++ .classpath	(working copy)
@@ -27,7 +27,7 @@
 			<attribute name="test" value="true"/>
 		</attributes>
 	</classpathentry>
-	<classpathentry kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=JOSM&amp;ivyXmlPath=ivy.xml&amp;confs=*&amp;ivySettingsPath=ivysettings.xml&amp;loadSettingsOnDemand=false&amp;ivyUserDir=&amp;propertyFiles="/>
+	<classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=JOSM&amp;ivyXmlPath=ivy.xml&amp;confs=*&amp;ivySettingsPath=ivysettings.xml&amp;loadSettingsOnDemand=false&amp;ivyUserDir=&amp;propertyFiles="/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
 		<attributes>
 			<attribute name="module" value="true"/>
Index: josm-latest.jnlp
===================================================================
--- josm-latest.jnlp	(revision 16248)
+++ josm-latest.jnlp	(working copy)
@@ -3,8 +3,8 @@
 <jnlp spec="6.0+" codebase="https://josm.openstreetmap.de/download/" href="josm-latest.jnlp">
     <information>
         <title>JOSM (development version)</title>
-        <vendor>OpenStreetMap</vendor> 
-        <homepage href="https://josm.openstreetmap.de"/> 
+        <vendor>OpenStreetMap</vendor>
+        <homepage href="https://josm.openstreetmap.de"/>
         <description>Java OpenStreetMap editor</description>
         <description kind="one-line">JOSM</description>
         <description kind="tooltip">JOSM</description>
@@ -20,7 +20,7 @@
         <all-permissions/>
     </security>
     <resources>
-        <java version="1.8+" max-heap-size="2048m" java-vm-args="--add-modules=java.scripting,java.sql --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
+        <java version="1.8+" max-heap-size="2048m" java-vm-args="--add-modules=java.scripting,java.sql --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED --add-exports=javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports=jdk.deploy/com.sun.deploy.config=ALL-UNNAMED --add-opens=jdk.swing.interop=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.desktop/javax.imageio.spi=ALL-UNNAMED --add-exports=java.desktop/com.sun.imageio.plugins.jpeg=ALL-UNNAMED --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED"/>
         <jar href="josm-latest.jar"/>
         <property name="java.util.Arrays.useLegacyMergeSort" value="true"/>
     </resources>
Index: src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 16248)
+++ src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(working copy)
@@ -11,6 +11,7 @@
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.awt.geom.Point2D;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -32,7 +33,9 @@
 import org.openstreetmap.josm.data.UndoRedoHandler;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.IWay;
 import org.openstreetmap.josm.data.osm.OsmData;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
@@ -183,7 +186,7 @@
      * to remove the highlight from them again as otherwise the whole data
      * set would have to be checked.
      */
-    private transient Optional<OsmPrimitive> currentHighlight = Optional.empty();
+    private transient Optional<IPrimitive> currentHighlight = Optional.empty();
 
     /**
      * Create a new SelectAction
@@ -257,13 +260,13 @@
      * @return {@code true} if repaint is required
      */
     private boolean giveUserFeedback(MouseEvent e, int modifiers) {
-        Optional<OsmPrimitive> c = Optional.ofNullable(
+        Optional<IPrimitive> c = Optional.ofNullable(
                 mv.getNearestNodeOrWay(e.getPoint(), mv.isSelectablePredicate, true));
 
         updateKeyModifiersEx(modifiers);
         determineMapMode(c.isPresent());
 
-        Optional<OsmPrimitive> newHighlight = Optional.empty();
+        Optional<IPrimitive> newHighlight = Optional.empty();
 
         virtualManager.clear();
         if (mode == Mode.MOVE && !dragInProgress() && virtualManager.activateVirtualNodeNearPoint(e.getPoint())) {
@@ -300,7 +303,7 @@
      * @param nearbyStuff primitives near the cursor
      * @return the cursor that should be displayed
      */
-    private Cursor getCursor(OsmPrimitive nearbyStuff) {
+    private Cursor getCursor(IPrimitive nearbyStuff) {
         String c = "rect";
         switch(mode) {
         case MOVE:
@@ -308,7 +311,7 @@
                 c = "virtual_node";
                 break;
             }
-            final OsmPrimitive osm = nearbyStuff;
+            final IPrimitive osm = nearbyStuff;
 
             if (dragInProgress()) {
                 // only consider merge if ctrl is pressed and there are nodes in
@@ -319,13 +322,13 @@
                 }
                 // only show merge to node cursor if nearby node and that node is currently
                 // not being dragged
-                final boolean hasTarget = osm instanceof Node && !osm.isSelected();
+                final boolean hasTarget = osm instanceof INode && !osm.isSelected();
                 c = hasTarget ? "merge_to_node" : "merge";
                 break;
             }
 
-            c = (osm instanceof Node) ? "node" : c;
-            c = (osm instanceof Way) ? "way" : c;
+            c = (osm instanceof INode) ? "node" : c;
+            c = (osm instanceof IWay) ? "way" : c;
             if (shift) {
                 c += "_add";
             } else if (ctrl) {
@@ -369,7 +372,7 @@
         return true;
     }
 
-    private boolean repaintIfRequired(Optional<OsmPrimitive> newHighlight) {
+    private boolean repaintIfRequired(Optional<IPrimitive> newHighlight) {
         if (!drawTargetHighlight || currentHighlight.equals(newHighlight))
             return false;
         currentHighlight.ifPresent(osm -> osm.setHighlighted(false));
@@ -413,7 +416,7 @@
 
         // primitives under cursor are stored in c collection
 
-        OsmPrimitive nearestPrimitive = mv.getNearestNodeOrWay(e.getPoint(), mv.isSelectablePredicate, true);
+        IPrimitive nearestPrimitive = mv.getNearestNodeOrWay(e.getPoint(), mv.isSelectablePredicate, true);
 
         determineMapMode(nearestPrimitive != null);
 
@@ -437,7 +440,7 @@
             if (!cancelDrawMode && nearestPrimitive instanceof Way) {
                 virtualManager.activateVirtualNodeNearPoint(e.getPoint());
             }
-            OsmPrimitive toSelect = cycleManager.cycleSetup(nearestPrimitive, e.getPoint());
+            IPrimitive toSelect = cycleManager.cycleSetup(nearestPrimitive, e.getPoint());
             selectPrims(asColl(toSelect), false, false);
             useLastMoveCommandIfPossible();
             // Schedule a timer to update status line "initialMoveDelay+1" ms in the future
@@ -510,7 +513,7 @@
             // If ctrl is pressed we are in merge mode. Look for a nearby node,
             // highlight it and adjust the cursor accordingly.
             final boolean canMerge = ctrl && !getLayerManager().getEditDataSet().getSelectedNodes().isEmpty();
-            final OsmPrimitive p = canMerge ? findNodeToMergeTo(e.getPoint()) : null;
+            final IPrimitive p = canMerge ? findNodeToMergeTo(e.getPoint()) : null;
             boolean needsRepaint = removeHighlighting();
             if (p != null) {
                 p.setHighlighted(true);
@@ -603,8 +606,8 @@
                     selectPrims(cycleManager.cyclePrims(), true, false);
 
                     // If the user double-clicked a node, change to draw mode
-                    Collection<OsmPrimitive> c = ds.getSelected();
-                    if (e.getClickCount() >= 2 && c.size() == 1 && c.iterator().next() instanceof Node) {
+                    Collection<IPrimitive> c = new ArrayList<>(ds.getSelected());
+                    if (e.getClickCount() >= 2 && c.size() == 1 && c.iterator().next() instanceof INode) {
                         // We need to do it like this as otherwise drawAction will see a double
                         // click and switch back to SelectMode
                         MainApplication.worker.execute(() -> map.selectDrawTool(true));
@@ -698,12 +701,12 @@
         // Currently we support only transformations which do not affect relations.
         // So don't add them in the first place to make handling easier
         DataSet ds = getLayerManager().getEditDataSet();
-        Collection<OsmPrimitive> selection = ds.getSelectedNodesAndWays();
+        Collection<IPrimitive> selection = new ArrayList<>(ds.getSelectedNodesAndWays());
         if (selection.isEmpty()) { // if nothing was selected to drag, just select nearest node/way to the cursor
             ds.setSelected(mv.getNearestNodeOrWay(mv.getPoint(startEN), mv.isSelectablePredicate, true));
         }
 
-        Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(selection);
+        Collection<INode> affectedNodes = new ArrayList<>(AllNodesVisitor.getAllNodes(selection));
         // for these transformations, having only one node makes no sense - quit silently
         if (affectedNodes.size() < 2 && (mode == Mode.ROTATE || mode == Mode.SCALE)) {
             return false;
@@ -721,7 +724,7 @@
                     moveCmd = new MoveCommand(selection, startEN, currentEN);
                     UndoRedoHandler.getInstance().add(moveCmd);
                 }
-                for (Node n : affectedNodes) {
+                for (INode n : affectedNodes) {
                     if (n.isOutSideWorld()) {
                         // Revert move
                         if (moveCmd != null) {
@@ -770,9 +773,9 @@
         }
     }
 
-    private static boolean doesImpactStatusLine(Collection<Node> affectedNodes, Collection<Way> selectedWays) {
-        for (Way w : selectedWays) {
-            for (Node n : w.getNodes()) {
+    private static boolean doesImpactStatusLine(Collection<INode> affectedNodes, Collection<IWay<?>> selectedWays) {
+        for (IWay<?> w : selectedWays) {
+            for (INode n : w.getNodes()) {
                 if (affectedNodes.contains(n)) {
                     return true;
                 }
@@ -791,7 +794,7 @@
             return;
         }
         Command c = getLastCommandInDataset(dataSet);
-        Collection<Node> affectedNodes = AllNodesVisitor.getAllNodes(dataSet.getSelected());
+        Collection<? extends INode> affectedNodes = AllNodesVisitor.getAllNodes(dataSet.getSelected());
         if (c instanceof MoveCommand && affectedNodes.equals(((MoveCommand) c).getParticipatingPrimitives())) {
             // old command was created with different base point of movement, we need to recalculate it
             ((MoveCommand) c).changeStartPoint(startEN);
@@ -830,13 +833,13 @@
             ed.toggleEnable("movedHiddenElements");
             showConfirmMoveDialog(ed);
         }
-        Set<Node> nodes = new HashSet<>();
+        Set<INode> nodes = new HashSet<>();
         int max = Config.getPref().getInt("warn.move.maxelements", 20);
-        for (OsmPrimitive osm : getLayerManager().getEditDataSet().getSelected()) {
-            if (osm instanceof Way) {
-                nodes.addAll(((Way) osm).getNodes());
-            } else if (osm instanceof Node) {
-                nodes.add((Node) osm);
+        for (IPrimitive osm : getLayerManager().getEditDataSet().getSelected()) {
+            if (osm instanceof IWay) {
+                nodes.addAll(((IWay<?>) osm).getNodes());
+            } else if (osm instanceof INode) {
+                nodes.add((INode) osm);
             }
             if (nodes.size() > max) {
                 break;
@@ -929,8 +932,8 @@
      * @param nodes the collection of nodes. Ignored if null
      * @param targetLocationNode this node's location will be used for the target node
      */
-    public void mergeNodes(OsmDataLayer layer, Collection<Node> nodes,
-                           Node targetLocationNode) {
+    public void mergeNodes(OsmDataLayer layer, Collection<INode> nodes,
+                           INode targetLocationNode) {
         MergeNodesAction.doMergeNodes(layer, nodes, targetLocationNode);
     }
 
@@ -940,15 +943,15 @@
      * @param p mouse position
      * @return node to merge to, or null
      */
-    private Node findNodeToMergeTo(Point p) {
-        Collection<Node> target = mv.getNearestNodes(p,
-                getLayerManager().getEditDataSet().getSelectedNodes(),
+    private INode findNodeToMergeTo(Point p) {
+        Collection<INode> target = mv.getNearestNodes(p,
+                new ArrayList<>(getLayerManager().getEditDataSet().getSelectedNodes()),
                 mv.isSelectablePredicate);
         return target.isEmpty() ? null : target.iterator().next();
     }
 
-    private void selectPrims(Collection<OsmPrimitive> prims, boolean released, boolean area) {
-        DataSet ds = getLayerManager().getActiveDataSet();
+    private void selectPrims(Collection<IPrimitive> prims, boolean released, boolean area) {
+        OsmData<?, ?, ?, ?> ds = getLayerManager().getActiveData();
 
         // not allowed together: do not change dataset selection, return early
         // Virtual Ways: if non-empty the cursor is above a virtual node. So don't highlight
@@ -1029,9 +1032,9 @@
 
     private class CycleManager {
 
-        private Collection<OsmPrimitive> cycleList = Collections.emptyList();
+        private Collection<IPrimitive> cycleList = Collections.emptyList();
         private boolean cyclePrims;
-        private OsmPrimitive cycleStart;
+        private IPrimitive cycleStart;
         private boolean waitForMouseUpParameter;
         private boolean multipleMatchesParameter;
         /**
@@ -1105,7 +1108,7 @@
          * <code>cycleList</code> field
          * @return the next element of cycle list
          */
-        private Collection<OsmPrimitive> cyclePrims() {
+        private Collection<IPrimitive> cyclePrims() {
             if (cycleList.size() <= 1) {
                 // no real cycling, just return one-element collection with nearest primitive in it
                 return cycleList;
Index: src/org/openstreetmap/josm/command/DeleteCommand.java
===================================================================
--- src/org/openstreetmap/josm/command/DeleteCommand.java	(revision 16248)
+++ src/org/openstreetmap/josm/command/DeleteCommand.java	(working copy)
@@ -478,7 +478,7 @@
             cmds.add(new DeleteCommand(primitivesToDelete.iterator().next().getDataSet(), primitivesToDelete));
         }
 
-        return new SequenceCommand(tr("Delete"), cmds);
+        return SequenceCommand.createSimplifiedSequenceCommand(tr("Delete"), cmds);
     }
 
     /**
Index: src/org/openstreetmap/josm/command/SequenceCommand.java
===================================================================
--- src/org/openstreetmap/josm/command/SequenceCommand.java	(revision 16248)
+++ src/org/openstreetmap/josm/command/SequenceCommand.java	(working copy)
@@ -74,6 +74,34 @@
         this(name, Arrays.asList(sequenz));
     }
 
+    /**
+     * Convenient constructor, if the commands are known at compile time.
+     * @param name The description text to be used for the sequence command, if one is created.
+     * @param sequenz The sequence that should be executed.
+     * @return Either a SequenceCommand, or the only command in the potential sequence
+     * @since xxx
+     */
+    public static Command createSimplifiedSequenceCommand(String name, Command... sequenz) {
+        if (sequenz.length == 1) {
+            return sequenz[0];
+        }
+        return new SequenceCommand(name, sequenz);
+    }
+
+    /**
+     * Convenient constructor, if the commands are known at compile time.
+     * @param name The description text to be used for the sequence command, if one is created.
+     * @param sequenz The sequence that should be executed.
+     * @return Either a SequenceCommand, or the only command in the potential sequence
+     * @since xxx
+     */
+    public static Command createSimplifiedSequenceCommand(String name, Collection<Command> sequenz) {
+        if (sequenz.size() == 1) {
+            return sequenz.iterator().next();
+        }
+        return new SequenceCommand(name, sequenz);
+    }
+
     @Override public boolean executeCommand() {
         for (int i = 0; i < sequence.length; i++) {
             boolean result = sequence[i].executeCommand();
Index: src/org/openstreetmap/josm/data/osm/INode.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/INode.java	(revision 16248)
+++ src/org/openstreetmap/josm/data/osm/INode.java	(working copy)
@@ -1,6 +1,7 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.osm;
 
+import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.ILatLon;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -70,4 +71,25 @@
     default String getDisplayName(NameFormatter formatter) {
         return formatter.format(this);
     }
+
+
+    /**
+     * Determines if this node is outside of the world. See also #13538.
+     * @return <code>true</code>, if the coordinate is outside the world, compared by using lat/lon and east/north
+     * @since 14960 (extracted to INode in xxx)
+     */
+    default public boolean isOutSideWorld() {
+        LatLon ll = getCoor();
+        if (ll != null) {
+            Bounds b = ProjectionRegistry.getProjection().getWorldBoundsLatLon();
+            if (lat() < b.getMinLat() || lat() > b.getMaxLat() || lon() < b.getMinLon() || lon() > b.getMaxLon()) {
+                return true;
+            }
+            if (!ProjectionRegistry.getProjection().latlon2eastNorth(ll).equalsEpsilon(getEastNorth(), 1.0)) {
+                // we get here if a node was moved or created left from -180 or right from +180
+                return true;
+            }
+        }
+        return false;
+    }
 }
Index: src/org/openstreetmap/josm/data/osm/IWay.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/IWay.java	(revision 16248)
+++ src/org/openstreetmap/josm/data/osm/IWay.java	(working copy)
@@ -132,4 +132,14 @@
      * @since 13922
      */
     boolean isInnerNode(INode n);
+
+    /**
+     * Adds a node to the end of the list of nodes. Ignored, if n is null.
+     *
+     * @param n the node. Ignored, if null
+     * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
+     * to an incomplete way
+     * @since 1313 Way, xxx IWay
+     */
+    void addNode(N node);
 }
Index: src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Node.java	(revision 16248)
+++ src/org/openstreetmap/josm/data/osm/Node.java	(working copy)
@@ -10,7 +10,6 @@
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
@@ -405,26 +404,6 @@
         return referrers(Way.class).collect(Collectors.toList());
     }
 
-    /**
-     * Determines if this node is outside of the world. See also #13538.
-     * @return <code>true</code>, if the coordinate is outside the world, compared by using lat/lon and east/north
-     * @since 14960
-     */
-    public boolean isOutSideWorld() {
-        LatLon ll = getCoor();
-        if (ll != null) {
-            Bounds b = ProjectionRegistry.getProjection().getWorldBoundsLatLon();
-            if (lat() < b.getMinLat() || lat() > b.getMaxLat() || lon() < b.getMinLon() || lon() > b.getMaxLon()) {
-                return true;
-            }
-            if (!ProjectionRegistry.getProjection().latlon2eastNorth(ll).equalsEpsilon(getEastNorth(), 1.0)) {
-                // we get here if a node was moved or created left from -180 or right from +180
-                return true;
-            }
-        }
-        return false;
-    }
-
     @Override
     public UniqueIdGenerator getIdGenerator() {
         return idGenerator;
Index: src/org/openstreetmap/josm/data/osm/Way.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/Way.java	(revision 16248)
+++ src/org/openstreetmap/josm/data/osm/Way.java	(working copy)
@@ -395,14 +395,7 @@
         }
     }
 
-    /**
-     * Adds a node to the end of the list of nodes. Ignored, if n is null.
-     *
-     * @param n the node. Ignored, if null
-     * @throws IllegalStateException if this way is marked as incomplete. We can't add a node
-     * to an incomplete way
-     * @since 1313
-     */
+    @Override
     public void addNode(Node n) {
         checkDatasetNotReadOnly();
         if (n == null) return;
Index: src/org/openstreetmap/josm/data/osm/WaySegment.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/WaySegment.java	(revision 16248)
+++ src/org/openstreetmap/josm/data/osm/WaySegment.java	(working copy)
@@ -2,17 +2,20 @@
 package org.openstreetmap.josm.data.osm;
 
 import java.awt.geom.Line2D;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Objects;
 
+import org.openstreetmap.josm.tools.Logging;
+
 /**
  * A segment consisting of 2 consecutive nodes out of a way.
  */
-public final class WaySegment implements Comparable<WaySegment> {
+public final class WaySegment<N extends INode, W extends IWay<N>> implements Comparable<WaySegment<N, W>> {
 
     /**
      * The way.
      */
-    public final Way way;
+    public final W way;
 
     /**
      * The index of one of the 2 nodes in the way.  The other node has the
@@ -26,7 +29,7 @@
      * @param i The node lower index
      * @throws IllegalArgumentException in case of invalid index
      */
-    public WaySegment(Way w, int i) {
+    public WaySegment(W w, int i) {
         way = w;
         lowerIndex = i;
         if (i < 0 || i >= w.getNodesCount() - 1) {
@@ -38,7 +41,7 @@
      * Returns the first node of the way segment.
      * @return the first node
      */
-    public Node getFirstNode() {
+    public N getFirstNode() {
         return way.getNode(lowerIndex);
     }
 
@@ -46,7 +49,7 @@
      * Returns the second (last) node of the way segment.
      * @return the second node
      */
-    public Node getSecondNode() {
+    public N getSecondNode() {
         return way.getNode(lowerIndex + 1);
     }
 
@@ -58,12 +61,12 @@
      * @return way segment
      * @throws IllegalArgumentException if the node pair is not part of way
      */
-    public static WaySegment forNodePair(Way way, Node first, Node second) {
+    public static <N extends INode, W extends IWay<N>> WaySegment<N, W> forNodePair(W way, N first, N second) {
         int endIndex = way.getNodesCount() - 1;
         while (endIndex > 0) {
             final int indexOfFirst = way.getNodes().subList(0, endIndex).lastIndexOf(first);
             if (second.equals(way.getNode(indexOfFirst + 1))) {
-                return new WaySegment(way, indexOfFirst);
+                return new WaySegment<>(way, indexOfFirst);
             }
             endIndex--;
         }
@@ -72,13 +75,21 @@
 
     /**
      * Returns this way segment as complete way.
-     * @return the way segment as {@code Way}
+     * @return the way segment as {@code W}
      */
-    public Way toWay() {
-        Way w = new Way();
-        w.addNode(getFirstNode());
-        w.addNode(getSecondNode());
-        return w;
+    public W toWay() {
+        try {
+            /** way is of type W, so it should always create a new W */
+            @SuppressWarnings("unchecked")
+            W w = (W) way.getClass().getConstructor().newInstance();
+            w.addNode(getFirstNode());
+            w.addNode(getSecondNode());
+            return w;
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
+                | NoSuchMethodException | SecurityException e) {
+            Logging.trace(e);
+            return null;
+        }
     }
 
     @Override
@@ -85,7 +96,7 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        WaySegment that = (WaySegment) o;
+        WaySegment<?, ?> that = (WaySegment<?, ?>) o;
         return lowerIndex == that.lowerIndex &&
                 Objects.equals(way, that.way);
     }
@@ -96,7 +107,7 @@
     }
 
     @Override
-    public int compareTo(WaySegment o) {
+    public int compareTo(WaySegment<N, W> o) {
         return o == null ? -1 : (equals(o) ? 0 : toWay().compareTo(o.toWay()));
     }
 
Index: src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 16248)
+++ src/org/openstreetmap/josm/gui/NavigatableComponent.java	(working copy)
@@ -40,10 +40,11 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.BBox;
 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.Way;
+import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.IRelation;
+import org.openstreetmap.josm.data.osm.IWay;
+import org.openstreetmap.josm.data.osm.OsmData;
 import org.openstreetmap.josm.data.osm.WaySegment;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
@@ -90,7 +91,7 @@
     /**
      * To determine if a primitive is currently selectable.
      */
-    public transient Predicate<OsmPrimitive> isSelectablePredicate = prim -> {
+    public transient Predicate<IPrimitive> isSelectablePredicate = prim -> {
         if (!prim.isSelectable()) return false;
         // if it isn't displayed on screen, you cannot click on it
         MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock();
@@ -549,7 +550,7 @@
      * @param n The node, where this geopoint would be drawn.
      * @return The point on screen where "node" would be drawn, relative to the own top/left.
      */
-    public Point2D getPoint2D(Node n) {
+    public Point2D getPoint2D(INode n) {
         return getPoint2D(n.getEastNorth());
     }
 
@@ -590,9 +591,9 @@
      * looses precision, may overflow (depends on p and current scale)
      * @param n node
      * @return point
-     * @see #getPoint2D(Node)
+     * @see #getPoint2D(INode)
      */
-    public Point getPoint(Node n) {
+    public Point getPoint(INode n) {
         Point2D d = getPoint2D(n);
         return new Point((int) d.getX(), (int) d.getY());
     }
@@ -982,15 +983,15 @@
      *
      * @return a sorted map with the keys representing the distance of their associated nodes to point p.
      */
-    private Map<Double, List<Node>> getNearestNodesImpl(Point p, Predicate<OsmPrimitive> predicate) {
-        Map<Double, List<Node>> nearestMap = new TreeMap<>();
-        DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
+    private Map<Double, List<INode>> getNearestNodesImpl(Point p, Predicate<IPrimitive> predicate) {
+        Map<Double, List<INode>> nearestMap = new TreeMap<>();
+        OsmData<?, ?, ?, ?> ds = MainApplication.getLayerManager().getActiveData();
 
         if (ds != null) {
             double dist, snapDistanceSq = PROP_SNAP_DISTANCE.get();
             snapDistanceSq *= snapDistanceSq;
 
-            for (Node n : ds.searchNodes(getBBox(p, PROP_SNAP_DISTANCE.get()))) {
+            for (INode n : ds.searchNodes(getBBox(p, PROP_SNAP_DISTANCE.get()))) {
                 if (predicate.test(n)
                         && (dist = getPoint2D(n).distanceSq(p)) < snapDistanceSq) {
                     nearestMap.computeIfAbsent(dist, k -> new LinkedList<>()).add(n);
@@ -1014,20 +1015,20 @@
      *      dist(nearest) to dist(nearest)+4px around p and
      *      that are not in ignore.
      */
-    public final List<Node> getNearestNodes(Point p,
-            Collection<Node> ignore, Predicate<OsmPrimitive> predicate) {
-        List<Node> nearestList = Collections.emptyList();
+    public final List<INode> getNearestNodes(Point p,
+            Collection<INode> ignore, Predicate<IPrimitive> predicate) {
+        List<INode> nearestList = Collections.emptyList();
 
         if (ignore == null) {
             ignore = Collections.emptySet();
         }
 
-        Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate);
+        Map<Double, List<INode>> nlists = getNearestNodesImpl(p, predicate);
         if (!nlists.isEmpty()) {
             Double minDistSq = null;
-            for (Entry<Double, List<Node>> entry : nlists.entrySet()) {
+            for (Entry<Double, List<INode>> entry : nlists.entrySet()) {
                 Double distSq = entry.getKey();
-                List<Node> nlist = entry.getValue();
+                List<INode> nlist = entry.getValue();
 
                 // filter nodes to be ignored before determining minDistSq..
                 nlist.removeAll(ignore);
@@ -1060,7 +1061,7 @@
      *      dist(nearest) to dist(nearest)+4px around p.
      * @see #getNearestNodes(Point, Collection, Predicate)
      */
-    public final List<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) {
+    public final List<INode> getNearestNodes(Point p, Predicate<IPrimitive> predicate) {
         return getNearestNodes(p, null, predicate);
     }
 
@@ -1084,7 +1085,7 @@
      *
      * @return A node within snap-distance to point p, that is chosen by the algorithm described.
      */
-    public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate, boolean useSelected) {
+    public final INode getNearestNode(Point p, Predicate<IPrimitive> predicate, boolean useSelected) {
         return getNearestNode(p, predicate, useSelected, null);
     }
 
@@ -1112,20 +1113,20 @@
      * @return A node within snap-distance to point p, that is chosen by the algorithm described.
      * @since 6065
      */
-    public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate,
-            boolean useSelected, Collection<OsmPrimitive> preferredRefs) {
+    public final INode getNearestNode(Point p, Predicate<IPrimitive> predicate,
+            boolean useSelected, Collection<IPrimitive> preferredRefs) {
 
-        Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate);
+        Map<Double, List<INode>> nlists = getNearestNodesImpl(p, predicate);
         if (nlists.isEmpty()) return null;
 
         if (preferredRefs != null && preferredRefs.isEmpty()) preferredRefs = null;
-        Node ntsel = null, ntnew = null, ntref = null;
+        INode ntsel = null, ntnew = null, ntref = null;
         boolean useNtsel = useSelected;
         double minDistSq = nlists.keySet().iterator().next();
 
-        for (Entry<Double, List<Node>> entry : nlists.entrySet()) {
+        for (Entry<Double, List<INode>> entry : nlists.entrySet()) {
             Double distSq = entry.getKey();
-            for (Node nd : entry.getValue()) {
+            for (INode nd : entry.getValue()) {
                 // find the nearest selected node
                 if (ntsel == null && nd.isSelected()) {
                     ntsel = nd;
@@ -1136,8 +1137,8 @@
                     useNtsel |= Utils.equalsEpsilon(distSq, minDistSq);
                 }
                 if (ntref == null && preferredRefs != null && Utils.equalsEpsilon(distSq, minDistSq)) {
-                    List<OsmPrimitive> ndRefs = nd.getReferrers();
-                    for (OsmPrimitive ref: preferredRefs) {
+                    List<? extends IPrimitive> ndRefs = nd.getReferrers();
+                    for (IPrimitive ref: preferredRefs) {
                         if (ndRefs.contains(ref)) {
                             ntref = nd;
                             break;
@@ -1170,7 +1171,7 @@
      *
      * @return The nearest node to point p.
      */
-    public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate) {
+    public final INode getNearestNode(Point p, Predicate<IPrimitive> predicate) {
         return getNearestNode(p, predicate, true);
     }
 
@@ -1184,8 +1185,8 @@
      * @return a sorted map with the keys representing the perpendicular
      *      distance of their associated way segments to point p.
      */
-    private Map<Double, List<WaySegment>> getNearestWaySegmentsImpl(Point p, Predicate<OsmPrimitive> predicate) {
-        Map<Double, List<WaySegment>> nearestMap = new TreeMap<>();
+    private Map<Double, List<WaySegment<?, ?>>> getNearestWaySegmentsImpl(Point p, Predicate<IPrimitive> predicate) {
+        Map<Double, List<WaySegment<?, ?>>> nearestMap = new TreeMap<>();
         DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
 
         if (ds != null) {
@@ -1192,13 +1193,13 @@
             double snapDistanceSq = Config.getPref().getInt("mappaint.segment.snap-distance", 10);
             snapDistanceSq *= snapDistanceSq;
 
-            for (Way w : ds.searchWays(getBBox(p, Config.getPref().getInt("mappaint.segment.snap-distance", 10)))) {
+            for (IWay<?> w : ds.searchWays(getBBox(p, Config.getPref().getInt("mappaint.segment.snap-distance", 10)))) {
                 if (!predicate.test(w)) {
                     continue;
                 }
-                Node lastN = null;
+                INode lastN = null;
                 int i = -2;
-                for (Node n : w.getNodes()) {
+                for (INode n : w.getNodes()) {
                     i++;
                     if (n.isDeleted() || n.isIncomplete()) { //FIXME: This shouldn't happen, raise exception?
                         continue;
@@ -1224,7 +1225,7 @@
                             >> 32 << 32); // resolution in numbers with large exponent not needed here..
 
                     if (perDistSq < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
-                        nearestMap.computeIfAbsent(perDistSq, k -> new LinkedList<>()).add(new WaySegment(w, i));
+                        nearestMap.computeIfAbsent(perDistSq, k -> new LinkedList<>()).add(new WaySegment<>(w, i));
                     }
 
                     lastN = n;
@@ -1247,15 +1248,15 @@
      * @return all segments within 10px of p that are not in ignore,
      *          sorted by their perpendicular distance.
      */
-    public final List<WaySegment> getNearestWaySegments(Point p,
-            Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) {
-        List<WaySegment> nearestList = new ArrayList<>();
-        List<WaySegment> unselected = new LinkedList<>();
+    public final List<WaySegment<?, ?>> getNearestWaySegments(Point p,
+            Collection<WaySegment<?, ?>> ignore, Predicate<IPrimitive> predicate) {
+        List<WaySegment<?, ?>> nearestList = new ArrayList<>();
+        List<WaySegment<?, ?>> unselected = new LinkedList<>();
 
-        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+        for (List<WaySegment<?, ?>> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
             // put selected waysegs within each distance group first
             // makes the order of nearestList dependent on current selection state
-            for (WaySegment ws : wss) {
+            for (WaySegment<?, ?> ws : wss) {
                 (ws.way.isSelected() ? nearestList : unselected).add(ws);
             }
             nearestList.addAll(unselected);
@@ -1277,7 +1278,7 @@
      * @return all segments within 10px of p, sorted by their perpendicular distance.
      * @see #getNearestWaySegments(Point, Collection, Predicate)
      */
-    public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) {
+    public final List<WaySegment<?, ?>> getNearestWaySegments(Point p, Predicate<IPrimitive> predicate) {
         return getNearestWaySegments(p, null, predicate);
     }
 
@@ -1292,15 +1293,15 @@
      *      and, depending on use_selected, prefers a selected way segment, if found.
      * @see #getNearestWaySegments(Point, Collection, Predicate)
      */
-    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate, boolean useSelected) {
-        WaySegment wayseg = null;
-        WaySegment ntsel = null;
+    public final WaySegment<?, ?> getNearestWaySegment(Point p, Predicate<IPrimitive> predicate, boolean useSelected) {
+        WaySegment<?, ?> wayseg = null;
+        WaySegment<?, ?> ntsel = null;
 
-        for (List<WaySegment> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
+        for (List<WaySegment<?, ?>> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
             if (wayseg != null && ntsel != null) {
                 break;
             }
-            for (WaySegment ws : wslist) {
+            for (WaySegment<?, ?> ws : wslist) {
                 if (wayseg == null) {
                     wayseg = ws;
                 }
@@ -1328,14 +1329,14 @@
      * @see #getNearestWaySegments(Point, Collection, Predicate)
      * @since 6065
      */
-    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate,
-            boolean useSelected, Collection<OsmPrimitive> preferredRefs) {
-        WaySegment wayseg = null;
+    public final WaySegment<? extends INode, ? extends IWay<?>> getNearestWaySegment(Point p, Predicate<IPrimitive> predicate,
+            boolean useSelected, Collection<IPrimitive> preferredRefs) {
+        WaySegment<?, ?> wayseg = null;
         if (preferredRefs != null && preferredRefs.isEmpty())
             preferredRefs = null;
 
-        for (List<WaySegment> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
-            for (WaySegment ws : wslist) {
+        for (List<WaySegment<?, ?>> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
+            for (WaySegment<?, ?> ws : wslist) {
                 if (wayseg == null) {
                     wayseg = ws;
                 }
@@ -1347,10 +1348,10 @@
                     if (preferredRefs.contains(ws.getFirstNode()) || preferredRefs.contains(ws.getSecondNode())) {
                         return ws;
                     }
-                    Collection<OsmPrimitive> wayRefs = ws.way.getReferrers();
+                    Collection<? extends IPrimitive> wayRefs = ws.way.getReferrers();
                     // prefer member of the given relations
-                    for (OsmPrimitive ref: preferredRefs) {
-                        if (ref instanceof Relation && wayRefs.contains(ref)) {
+                    for (IPrimitive ref: preferredRefs) {
+                        if (ref instanceof IRelation && wayRefs.contains(ref)) {
                             return ws;
                         }
                     }
@@ -1367,7 +1368,7 @@
      *
      * @return The nearest way segment to point p.
      */
-    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) {
+    public final WaySegment<?, ?> getNearestWaySegment(Point p, Predicate<IPrimitive> predicate) {
         return getNearestWaySegment(p, predicate, true);
     }
 
@@ -1383,13 +1384,13 @@
      * @return all nearest ways to the screen point given that are not in ignore.
      * @see #getNearestWaySegments(Point, Collection, Predicate)
      */
-    public final List<Way> getNearestWays(Point p,
-            Collection<Way> ignore, Predicate<OsmPrimitive> predicate) {
-        List<Way> nearestList = new ArrayList<>();
-        Set<Way> wset = new HashSet<>();
+    public final List<IWay<?>> getNearestWays(Point p,
+            Collection<IWay<?>> ignore, Predicate<IPrimitive> predicate) {
+        List<IWay<?>> nearestList = new ArrayList<>();
+        Set<IWay<?>> wset = new HashSet<>();
 
-        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
-            for (WaySegment ws : wss) {
+        for (List<WaySegment<?, ?>> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+            for (WaySegment<?, ?> ws : wss) {
                 if (wset.add(ws.way)) {
                     nearestList.add(ws.way);
                 }
@@ -1413,7 +1414,7 @@
      * @return all nearest ways to the screen point given.
      * @see #getNearestWays(Point, Collection, Predicate)
      */
-    public final List<Way> getNearestWays(Point p, Predicate<OsmPrimitive> predicate) {
+    public final List<IWay<?>> getNearestWays(Point p, Predicate<IPrimitive> predicate) {
         return getNearestWays(p, null, predicate);
     }
 
@@ -1426,8 +1427,8 @@
      * @return The nearest way to point p, prefer a selected way if there are multiple nearest.
      * @see #getNearestWaySegment(Point, Predicate)
      */
-    public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) {
-        WaySegment nearestWaySeg = getNearestWaySegment(p, predicate);
+    public final IWay<?> getNearestWay(Point p, Predicate<IPrimitive> predicate) {
+        WaySegment<?, ?> nearestWaySeg = getNearestWaySegment(p, predicate);
         return (nearestWaySeg == null) ? null : nearestWaySeg.way;
     }
 
@@ -1452,15 +1453,15 @@
      * @see #getNearestNodes(Point, Collection, Predicate)
      * @see #getNearestWays(Point, Collection, Predicate)
      */
-    public final List<OsmPrimitive> getNearestNodesOrWays(Point p,
-            Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) {
-        List<OsmPrimitive> nearestList = Collections.emptyList();
-        OsmPrimitive osm = getNearestNodeOrWay(p, predicate, false);
+    public final List<IPrimitive> getNearestNodesOrWays(Point p,
+            Collection<IPrimitive> ignore, Predicate<IPrimitive> predicate) {
+        List<IPrimitive> nearestList = Collections.emptyList();
+        IPrimitive osm = getNearestNodeOrWay(p, predicate, false);
 
         if (osm != null) {
-            if (osm instanceof Node) {
+            if (osm instanceof INode) {
                 nearestList = new ArrayList<>(getNearestNodes(p, predicate));
-            } else if (osm instanceof Way) {
+            } else if (osm instanceof IWay) {
                 nearestList = new ArrayList<>(getNearestWays(p, predicate));
             }
             if (ignore != null) {
@@ -1481,7 +1482,7 @@
      * @return Primitives nearest to the given screen point.
      * @see #getNearestNodesOrWays(Point, Collection, Predicate)
      */
-    public final List<OsmPrimitive> getNearestNodesOrWays(Point p, Predicate<OsmPrimitive> predicate) {
+    public final List<IPrimitive> getNearestNodesOrWays(Point p, Predicate<IPrimitive> predicate) {
         return getNearestNodesOrWays(p, null, predicate);
     }
 
@@ -1494,7 +1495,7 @@
      * @param useSelected whether to prefer selected nodes
      * @return true, if the node fulfills the properties of the function body
      */
-    private boolean isPrecedenceNode(Node osm, Point p, boolean useSelected) {
+    private boolean isPrecedenceNode(INode osm, Point p, boolean useSelected) {
         if (osm != null) {
             if (p.distanceSq(getPoint2D(osm)) <= (4*4)) return true;
             if (osm.isTagged()) return true;
@@ -1527,18 +1528,18 @@
      * @see #getNearestNode(Point, Predicate)
      * @see #getNearestWay(Point, Predicate)
      */
-    public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate, boolean useSelected) {
-        Collection<OsmPrimitive> sel;
-        DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
+    public final IPrimitive getNearestNodeOrWay(Point p, Predicate<IPrimitive> predicate, boolean useSelected) {
+        Collection<IPrimitive> sel;
+        OsmData<?, ?, ?, ?> ds = MainApplication.getLayerManager().getActiveData();
         if (useSelected && ds != null) {
-            sel = ds.getSelected();
+            sel = new ArrayList<>(ds.getSelected());
         } else {
             sel = null;
         }
-        OsmPrimitive osm = getNearestNode(p, predicate, useSelected, sel);
+        IPrimitive osm = getNearestNode(p, predicate, useSelected, sel);
 
-        if (isPrecedenceNode((Node) osm, p, useSelected)) return osm;
-        WaySegment ws;
+        if (isPrecedenceNode((INode) osm, p, useSelected)) return osm;
+        WaySegment<?, ?> ws;
         if (useSelected) {
             ws = getNearestWaySegment(p, predicate, useSelected, sel);
         } else {
@@ -1559,7 +1560,7 @@
             // is wayseg shorter than maxWaySegLenSq and
             // is p closer to the middle of wayseg  than  to the nearest node?
             if (wp1.distanceSq(wp2) < maxWaySegLenSq &&
-                    p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((Node) osm))) {
+                    p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((INode) osm))) {
                 osm = ws.way;
             }
         }
@@ -1596,14 +1597,14 @@
      * @return a list of all objects that are nearest to point p and
      *          not in ignore or an empty list if nothing was found.
      */
-    public final List<OsmPrimitive> getAllNearest(Point p,
-            Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) {
-        List<OsmPrimitive> nearestList = new ArrayList<>();
-        Set<Way> wset = new HashSet<>();
+    public final List<IPrimitive> getAllNearest(Point p,
+            Collection<IPrimitive> ignore, Predicate<IPrimitive> predicate) {
+        List<IPrimitive> nearestList = new ArrayList<>();
+        Set<IWay<?>> wset = new HashSet<>();
 
         // add nearby ways
-        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
-            for (WaySegment ws : wss) {
+        for (List<WaySegment<?, ?>> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+            for (WaySegment<?, ?> ws : wss) {
                 if (wset.add(ws.way)) {
                     nearestList.add(ws.way);
                 }
@@ -1611,15 +1612,15 @@
         }
 
         // add nearby nodes
-        for (List<Node> nlist : getNearestNodesImpl(p, predicate).values()) {
+        for (List<INode> nlist : getNearestNodesImpl(p, predicate).values()) {
             nearestList.addAll(nlist);
         }
 
         // add parent relations of nearby nodes and ways
-        Set<OsmPrimitive> parentRelations = new HashSet<>();
-        for (OsmPrimitive o : nearestList) {
-            for (OsmPrimitive r : o.getReferrers()) {
-                if (r instanceof Relation && predicate.test(r)) {
+        Set<IPrimitive> parentRelations = new HashSet<>();
+        for (IPrimitive o : nearestList) {
+            for (IPrimitive r : o.getReferrers()) {
+                if (r instanceof IRelation && predicate.test(r)) {
                     parentRelations.add(r);
                 }
             }
@@ -1644,7 +1645,7 @@
      *          or an empty list if nothing was found.
      * @see #getAllNearest(Point, Collection, Predicate)
      */
-    public final List<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) {
+    public final List<IPrimitive> getAllNearest(Point p, Predicate<IPrimitive> predicate) {
         return getAllNearest(p, null, predicate);
     }
 
Index: src/org/openstreetmap/josm/gui/SelectionManager.java
===================================================================
--- src/org/openstreetmap/josm/gui/SelectionManager.java	(revision 16248)
+++ src/org/openstreetmap/josm/gui/SelectionManager.java	(working copy)
@@ -19,10 +19,10 @@
 
 import org.openstreetmap.josm.actions.SelectByInternalPointAction;
 import org.openstreetmap.josm.data.Bounds;
-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.Way;
+import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.IWay;
+import org.openstreetmap.josm.data.osm.OsmData;
 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
 import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
 import org.openstreetmap.josm.tools.Utils;
@@ -363,8 +363,8 @@
      * objects that are touched, instead those which are completely covered.
      * @return The collection of selected objects.
      */
-    public Collection<OsmPrimitive> getSelectedObjects(boolean alt) {
-        Collection<OsmPrimitive> selection = new LinkedList<>();
+    public Collection<IPrimitive> getSelectedObjects(boolean alt) {
+        Collection<IPrimitive> selection = new LinkedList<>();
 
         // whether user only clicked, not dragged.
         boolean clicked = false;
@@ -373,16 +373,16 @@
             clicked = true;
         }
 
-        DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
+        OsmData<?, ?, ?, ?> ds = MainApplication.getLayerManager().getActiveData();
         if (clicked) {
             Point center = new Point(selectionResult.xpoints[0], selectionResult.ypoints[0]);
-            OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive::isSelectable, false);
+            IPrimitive osm = nc.getNearestNodeOrWay(center, IPrimitive::isSelectable, false);
             if (osm != null) {
                 selection.add(osm);
             }
         } else if (ds != null) {
             // nodes
-            for (Node n : ds.getNodes()) {
+            for (INode n : ds.getNodes()) {
                 if (n.isSelectable() && selectionResult.contains(nc.getPoint2D(n))) {
                     selection.add(n);
                 }
@@ -389,12 +389,12 @@
             }
 
             // ways
-            for (Way w : ds.getWays()) {
+            for (IWay<?> w : ds.getWays()) {
                 if (!w.isSelectable() || w.isEmpty()) {
                     continue;
                 }
                 if (alt) {
-                    for (Node n : w.getNodes()) {
+                    for (INode n : w.getNodes()) {
                         if (!n.isIncomplete() && selectionResult.contains(nc.getPoint2D(n))) {
                             selection.add(w);
                             break;
@@ -402,7 +402,7 @@
                     }
                 } else {
                     boolean allIn = true;
-                    for (Node n : w.getNodes()) {
+                    for (INode n : w.getNodes()) {
                         if (!n.isIncomplete() && !selectionResult.contains(nc.getPoint(n))) {
                             allIn = false;
                             break;
