Index: trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java	(revision 801)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/RelationEditor.java	(revision 803)
@@ -1,6 +1,6 @@
 package org.openstreetmap.josm.gui.dialogs;
 
+import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import java.awt.BorderLayout;
@@ -14,9 +14,11 @@
 import java.beans.PropertyChangeListener;
 import java.io.IOException;
+import java.text.Collator;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Map.Entry;
 
 import javax.swing.JButton;
-import javax.swing.JDialog;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
@@ -48,10 +50,10 @@
 /**
  * This dialog is for editing relations.
- * 
+ *
  * In the basic form, it provides two tables, one with the relation tags
- * and one with the relation members. (Relation tags can be edited through 
- * the normal properties dialog as well, if you manage to get a relation 
+ * and one with the relation members. (Relation tags can be edited through
+ * the normal properties dialog as well, if you manage to get a relation
  * selected!)
- * 
+ *
  * @author Frederik Ramm <frederik@remote.org>
  *
@@ -66,5 +68,5 @@
 	private final Relation clone;
 	private JLabel status;
-	
+
 	/**
 	 * The property data.
@@ -90,5 +92,5 @@
 		}
 	};
-	
+
 	/**
 	 * The properties and membership lists.
@@ -96,20 +98,81 @@
 	private final JTable propertyTable = new JTable(propertyData);
 	private final JTable memberTable = new JTable(memberData);
-	
+
+	/**
+	 * Collator for sorting the roles and entries of the member table.
+	 */
+	private static final Collator collator;
+	static {
+		collator = Collator.getInstance();
+		collator.setStrength(Collator.PRIMARY);
+	}
+
+	/**
+	 * Compare role strings.
+	 */
+	private static int compareRole(String s1, String s2) {
+		int last1 = s1.lastIndexOf('_');
+		if (last1 > 0) {
+			int last2 = s2.lastIndexOf('_');
+			if (last2 == last1) {
+				String prefix1 = s1.substring(0, last1);
+				String prefix2 = s2.substring(0, last2);
+
+				if (prefix1.equalsIgnoreCase(prefix2)) {
+					// Both roles have the same prefix, now determine the
+					// suffix.
+					String suffix1 = s1.substring(last1 + 1, s1.length());
+					String suffix2 = s2.substring(last2 + 1, s2.length());
+
+					if (suffix1.matches("\\d+") && suffix2.matches("\\d+")) {
+						// Suffix is an number -> compare it.
+						int i1 = Integer.parseInt(suffix1);
+						int i2 = Integer.parseInt(suffix2);
+
+						return i1 - i2;
+					}
+				}
+			}
+		}
+
+		// Default handling if the role name is nothing like "stop_xx"
+		return collator.compare(s1, s2);
+	}
+
+
+	/**
+	 * Compare two OsmPrimitives.
+	 */
+	private static int compareMemebers(OsmPrimitive o1, OsmPrimitive o2) {
+		return collator.compare(o1.getName(), o2.getName());
+	}
+
+	private final Comparator<RelationMember> memberComparator = new Comparator<RelationMember>() {
+		public int compare(RelationMember r1, RelationMember r2) {
+			int roleResult = compareRole(r1.role, r2.role);
+
+			if (roleResult == 0) {
+				return compareMemebers(r1.member, r2.member);
+			}
+
+			return roleResult;
+		}
+	};
+
 	/**
 	 * Creates a new relation editor for the given relation. The relation
 	 * will be saved if the user selects "ok" in the editor.
-	 * 
+	 *
 	 * If no relation is given, will create an editor for a new relation.
-	 * 
+	 *
 	 * @param relation relation to edit, or null to create a new one.
 	 */
 	public RelationEditor(Relation relation)
 	{
-		super(relation == null ? tr("Create new relation") : 
+		super(relation == null ? tr("Create new relation") :
 			relation.id == 0 ? tr ("Edit new relation") :
 			tr("Edit relation #{0}", relation.id));
 		this.relation = relation;
-		
+
 		if (relation == null) {
 			// create a new relation
@@ -117,18 +180,19 @@
 		} else {
 			// edit an existing relation
-			this.clone = new Relation(relation);	
-		}
-		
+			this.clone = new Relation(relation);
+			Collections.sort(this.clone.members, memberComparator);
+		}
+
 		getContentPane().setLayout(new BorderLayout());
 		JTabbedPane tabPane = new JTabbedPane();
 		getContentPane().add(tabPane, BorderLayout.CENTER);
-		
+
 		// (ab)use JOptionPane to make this look familiar;
 		// hook up with JOptionPane's property change event
 		// to detect button click
-		final JOptionPane okcancel = new JOptionPane("", 
+		final JOptionPane okcancel = new JOptionPane("",
 			JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null);
 		getContentPane().add(okcancel, BorderLayout.SOUTH);
-		
+
 		okcancel.addPropertyChangeListener(new PropertyChangeListener() {
 			public void propertyChange(PropertyChangeEvent event) {
@@ -155,10 +219,10 @@
 		getContentPane().add(help, BorderLayout.NORTH);
 		try { setAlwaysOnTop(true); } catch (SecurityException sx) {}
-		
-		// Basic Editor panel has two blocks; 
+
+		// Basic Editor panel has two blocks;
 		// a tag table at the top and a membership list below.
 
 		// setting up the properties table
-		
+
 		propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
 		propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -167,5 +231,5 @@
 				if (tme.getType() == TableModelEvent.UPDATE) {
 					int row = tme.getFirstRow();
-			
+
 					if (!(tme.getColumn() == 0 && row == propertyData.getRowCount() -1)) {
 						clone.entrySet().clear();
@@ -181,22 +245,10 @@
 		});
 		propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
-		
+
 		// setting up the member table
-		
+
 		memberData.setColumnIdentifiers(new String[]{tr("Role"),tr("Occupied By")});
 		memberTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
 		memberTable.getColumnModel().getColumn(1).setCellRenderer(new OsmPrimitivRenderer());
-		/*
-		memberTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
-			public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
-				Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
-				if (c instanceof JLabel) {
-					((OsmPrimitive)value).visit(nameVisitor);
-					((JLabel)c).setText(nameVisitor.name);
-				}
-				return c;
-			}
-		});	
-		*/
 		memberData.addTableModelListener(new TableModelListener() {
 			public void tableChanged(TableModelEvent tme) {
@@ -209,5 +261,5 @@
 		memberTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
 
-		
+
 		// combine both tables and wrap them in a scrollPane
 		JPanel bothTables = new JPanel();
@@ -217,8 +269,9 @@
 		bothTables.add(status = new JLabel(tr("Members")), GBC.eol().fill(GBC.HORIZONTAL));
 		bothTables.add(new JScrollPane(memberTable), GBC.eol().fill(GBC.BOTH));
-		
+
 		JPanel buttonPanel = new JPanel(new GridLayout(1,3));
-		
-		buttonPanel.add(createButton(marktr("Add Selected"),"addselected", tr("Add all currently selected objects as members"), KeyEvent.VK_A, new ActionListener() {
+
+		buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
+		tr("Add all currently selected objects as members"), KeyEvent.VK_A, new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
 				addSelected();
@@ -226,5 +279,6 @@
 		}));
 
-		buttonPanel.add(createButton(marktr("Delete Selected"),"deleteselected", tr("Delete all currently selected objects from releation"), KeyEvent.VK_R, new ActionListener() {
+		buttonPanel.add(createButton(marktr("Delete Selected"),"deleteselected",
+		tr("Delete all currently selected objects from releation"), KeyEvent.VK_R, new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
 				deleteSelected();
@@ -232,5 +286,6 @@
 		}));
 
-		buttonPanel.add(createButton(marktr("Delete"),"delete", tr("Remove the member in the current table row from this relation"), KeyEvent.VK_D, new ActionListener() {
+		buttonPanel.add(createButton(marktr("Delete"),"delete",
+		tr("Remove the member in the current table row from this relation"), KeyEvent.VK_D, new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
 				int[] rows = memberTable.getSelectedRows();
@@ -245,5 +300,6 @@
 		}));
 
-		buttonPanel.add(createButton(marktr("Select"),"select", tr("Highlight the member from the current table row as JOSM's selection"), KeyEvent.VK_S, new ActionListener() {
+		buttonPanel.add(createButton(marktr("Select"),"select",
+		tr("Highlight the member from the current table row as JOSM's selection"), KeyEvent.VK_S, new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
 				int[] rows = memberTable.getSelectedRows();
@@ -253,5 +309,6 @@
 			}
 		}));
-		buttonPanel.add(createButton(marktr("Download Members"),"down", tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_L, new ActionListener() {
+		buttonPanel.add(createButton(marktr("Download Members"),"down",
+		tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_L, new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
 				downloadRelationMembers();
@@ -269,9 +326,8 @@
 		setLocationRelativeTo(Main.parent);
 	}
-	
+
 	private void refreshTables() {
-		
 		// re-load property data
-		
+
 		propertyData.setRowCount(0);
 		for (Entry<String, String> e : clone.entrySet()) {
@@ -279,7 +335,7 @@
 		}
 		propertyData.addRow(new Object[]{"", ""});
-		
+
 		// re-load membership data
-		
+
 		memberData.setRowCount(0);
 		for (RelationMember em : clone.members) {
@@ -288,5 +344,5 @@
 		status.setText(tr("Members: {0}", clone.members.size()));
 	}
-	
+
 	private JButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
 		JButton b = new JButton(tr(name), ImageProvider.get("dialogs", iconName));
@@ -298,5 +354,5 @@
 		return b;
 	}
-	
+
 	private void addSelected() {
 		for (OsmPrimitive p : Main.ds.getSelected()) {
@@ -336,50 +392,50 @@
 	}
 
-    private void downloadRelationMembers()  {
-
-        boolean download = false;
-        for (RelationMember member : clone.members) {
-            if (member.member.incomplete) {
-                download = true;
-                break;
-            }
-        }
-        if (download) {
-            OsmServerObjectReader reader = new OsmServerObjectReader();
-            try {
-                DataSet dataSet = reader.parseOsm(clone.id,
-                        OsmServerObjectReader.TYPE_REL, true);
-                if (dataSet != null) {
-                    final MergeVisitor visitor = new MergeVisitor(Main.main
-                            .editLayer().data, dataSet);
-                    for (final OsmPrimitive osm : dataSet.allPrimitives())
-                        osm.visit(visitor);
-                    visitor.fixReferences();
-
-                    // copy the merged layer's data source info
-                    for (DataSource src : dataSet.dataSources)
-                        Main.main.editLayer().data.dataSources.add(src);
-                    Main.main.editLayer().fireDataChange();
-
-                    if (visitor.conflicts.isEmpty())
-                        return;
-                    final ConflictDialog dlg = Main.map.conflictDialog;
-                    dlg.add(visitor.conflicts);
-                    JOptionPane.showMessageDialog(Main.parent,
-                            tr("There were conflicts during import."));
-                    if (!dlg.isVisible())
-                        dlg.action
-                                .actionPerformed(new ActionEvent(this, 0, ""));
-                }
-
-            } catch (SAXException e) {
-                e.printStackTrace();
-                JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);        
-            } catch (IOException e) {
-                e.printStackTrace();                
-                JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
-            }
-        }
-
-    }
+	private void downloadRelationMembers() {
+		boolean download = false;
+		for (RelationMember member : clone.members) {
+			if (member.member.incomplete) {
+				download = true;
+				break;
+			}
+		}
+		if (download) {
+			OsmServerObjectReader reader = new OsmServerObjectReader();
+			try {
+				DataSet dataSet = reader.parseOsm(clone.id,
+						OsmServerObjectReader.TYPE_REL, true);
+				if (dataSet != null) {
+					final MergeVisitor visitor = new MergeVisitor(Main.main
+							.editLayer().data, dataSet);
+					for (final OsmPrimitive osm : dataSet.allPrimitives())
+						osm.visit(visitor);
+					visitor.fixReferences();
+
+					// copy the merged layer's data source info
+					for (DataSource src : dataSet.dataSources)
+						Main.main.editLayer().data.dataSources.add(src);
+					Main.main.editLayer().fireDataChange();
+
+					if (visitor.conflicts.isEmpty())
+						return;
+					final ConflictDialog dlg = Main.map.conflictDialog;
+					dlg.add(visitor.conflicts);
+					JOptionPane.showMessageDialog(Main.parent,
+							tr("There were conflicts during import."));
+					if (!dlg.isVisible())
+						dlg.action
+								.actionPerformed(new ActionEvent(this, 0, ""));
+				}
+
+			} catch (SAXException e) {
+				e.printStackTrace();
+				JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(),
+				tr("Error"), JOptionPane.ERROR_MESSAGE);
+			} catch (IOException e) {
+				e.printStackTrace();
+				JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(),
+				tr("Error"), JOptionPane.ERROR_MESSAGE);
+			}
+		}
+	}
 }
