commit faee093e6f62484a9e501dfa88c27d6d676be424
Author: Simon Legner <Simon.Legner@gmail.com>
Date:   Tue Jan 8 19:03:16 2019 +0100

    fix #16255 - Possibility to edit relation roles from the membership dialog

diff --git a/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java b/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
index b84cdc0ef..5585204fc 100644
--- a/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
+++ b/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
@@ -16,13 +16,13 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -631,8 +631,7 @@ public void selectionChanged(SelectionChangeEvent event) {
             for (IPrimitive ref: primitive.getReferrers(true)) {
                 if (ref instanceof IRelation && !ref.isIncomplete() && !ref.isDeleted()) {
                     IRelation<?> r = (IRelation<?>) ref;
-                    MemberInfo mi = Optional.ofNullable(roles.get(r)).orElseGet(() -> new MemberInfo(newSel));
-                    roles.put(r, mi);
+                    MemberInfo mi = roles.computeIfAbsent(r, ignore -> new MemberInfo(newSel));
                     int i = 1;
                     for (IRelationMember<?> m : r.getMembers()) {
                         if (m.getMember() == primitive) {
@@ -892,7 +891,12 @@ public void mouseClicked(MouseEvent e) {
                 }
             } else if (e.getSource() == membershipTable) {
                 int row = membershipTable.rowAtPoint(e.getPoint());
-                if (row > -1) {
+                int col = membershipTable.columnAtPoint(e.getPoint());
+                if (row > -1 && col == 1) {
+                    final Relation relation = (Relation) membershipData.getValueAt(row, 0);
+                    final MemberInfo memberInfo = (MemberInfo) membershipData.getValueAt(row, 1);
+                    RelationRoleEditor.editRole(relation, memberInfo);
+                } else if (row > -1) {
                     editMembership(row);
                 }
             } else {
@@ -943,6 +947,10 @@ String getPositionString() {
             return Utils.shortenString(positionString, 20);
         }
 
+        List<IRelationMember<?>> getRole() {
+            return Collections.unmodifiableList(role);
+        }
+
         String getRoleString() {
             if (roleString == null) {
                 for (IRelationMember<?> r : role) {
diff --git a/src/org/openstreetmap/josm/gui/dialogs/properties/RelationRoleEditor.java b/src/org/openstreetmap/josm/gui/dialogs/properties/RelationRoleEditor.java
new file mode 100644
index 000000000..7f53823be
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/dialogs/properties/RelationRoleEditor.java
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs.properties;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.command.ChangeCommand;
+import org.openstreetmap.josm.data.UndoRedoHandler;
+import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Quickly change the relation roles of the selected members.
+ */
+final class RelationRoleEditor {
+
+    private RelationRoleEditor() {
+    }
+
+    /**
+     * Shows an {{@linkplain JOptionPane#showInputDialog input dialog} in order to quickly change
+     * the roles of the selected members.
+     * @param relation the relation to edit
+     * @param memberInfo the corresponding member info
+     */
+    static void editRole(Relation relation, PropertiesDialog.MemberInfo memberInfo) {
+        if (MainApplication.getLayerManager().getActiveDataLayer().isLocked()) {
+            return;
+        }
+        final Collection<RelationMember> members = Utils.filteredCollection(memberInfo.getRole(), RelationMember.class);
+        final String oldRole = memberInfo.getRoleString();
+        final DefaultNameFormatter formatter = DefaultNameFormatter.getInstance();
+        final String newRole = JOptionPane.showInputDialog("<html>" + tr("Change role for {0} in relation {1}",
+                formatter.formatAsHtmlUnorderedList(Utils.transform(members, RelationMember::getMember), 5),
+                formatter.formatAsHtmlUnorderedList(relation)),
+                oldRole);
+        if (newRole == null || oldRole.equals(newRole) || tr("<different>").equals(newRole)) {
+            return;
+        }
+        final Relation newRelation = new Relation(relation);
+        final List<RelationMember> newMembers = newRelation.getMembers();
+        newMembers.replaceAll(m -> members.contains(m) ? new RelationMember(newRole, m.getMember()) : m);
+        newRelation.setMembers(newMembers);
+        UndoRedoHandler.getInstance().add(new ChangeCommand(relation, newRelation));
+    }
+}
