Index: src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 4758)
+++ src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(working copy)
@@ -6,9 +6,14 @@
 
 import java.io.PushbackReader;
 import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.text.Normalizer;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -54,6 +59,7 @@
     private static String  rxErrorMsg = marktr("The regex \"{0}\" had a parse error at offset {1}, full error:\n\n{2}");
     private static String  rxErrorMsgNoPos = marktr("The regex \"{0}\" had a parse error, full error:\n\n{1}");
     private PushbackTokenizer tokenizer;
+    private static HashMap<String, Class<? extends PluginMatch>> pluginMatches = new HashMap<String, Class<? extends PluginMatch>>();
 
     public SearchCompiler(boolean caseSensitive, boolean regexSearch, PushbackTokenizer tokenizer) {
         this.caseSensitive = caseSensitive;
@@ -87,6 +93,19 @@
         }
     }
 
+    abstract public static class PluginMatch extends Match {
+        /* Returns the desired keyword for this search (may be modified, e.g. by
+         * appending "2" if the keyword is already registered)
+         * FIXME: should core notify plugin of keyword actually used?
+         */
+        abstract public String getKeyword();
+        
+        /* Returns a short description, possibly shown in the search dialog */
+        abstract public String getDescription();
+        
+        @Override public String toString() {return getKeyword();}
+    }
+
     public static class Always extends Match {
         public static Always INSTANCE = new Always();
         @Override public boolean match(OsmPrimitive osm) {
@@ -944,8 +963,32 @@
                 return new InView(false);
             else if ("allinview".equals(key))
                 return new InView(true);
-            else
-                return new Any(key, regexSearch, caseSensitive);
+            else {
+                if (pluginMatches.containsKey(key)) {
+                    PluginMatch match = null;
+                    try {
+                        Constructor ctor = pluginMatches.get(key).getDeclaredConstructor((Class[]) null);
+                        match = (PluginMatch)ctor.newInstance();
+                    } catch (InstantiationException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    } catch (IllegalAccessException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    } catch (IllegalArgumentException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    } catch (InvocationTargetException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    } catch (NoSuchMethodException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    } catch (SecurityException ex) {
+                        Logger.getLogger(SearchCompiler.class.getName()).log(Level.SEVERE, null, ex);
+                    }
+                    if (match == null)
+                        throw new ParseError("Failed to use plugin for keyword: " + key);
+                    return match;
+                }
+                else
+                    return new Any(key, regexSearch, caseSensitive);
+            }
         } else
             return null;
     }
@@ -994,4 +1037,9 @@
 
         return searchFlags;
     }
+    
+    public static void addPluginMatch(String keyword, Class<? extends PluginMatch> pm) {
+        /* TODO: check for keyword clashes, append incrementing number to new keyword */
+        pluginMatches.put(keyword, pm);
+    }
 }
