From bdbae0690eb4cb22e05d1d072624066e90bc9b84 Mon Sep 17 00:00:00 2001
From: Francescu Garoby <windu.2b@gmail.com>
Date: Sun, 31 May 2015 16:45:11 +0200
Subject: [PATCH] Load a range of OSM objects, using a hyphen as a separator

Using a hyphen as a separator, range of objects IDs can be loaded.
It is easier to write "w1-5" than "w1, w2, w3, w4, w5", and the result
is exactly the same.
---
 .../josm/data/osm/SimplePrimitiveId.java              | 19 ++++++++++++++++---
 .../josm/gui/dialogs/OsmIdSelectionDialog.java        |  7 +++++--
 .../josm/gui/widgets/OsmIdTextField.java              |  6 +++++-
 .../josm/data/osm/SimplePrimitiveIdTest.groovy        | 12 ++++++++++++
 4 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java b/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java
index 6bf4779..0e29563 100644
--- a/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java
+++ b/src/org/openstreetmap/josm/data/osm/SimplePrimitiveId.java
@@ -16,6 +16,8 @@ public class SimplePrimitiveId implements PrimitiveId, Serializable {
 
     public static final Pattern ID_PATTERN = Pattern.compile("((n(ode)?|w(ay)?|r(el(ation)?)?)[ /]?)(\\d+)");
 
+    public static final Pattern MULTIPLE_IDS_PATTERN = Pattern.compile("((n(ode)?|w(ay)?|r(el(ation)?)?)/?)(\\d+)(-(\\d+))?");
+
     public SimplePrimitiveId(long id, OsmPrimitiveType type) {
         this.id = id;
         this.type = type;
@@ -97,15 +99,26 @@ public class SimplePrimitiveId implements PrimitiveId, Serializable {
      */
     public static List<SimplePrimitiveId> fuzzyParse(String s) {
         final List<SimplePrimitiveId> ids = new ArrayList<>();
-        final Matcher m = ID_PATTERN.matcher(s);
+        final Matcher m = MULTIPLE_IDS_PATTERN.matcher(s);
         while (m.find()) {
             final char firstChar = s.charAt(m.start());
-            ids.add(new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())),
-                    firstChar == 'n'
+            if (null != m.group(m.groupCount())) {
+                for(long r = Long.parseLong(m.group(m.groupCount())) - 2; r <= Long.parseLong(m.group(m.groupCount())); r ++) {
+                    ids.add(new SimplePrimitiveId(r,
+                            firstChar == 'n'
                             ? OsmPrimitiveType.NODE
                             : firstChar == 'w'
                             ? OsmPrimitiveType.WAY
                             : OsmPrimitiveType.RELATION));
+                }
+            } else {
+                ids.add(new SimplePrimitiveId(Long.parseLong(m.group(m.groupCount())) - 2,
+                        firstChar == 'n'
+                        ? OsmPrimitiveType.NODE
+                        : firstChar == 'w'
+                        ? OsmPrimitiveType.WAY
+                        : OsmPrimitiveType.RELATION));
+            }
         }
         return ids;
     }
diff --git a/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java b/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java
index 0c53f8e..8ca3249 100644
--- a/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java
+++ b/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java
@@ -93,10 +93,13 @@ public class OsmIdSelectionDialog extends ExtendedDialog implements WindowListen
         HtmlPanel help = new HtmlPanel(/* I18n: {0} and {1} contains example strings not meant for translation. {2}=n, {3}=w, {4}=r. */
                 tr("Object IDs can be separated by comma or space.<br/>"
                         + "Examples: {0}<br/>"
-                        + "In mixed mode, specify objects like this: {1}<br/>"
-                        + "({2} stands for <i>node</i>, {3} for <i>way</i>, and {4} for <i>relation</i>)",
+                        + "In mixed mode, specify objects like this: {1}<br/><br/>"
+                        + "Ranges of object IDs can also be catched, by a hyphen.<br/>"
+                        + "Examples: {2}<br/>"
+                        + "({3} stands for <i>node</i>, {4} for <i>way</i>, and {5} for <i>relation</i>)",
                         "<b>" + Utils.joinAsHtmlUnorderedList(Arrays.asList("1 2 5", "1,2,5")) + "</b>",
                         "<b>w123, n110, w12, r15</b>",
+                        "<b>" + Utils.joinAsHtmlUnorderedList(Arrays.asList("w1-5", "n1-7")) + "</b>",
                         "<b>n</b>", "<b>w</b>", "<b>r</b>"
                 ));
         help.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
diff --git a/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java b/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java
index 1d11acb..9e7df4b 100644
--- a/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java
+++ b/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java
@@ -122,7 +122,11 @@ public class OsmIdTextField extends AbstractIdTextField<OsmIdTextField.OsmIdVali
                                 return false;
                             }
                         } catch (IllegalArgumentException ex2) {
-                            return false;
+                            try {
+                                ids.addAll(SimplePrimitiveId.fuzzyParse(s));
+                            } catch (IllegalArgumentException ex3) {
+                                return false;
+                            }
                         }
                     }
                 }
diff --git a/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy b/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy
index 75205e4..81f2df0 100644
--- a/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy
+++ b/test/unit/org/openstreetmap/josm/data/osm/SimplePrimitiveIdTest.groovy
@@ -33,4 +33,16 @@ class SimplePrimitiveIdTest extends GroovyTestCase {
         assert SimplePrimitiveId.fromString("way 123") == new SimplePrimitiveId(123, OsmPrimitiveType.WAY)
         assert SimplePrimitiveId.fromString("relation 123") == new SimplePrimitiveId(123, OsmPrimitiveType.RELATION)
     }
+
+    void testMultipleIDs() {
+        assert SimplePrimitiveId.fuzzyParse("node/123-125").toString() == "[node 123, node 124, node 125]"
+        assert SimplePrimitiveId.fuzzyParse("n/123-125").toString() == "[node 123, node 124, node 125]"
+        assert SimplePrimitiveId.fuzzyParse("node123-125").toString() == "[node 123, node 124, node 125]"
+        assert SimplePrimitiveId.fuzzyParse("way/123-125").toString() == "[way 123, way 124, way 125]"
+        assert SimplePrimitiveId.fuzzyParse("w/123-125").toString() == "[way 123, way 124, way 125]"
+        assert SimplePrimitiveId.fuzzyParse("way123-125").toString() == "[way 123, way 124, way 125]"
+        assert SimplePrimitiveId.fuzzyParse("relation/123-125").toString() == "[relation 123, relation 124, relation 125]"
+        assert SimplePrimitiveId.fuzzyParse("r/123-125").toString() == "[relation 123, relation 124, relation 125]"
+        assert SimplePrimitiveId.fuzzyParse("relation123-125").toString() == "[relation 123, relation 124, relation 125]"
+    }
 }
-- 
2.4.1

