Index: /trunk/resources/images/select_shared_children.svg
===================================================================
--- /trunk/resources/images/select_shared_children.svg	(revision 18814)
+++ /trunk/resources/images/select_shared_children.svg	(revision 18814)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="24px" height="24px">
+  <path d="m6,5l7,6l2,8m-12,2l18,-18" stroke="#ffffff" fill="none"/>
+  <path d="m4.5,3.5h3v3h-3z
+    m9,14h3v3h-3z
+    m-12,2h3v3h-3z
+    m18,-18h3v3h-3z" fill="#ffffff" stroke="#cccccc"/>
+  <path d="m11,9h4v4h-4z" fill="#c4c4c4" stroke="#ab3217" style="stroke-width:2"/>
+</svg>
Index: /trunk/src/org/openstreetmap/josm/actions/InvertSelectionAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/InvertSelectionAction.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/actions/InvertSelectionAction.java	(revision 18814)
@@ -23,5 +23,5 @@
                 Shortcut.registerShortcut("selection:invertselection",
                 tr("Selection: {0}", tr("Invert Selection")), KeyEvent.CHAR_UNDEFINED, Shortcut.NONE), true);
-                setHelpId(ht("/Action/InvertSelection"));
+        setHelpId(ht("/Action/InvertSelection"));
     }
 
Index: /trunk/src/org/openstreetmap/josm/actions/SelectSharedChildObjectsAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/SelectSharedChildObjectsAction.java	(revision 18814)
+++ /trunk/src/org/openstreetmap/josm/actions/SelectSharedChildObjectsAction.java	(revision 18814)
@@ -0,0 +1,74 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Select child objects (way nodes and relation members) that are shared by all objects in the current selection.
+ * @since 18814
+ */
+public class SelectSharedChildObjectsAction extends JosmAction {
+
+    /**
+     * Create a new SelectSharedChildObjectsAction
+     */
+    public SelectSharedChildObjectsAction() {
+        super(tr("Select shared child objects"),
+                "select_shared_children",
+                tr("Select child objects (way nodes and relation members) that are shared by all objects in the current selection"),
+                Shortcut.registerShortcut("selection:sharedchildobjects",
+                    tr("Selection: {0}", tr("Shared Child Objects")),
+                    KeyEvent.CHAR_UNDEFINED, Shortcut.NONE),
+                true);
+        setHelpId(ht("/Action/SelectSharedChildObjectsAction"));
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        Collection<? extends IPrimitive> selection = getLayerManager().getActiveData().getSelected();
+        Set<? extends IPrimitive> shared = getSharedChildren(selection);
+        getLayerManager().getActiveData().setSelected(shared);
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        updateEnabledStateOnCurrentSelection(true);
+    }
+
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        setEnabled(!Utils.isEmpty(selection));
+    }
+
+    /**
+     * Get the shared children for a selection
+     * @param selection The selection to get shared children for
+     * @return The shared children
+     */
+    private static Set<? extends IPrimitive> getSharedChildren(Collection<? extends IPrimitive> selection) {
+        Set<IPrimitive> sharedChildObjects = new HashSet<>(selection.stream()
+                .findAny().map(IPrimitive::getChildren).orElse(Collections.emptyList()));
+
+        for (IPrimitive p : selection) {
+            if (sharedChildObjects.isEmpty())
+                break;
+
+            sharedChildObjects.retainAll(p.getChildren());
+        }
+
+        return sharedChildObjects;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 18814)
@@ -3,4 +3,5 @@
 
 import java.time.Instant;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
@@ -20,5 +21,5 @@
      * Replies <code>true</code> if the object has been modified since it was loaded from
      * the server. In this case, on next upload, this object will be updated.
-     *
+     * <p>
      * Deleted objects are deleted from the server. If the objects are added (id=0),
      * the modified is ignored and the object is added to the server.
@@ -68,5 +69,5 @@
     /**
      * Sets whether this primitive is deleted or not.
-     *
+     * <p>
      * Also marks this primitive as modified if deleted is true.
      *
@@ -244,5 +245,5 @@
     /**
      * Sets the id and the version of this primitive if it is known to the OSM API.
-     *
+     * <p>
      * Since we know the id and its version it can't be incomplete anymore. incomplete
      * is set to false.
@@ -277,5 +278,5 @@
      * @return date of last modification
      * @see #setTimestamp
-     * @deprecated Use {@link #getInstant}
+     * @deprecated since 17749, use {@link #getInstant} instead
      */
     @Deprecated
@@ -306,5 +307,5 @@
      * @param timestamp date of last modification
      * @see #getTimestamp
-     * @deprecated Use {@link #setInstant}
+     * @deprecated since 17749, use {@link #setInstant} instead
      */
     @Deprecated
@@ -546,3 +547,15 @@
         }
     }
+
+    /**
+     * Get child primitives that are referred by this primitive.
+     * {@link Relation}: Members of the relation
+     * {@link Way}: Nodes used by the way
+     * {@link Node}: None
+     * @return List of child primitives
+     * @since 18814
+     */
+    default List<? extends IPrimitive> getChildren() {
+        return Collections.emptyList();
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/osm/IRelation.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/IRelation.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/data/osm/IRelation.java	(revision 18814)
@@ -116,4 +116,9 @@
     }
 
+    @Override
+    default List<? extends IPrimitive> getChildren() {
+        return getMemberPrimitivesList();
+    }
+
     /**
      * Replies a collection with the incomplete children this relation refers to.
Index: /trunk/src/org/openstreetmap/josm/data/osm/IWay.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/IWay.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/data/osm/IWay.java	(revision 18814)
@@ -61,4 +61,9 @@
      */
     List<N> getNodes();
+
+    @Override
+    default List<N> getChildren() {
+        return this.getNodes();
+    }
 
     /**
Index: /trunk/src/org/openstreetmap/josm/data/osm/Relation.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 18814)
@@ -148,7 +148,7 @@
         boolean locked = writeLock();
         try {
-            List<RelationMember> members = getMembers();
-            RelationMember result = members.remove(index);
-            setMembers(members);
+            List<RelationMember> currentMembers = getMembers();
+            RelationMember result = currentMembers.remove(index);
+            setMembers(currentMembers);
             return result;
         } finally {
@@ -390,7 +390,7 @@
         boolean locked = writeLock();
         try {
-            List<RelationMember> members = getMembers();
-            members.removeAll(getMembersFor(primitives));
-            setMembers(members);
+            List<RelationMember> currentMembers = getMembers();
+            currentMembers.removeAll(getMembersFor(primitives));
+            setMembers(currentMembers);
         } finally {
             writeUnlock(locked);
@@ -428,4 +428,9 @@
 
     @Override
+    public List<OsmPrimitive> getChildren() {
+        return getMemberPrimitivesList();
+    }
+
+    @Override
     public OsmPrimitiveType getType() {
         return OsmPrimitiveType.RELATION;
@@ -444,5 +449,5 @@
 
         BBox box = new BBox();
-        addToBBox(box, new HashSet<PrimitiveId>());
+        addToBBox(box, new HashSet<>());
         if (getDataSet() == null) {
             return box;
@@ -494,5 +499,5 @@
                 for (RelationMember rm: members) {
                     if (rm.getMember().isDeleted())
-                        throw new DataIntegrityProblemException("Deleted member referenced: " + toString(), null, this, rm.getMember());
+                        throw new DataIntegrityProblemException("Deleted member referenced: " + this, null, this, rm.getMember());
                 }
             }
@@ -560,6 +565,6 @@
     public List<? extends OsmPrimitive> findRelationMembers(String role) {
         return IRelation.super.findRelationMembers(role).stream()
-                .filter(m -> m instanceof OsmPrimitive)
-                .map(m -> (OsmPrimitive) m).collect(Collectors.toList());
+                .filter(OsmPrimitive.class::isInstance)
+                .map(OsmPrimitive.class::cast).collect(Collectors.toList());
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 18813)
+++ /trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 18814)
@@ -97,4 +97,5 @@
 import org.openstreetmap.josm.actions.SelectAllAction;
 import org.openstreetmap.josm.actions.SelectNonBranchingWaySequencesAction;
+import org.openstreetmap.josm.actions.SelectSharedChildObjectsAction;
 import org.openstreetmap.josm.actions.SessionSaveAction;
 import org.openstreetmap.josm.actions.SessionSaveAsAction;
@@ -143,5 +144,5 @@
  * This is the JOSM main menu bar. It is overwritten to initialize itself and provide all menu
  * entries as member variables (sort of collect them).
- *
+ * <p>
  * It also provides possibilities to attach new menu entries (used by plugins).
  *
@@ -320,4 +321,6 @@
     /** Selection / Non-branching way sequences */
     public final SelectNonBranchingWaySequencesAction nonBranchingWaySequences = new SelectNonBranchingWaySequencesAction();
+    /** Selection / Shared Child Objects */
+    public final SelectSharedChildObjectsAction sharedChildObjects = new SelectSharedChildObjectsAction();
 
     /* Audio menu */
@@ -471,11 +474,11 @@
             for (int i = 0; i < m.getComponentCount()-1; i++) {
                 // hide separator if the next menu item is one as well
-                if (m.getComponent(i) instanceof JSeparator && m.getComponent(i+1) instanceof JSeparator) {
-                    ((JSeparator) m.getComponent(i)).setVisible(false);
+                if (m.getComponent(i) instanceof JSeparator && m.getComponent(i + 1) instanceof JSeparator) {
+                    m.getComponent(i).setVisible(false);
                 }
             }
             // hide separator at the end of the menu
-            if (m.getComponent(m.getComponentCount()-1) instanceof JSeparator) {
-                ((JSeparator) m.getComponent(m.getComponentCount()-1)).setVisible(false);
+            if (m.getComponent(m.getComponentCount() - 1) instanceof JSeparator) {
+                m.getComponent(m.getComponentCount() - 1).setVisible(false);
             }
         }
@@ -493,5 +496,5 @@
     /**
      * Add a JosmAction at the end of a menu.
-     *
+     * <p>
      * This method handles all the shortcut handling. It also makes sure that actions that are
      * handled by the OS are not duplicated on the menu.
@@ -506,5 +509,5 @@
     /**
      * Add a JosmAction at the end of a menu.
-     *
+     * <p>
      * This method handles all the shortcut handling. It also makes sure that actions that are
      * handled by the OS are not duplicated on the menu.
@@ -520,5 +523,5 @@
     /**
      * Add a JosmAction at the end of a menu.
-     *
+     * <p>
      * This method handles all the shortcut handling. It also makes sure that actions that are
      * handled by the OS are not duplicated on the menu.
@@ -552,5 +555,5 @@
     /**
      * Add the JosmAction {@code actionToBeInserted} directly below {@code existingMenuEntryAction}.
-     *
+     * <p>
      * This method handles all the shortcut handling. It also makes sure that actions that are
      * handled by the OS are not duplicated on the menu.
@@ -575,5 +578,5 @@
     /**
      * Add a JosmAction to a menu.
-     *
+     * <p>
      * This method handles all the shortcut handling. It also makes sure that actions that are
      * handled by the OS are not duplicated on the menu.
@@ -864,4 +867,5 @@
         add(selectionMenu, invertSelection, true);
         add(selectionMenu, nonBranchingWaySequences);
+        add(selectionMenu, sharedChildObjects, true);
 
         add(toolsMenu, splitWay);
@@ -948,6 +952,6 @@
     public Optional<JCheckBoxMenuItem> findMapModeMenuItem(MapMode mode) {
         return Arrays.stream(modeMenu.getMenuComponents())
-                .filter(m -> m instanceof JCheckBoxMenuItem)
-                .map(m -> (JCheckBoxMenuItem) m)
+                .filter(JCheckBoxMenuItem.class::isInstance)
+                .map(JCheckBoxMenuItem.class::cast)
                 .filter(m -> Objects.equals(mode, m.getAction()))
                 .findFirst();
Index: /trunk/test/unit/org/openstreetmap/josm/actions/SelectSharedChildObjectsActionTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/actions/SelectSharedChildObjectsActionTest.java	(revision 18814)
+++ /trunk/test/unit/org/openstreetmap/josm/actions/SelectSharedChildObjectsActionTest.java	(revision 18814)
@@ -0,0 +1,70 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+
+/**
+ * Test class for {@link SelectSharedChildObjectsAction}
+ */
+class SelectSharedChildObjectsActionTest {
+    private static SelectSharedChildObjectsAction action;
+    private DataSet ds;
+
+    @BeforeAll
+    static void classSetup() {
+        action = new SelectSharedChildObjectsAction();
+    }
+
+    @BeforeEach
+    void setup() {
+        ds = new DataSet();
+        MainApplication.getLayerManager().addLayer(new OsmDataLayer(ds, "SelectSharedChildObjectsActionTest", null));
+    }
+
+    @Test
+    void testNoIntersection() {
+        Way way1 = TestUtils.newWay("", TestUtils.newNode(""), TestUtils.newNode(""), TestUtils.newNode(""));
+        Way way2 = TestUtils.newWay("", TestUtils.newNode(""), TestUtils.newNode(""), TestUtils.newNode(""));
+        ds.addPrimitiveRecursive(way1);
+        ds.addPrimitiveRecursive(way2);
+        ds.setSelected(way1, way2);
+        assertAll("Sanity check that the current selection code works before we check the selection action",
+                () -> assertEquals(2, ds.getSelected().size()),
+                () -> assertTrue(ds.getSelected().contains(way1)),
+                () -> assertTrue(ds.getSelected().contains(way2)));
+
+        action.actionPerformed(null);
+        assertEquals(0, ds.getSelected().size(), "Nothing should be selected");
+    }
+
+    @Test
+    void testBasicIntersection() {
+        Way way1 = TestUtils.newWay("", TestUtils.newNode(""), TestUtils.newNode(""), TestUtils.newNode(""));
+        Way way2 = TestUtils.newWay("", TestUtils.newNode(""), TestUtils.newNode(""));
+        ds.addPrimitiveRecursive(way1);
+        ds.addPrimitiveRecursive(way2);
+        way2.addNode(1, way1.getNode(1));
+        ds.setSelected(way1, way2);
+        assertAll("Sanity check that the current selection code works before we check the selection action",
+                () -> assertEquals(2, ds.getSelected().size()),
+                () -> assertTrue(ds.getSelected().contains(way1)),
+                () -> assertTrue(ds.getSelected().contains(way2)));
+
+        action.actionPerformed(null);
+        assertAll("Check that the selected object is the common node",
+                () -> assertEquals(1, ds.getSelected().size()),
+                () -> assertSame(way1.getNode(1), ds.getSelected().iterator().next()));
+    }
+}
