Index: /trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java	(revision 2992)
+++ /trunk/src/org/openstreetmap/josm/actions/search/PushbackTokenizer.java	(revision 2993)
@@ -11,4 +11,23 @@
 
 public class PushbackTokenizer {
+
+    public class Range {
+        private final long start;
+        private final long end;
+
+        public Range(long start, long end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public long getStart() {
+            return start;
+        }
+
+        public long getEnd() {
+            return end;
+        }
+    }
+
     private final Reader search;
 
@@ -16,4 +35,5 @@
     private String currentText;
     private long currentNumber;
+    private long currentRange;
     private int c;
 
@@ -27,5 +47,5 @@
         RIGHT_PARENT(marktr("<right parent>")), COLON(marktr("<colon>")), EQUALS(marktr("<equals>")),
         KEY(marktr("<key>")), QUESTION_MARK(marktr("<question mark>")), NUMBER(marktr("<number>")),
-        EOF(marktr("<end-of-file>"));
+        RANGE(marktr("range")), EOF(marktr("<end-of-file>"));
 
         private Token(String name) {
@@ -128,5 +148,11 @@
             if (Character.isDigit(c)) {
                 currentNumber = getNumber();
-                return Token.NUMBER;
+                if (c == '-') {
+                    getChar();
+                    currentRange = getNumber();
+                    return Token.RANGE;
+                } else
+                    return Token.NUMBER;
+
             }
 
@@ -153,18 +179,12 @@
     }
 
-    public String readText() {
+    public String readTextOrNumber() {
         Token nextTok = nextToken();
         if (nextTok == Token.KEY)
             return currentText;
+        if (nextTok == Token.NUMBER)
+            return String.valueOf(currentNumber);
         currentToken = nextTok;
         return null;
-    }
-
-    public String readText(String errorMessage) throws ParseError {
-        String text = readText();
-        if (text == null)
-            throw new ParseError(errorMessage);
-        else
-            return text;
     }
 
@@ -176,4 +196,18 @@
     }
 
+    public long getReadNumber() {
+        return currentNumber;
+    }
+
+    public Range readRange() throws ParseError {
+        Token token = nextToken();
+        if (token == Token.NUMBER)
+            return new Range(currentNumber, currentNumber);
+        else if (token == Token.RANGE)
+            return new Range(currentNumber, currentRange);
+        else
+            throw new ParseError(Token.NUMBER, token);
+    }
+
     public String getText() {
         return currentText;
Index: /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 2992)
+++ /trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 2993)
@@ -12,4 +12,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.search.PushbackTokenizer.Range;
 import org.openstreetmap.josm.actions.search.PushbackTokenizer.Token;
 import org.openstreetmap.josm.data.osm.Node;
@@ -254,7 +255,7 @@
             this.key = key;
             this.value = value == null?"":value;
-            if ("".equals(value) && "*".equals(key)) {
+            if ("".equals(this.value) && "*".equals(key)) {
                 mode = Mode.NONE;
-            } else if ("".equals(value)) {
+            } else if ("".equals(this.value)) {
                 if (regexp) {
                     mode = Mode.MISSING_KEY_REGEXP;
@@ -262,5 +263,5 @@
                     mode = Mode.MISSING_KEY;
                 }
-            } else if ("*".equals(key) && "*".equals(value)) {
+            } else if ("*".equals(key) && "*".equals(this.value)) {
                 mode = Mode.ANY;
             } else if ("*".equals(key)) {
@@ -270,5 +271,5 @@
                     mode = Mode.ANY_KEY;
                 }
-            } else if ("*".equals(value)) {
+            } else if ("*".equals(this.value)) {
                 if (regexp) {
                     mode = Mode.ANY_VALUE_REGEXP;
@@ -289,7 +290,7 @@
                 keyPattern = null;
             }
-            if (regexp && value.length() > 0 && !value.equals("*")) {
+            if (regexp && this.value.length() > 0 && !this.value.equals("*")) {
                 try {
-                    valuePattern = Pattern.compile(value);
+                    valuePattern = Pattern.compile(this.value);
                 } catch (PatternSyntaxException e) {
                     throw new ParseError(tr("Pattern Syntax Error: Pattern {0} in {1} is illegal!", e.getPattern(), value));
@@ -471,13 +472,4 @@
     }
 
-    private static class NodeCount extends Match {
-        private int count;
-        public NodeCount(int count) {this.count = count;}
-        @Override public boolean match(OsmPrimitive osm) {
-            return osm instanceof Way && ((Way) osm).getNodesCount() == count;
-        }
-        @Override public String toString() {return "nodes="+count;}
-    }
-
     private static class NodeCountRange extends Match {
         private int minCount;
@@ -500,14 +492,4 @@
     }
 
-    private static class TagCount extends Match {
-        private int count;
-        public TagCount(int count) {this.count = count;}
-        @Override public boolean match(OsmPrimitive osm) {
-            int size = osm.getKeys().size();
-            return size == count;
-        }
-        @Override public String toString() {return "tags="+count;}
-    }
-
     private static class TagCountRange extends Match {
         private int minCount;
@@ -609,4 +591,7 @@
         public ParseError(String msg) {
             super(msg);
+        }
+        public ParseError(Token expected, Token found) {
+            this(tr("Unexpected token. Expected {0}, found {1}", expected, found));
         }
     }
@@ -656,5 +641,5 @@
             Match expression = parseExpression();
             if (!tokenizer.readIfEqual(Token.RIGHT_PARENT))
-                throw new ParseError(tr("Unexpected token. Expected {0}, found {1}", Token.RIGHT_PARENT, tokenizer.nextToken()));
+                throw new ParseError(Token.RIGHT_PARENT, tokenizer.nextToken());
             return expression;
         } else if (tokenizer.readIfEqual(Token.NOT))
@@ -663,11 +648,21 @@
             String key = tokenizer.getText();
             if (tokenizer.readIfEqual(Token.EQUALS))
-                return new ExactKeyValue(regexSearch, key, tokenizer.readText());
-            else if (tokenizer.readIfEqual(Token.COLON))
+                return new ExactKeyValue(regexSearch, key, tokenizer.readTextOrNumber());
+            else if (tokenizer.readIfEqual(Token.COLON)) {
                 if ("id".equals(key))
                     return new Id(tokenizer.readNumber(tr("Primitive id expected")));
+                else if ("tags".equals(key)) {
+                    Range range = tokenizer.readRange();
+                    return new TagCountRange((int)range.getStart(), (int)range.getEnd());
+                } else if ("nodes".equals("nodes")) {
+                    Range range = tokenizer.readRange();
+                    return new NodeCountRange((int)range.getStart(), (int)range.getEnd());
+                } else if ("changeset".equals(key))
+                    return new ChangesetId(tokenizer.readNumber(tr("Changeset id expected")));
+                else if ("version".equals(key))
+                    return new Version(tokenizer.readNumber(tr("Version expected")));
                 else
-                    return parseKV(key, tokenizer.readText());
-            else if (tokenizer.readIfEqual(Token.QUESTION_MARK))
+                    return parseKV(key, tokenizer.readTextOrNumber());
+            } else if (tokenizer.readIfEqual(Token.QUESTION_MARK))
                 return new BooleanMatch(key, false);
             else if ("modified".equals(key))
@@ -705,43 +700,4 @@
         else if (key.equals("user"))
             return new UserMatch(value);
-        else if (key.equals("tags")) {
-            try {
-                String[] range = value.split("-");
-                if (range.length == 1)
-                    return new TagCount(Integer.parseInt(value));
-                else if (range.length == 2)
-                    return new TagCountRange(Integer.parseInt(range[0]), Integer.parseInt(range[1]));
-                else
-                    throw new ParseError(tr("Wrong number of parameters for tags operator."));
-            } catch (NumberFormatException e) {
-                throw new ParseError(tr("Incorrect value of tags operator: {0}. Tags operator expects number of tags or range, for example tags:1 or tags:2-5", value));
-            }
-        } else if (key.equals("nodes")) {
-            try {
-                String[] range = value.split("-");
-                if (range.length == 1)
-                    return new NodeCount(Integer.parseInt(value));
-                else if (range.length == 2)
-                    return new NodeCountRange(Integer.parseInt(range[0]), Integer.parseInt(range[1]));
-                else
-                    throw new ParseError(tr("Wrong number of parameters for nodes operator."));
-            } catch (NumberFormatException e) {
-                throw new ParseError(tr("Incorrect value of nodes operator: {0}. Nodes operator expects number of nodes or range, for example nodes:10-20", value));
-            }
-
-        } else if (key.equals("changeset")) {
-            try {
-                return new ChangesetId(Integer.parseInt(value));
-            } catch (NumberFormatException x) {
-                throw new ParseError(tr("Incorrect value of changeset operator: {0}. Number is expected.", value));
-            }
-
-        }   else if (key.equals("version")) {
-            try {
-                return new Version(Long.parseLong(value));
-            } catch (NumberFormatException x) {
-                throw new ParseError(tr("Incorrect value of version operator: {0}. Number is expected.", value));
-            }
-        }
         else
             return new KeyValue(key, value, regexSearch, caseSensitive);
