Index: trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 8978)
+++ trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 8979)
@@ -28,4 +28,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Tagged;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.mappaint.Environment;
@@ -233,9 +234,24 @@
 
     /**
-     * Base class for all search operators.
+     * Base class for all search criteria. If the criterion only depends on an object's tags,
+     * inherit from {@link org.openstreetmap.josm.actions.search.SearchCompiler.TaggedMatch}.
      */
     public abstract static class Match implements Predicate<OsmPrimitive> {
 
+        /**
+         * Tests whether the primitive matches this criterion.
+         * @param osm the primitive to test
+         * @return true if the primitive matches this criterion
+         */
         public abstract boolean match(OsmPrimitive osm);
+
+        /**
+         * Tests whether the tagged object matches this criterion.
+         * @param tagged the tagged object to test
+         * @return true if the tagged object matches this criterion
+         */
+        public boolean match(Tagged tagged) {
+            return false;
+        }
 
         /**
@@ -271,4 +287,15 @@
     }
 
+    public abstract static class TaggedMatch extends Match {
+
+        @Override
+        public abstract boolean match(Tagged tags);
+
+        @Override
+        public final boolean match(OsmPrimitive osm) {
+            return match((Tagged) osm);
+        }
+    }
+
     /**
      * A unary search operator which may take data parameters.
@@ -318,9 +345,9 @@
      * Matches every OsmPrimitive.
      */
-    public static class Always extends Match {
+    public static class Always extends TaggedMatch {
         /** The unique instance/ */
         public static final Always INSTANCE = new Always();
         @Override
-        public boolean match(OsmPrimitive osm) {
+        public boolean match(Tagged osm) {
             return true;
         }
@@ -330,7 +357,7 @@
      * Never matches any OsmPrimitive.
      */
-    public static class Never extends Match {
-        @Override
-        public boolean match(OsmPrimitive osm) {
+    public static class Never extends TaggedMatch {
+        @Override
+        public boolean match(Tagged osm) {
             return false;
         }
@@ -351,4 +378,9 @@
 
         @Override
+        public boolean match(Tagged osm) {
+            return !match.match(osm);
+        }
+
+        @Override
         public String toString() {
             return "!" + match;
@@ -363,5 +395,5 @@
      * Matches if the value of the corresponding key is ''yes'', ''true'', ''1'' or ''on''.
      */
-    private static class BooleanMatch extends Match {
+    private static class BooleanMatch extends TaggedMatch {
         private final String key;
         private final boolean defaultValue;
@@ -373,5 +405,5 @@
 
         @Override
-        public boolean match(OsmPrimitive osm) {
+        public boolean match(Tagged osm) {
             Boolean ret = OsmUtils.getOsmBoolean(osm.get(key));
             if (ret == null)
@@ -401,4 +433,9 @@
 
         @Override
+        public boolean match(Tagged osm) {
+            return lhs.match(osm) && rhs.match(osm);
+        }
+
+        @Override
         public String toString() {
             return lhs + " && " + rhs;
@@ -420,4 +457,9 @@
 
         @Override
+        public boolean match(Tagged osm) {
+            return lhs.match(osm) || rhs.match(osm);
+        }
+
+        @Override
         public String toString() {
             return lhs + " || " + rhs;
@@ -439,4 +481,9 @@
 
         @Override
+        public boolean match(Tagged osm) {
+            return lhs.match(osm) ^ rhs.match(osm);
+        }
+
+        @Override
         public String toString() {
             return lhs + " ^ " + rhs;
@@ -516,5 +563,5 @@
      * Matches objects with the given key-value pair.
      */
-    private static class KeyValue extends Match {
+    private static class KeyValue extends TaggedMatch {
         private final String key;
         private final Pattern keyPattern;
@@ -559,5 +606,5 @@
 
         @Override
-        public boolean match(OsmPrimitive osm) {
+        public boolean match(Tagged osm) {
 
             if (keyPattern != null) {
@@ -589,6 +636,6 @@
                 String mv = null;
 
-                if ("timestamp".equals(key)) {
-                    mv = DateUtils.fromTimestamp(osm.getRawTimestamp());
+                if ("timestamp".equals(key) && osm instanceof OsmPrimitive) {
+                    mv = DateUtils.fromTimestamp(((OsmPrimitive) osm).getRawTimestamp());
                 } else {
                     mv = osm.get(key);
@@ -623,5 +670,5 @@
     }
 
-    public static class ValueComparison extends Match {
+    public static class ValueComparison extends TaggedMatch {
         private final String key;
         private final String referenceValue;
@@ -648,5 +695,5 @@
 
         @Override
-        public boolean match(OsmPrimitive osm) {
+        public boolean match(Tagged osm) {
             final String currentValue = osm.get(key);
             final int compareResult;
@@ -676,5 +723,5 @@
      * Matches objects with the exact given key-value pair.
      */
-    public static class ExactKeyValue extends Match {
+    public static class ExactKeyValue extends TaggedMatch {
 
         private enum Mode {
@@ -749,5 +796,5 @@
 
         @Override
-        public boolean match(OsmPrimitive osm) {
+        public boolean match(Tagged osm) {
 
             if (!osm.hasKeys())
@@ -806,5 +853,5 @@
      * Match a string in any tags (key or value), with optional regex and case insensitivity.
      */
-    private static class Any extends Match {
+    private static class Any extends TaggedMatch {
         private final String search;
         private final Pattern searchRegex;
@@ -833,6 +880,6 @@
 
         @Override
-        public boolean match(OsmPrimitive osm) {
-            if (!osm.hasKeys() && osm.getUser() == null)
+        public boolean match(Tagged osm) {
+            if (!osm.hasKeys())
                 return search.isEmpty();
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(revision 8978)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java	(revision 8979)
@@ -5,9 +5,10 @@
 
 import java.awt.BorderLayout;
-import java.awt.Color;
 import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -29,7 +30,4 @@
 import javax.swing.KeyStroke;
 import javax.swing.ListSelectionModel;
-import javax.swing.UIManager;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -70,4 +68,5 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.HighlightHelper;
+import org.openstreetmap.josm.gui.widgets.CompileSearchTextDecorator;
 import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
 import org.openstreetmap.josm.gui.widgets.JosmTextField;
@@ -283,31 +282,9 @@
         final JosmTextField f = new DisableShortcutsOnFocusGainedTextField();
         f.setToolTipText(tr("Relation list filter"));
-        f.getDocument().addDocumentListener(new DocumentListener() {
-
-            private void setFilter() {
-                try {
-                    f.setBackground(UIManager.getColor("TextField.background"));
-                    f.setToolTipText(tr("Relation list filter"));
-                    model.setFilter(SearchCompiler.compile(filter.getText()));
-                } catch (SearchCompiler.ParseError ex) {
-                    f.setBackground(new Color(255, 224, 224));
-                    f.setToolTipText(ex.getMessage());
-                    model.setFilter(new SearchCompiler.Always());
-                }
-            }
-
+        final CompileSearchTextDecorator decorator = CompileSearchTextDecorator.decorate(f);
+        f.addPropertyChangeListener("filter", new PropertyChangeListener() {
             @Override
-            public void insertUpdate(DocumentEvent e) {
-                setFilter();
-            }
-
-            @Override
-            public void removeUpdate(DocumentEvent e) {
-                setFilter();
-            }
-
-            @Override
-            public void changedUpdate(DocumentEvent e) {
-                setFilter();
+            public void propertyChange(PropertyChangeEvent evt) {
+                model.setFilter(decorator.getMatch());
             }
         });
Index: trunk/src/org/openstreetmap/josm/gui/widgets/CompileSearchTextDecorator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/CompileSearchTextDecorator.java	(revision 8979)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/CompileSearchTextDecorator.java	(revision 8979)
@@ -0,0 +1,74 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.widgets;
+
+import java.awt.Color;
+
+import javax.swing.UIManager;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.actions.search.SearchCompiler;
+
+/**
+ * Decorates a text component with an execution to the search compiler. Afterwards, a {@code "filter"} property change
+ * will be fired and the compiled search can be accessed with {@link #getMatch()}.
+ */
+public class CompileSearchTextDecorator implements DocumentListener {
+
+    private final JTextComponent textComponent;
+    private final String originalToolTipText;
+    private SearchCompiler.Match filter = null;
+
+    private CompileSearchTextDecorator(JTextComponent textComponent) {
+        this.textComponent = textComponent;
+        this.originalToolTipText = textComponent.getToolTipText();
+        textComponent.getDocument().addDocumentListener(this);
+    }
+
+    /**
+     * Decorates a text component with an execution to the search compiler. Afterwards, a {@code "filter"} property change
+     * will be fired and the compiled search can be accessed with {@link #getMatch()}.
+     * @param f the text component to decorate
+     * @return an instance of the decorator in order to access the compiled search via {@link #getMatch()}
+     */
+    public static CompileSearchTextDecorator decorate(JTextComponent f) {
+        return new CompileSearchTextDecorator(f);
+    }
+
+    private void setFilter() {
+        try {
+            textComponent.setBackground(UIManager.getColor("TextField.background"));
+            textComponent.setToolTipText(originalToolTipText);
+            filter = SearchCompiler.compile(textComponent.getText());
+        } catch (SearchCompiler.ParseError ex) {
+            textComponent.setBackground(new Color(255, 224, 224));
+            textComponent.setToolTipText(ex.getMessage());
+            filter = new SearchCompiler.Always();
+        }
+        textComponent.firePropertyChange("filter", 0, 1);
+    }
+
+    /**
+     * Returns the compiled search
+     * @return the compiled search
+     */
+    public SearchCompiler.Match getMatch() {
+        return filter;
+    }
+
+    @Override
+    public void insertUpdate(DocumentEvent e) {
+        setFilter();
+    }
+
+    @Override
+    public void removeUpdate(DocumentEvent e) {
+        setFilter();
+    }
+
+    @Override
+    public void changedUpdate(DocumentEvent e) {
+        setFilter();
+    }
+}
