Index: trunk/resources/data/tagging-preset.xsd
===================================================================
--- trunk/resources/data/tagging-preset.xsd	(revision 17661)
+++ trunk/resources/data/tagging-preset.xsd	(revision 17662)
@@ -156,4 +156,13 @@
             </annotation>
         </attribute>
+        <attribute name="match_expression" type="string">
+            <annotation>
+                <documentation>
+                    Additional criteria for matching primitives. Specified in <a href="https://josm.openstreetmap.de/wiki/Help/Action/Search">JOSM search syntax</a>.
+                    For instance, a preset with <code>match_expression="foo=bar"</code> requires OSM objects to have the tag <code>foo=bar</code>.
+                    You may want to use the <code>match_expression</code> to exclude certain OSM objects, for instance when a more specific preset is present.
+                </documentation>
+            </annotation>
+        </attribute>
         <attribute name="preset_name_label" type="boolean">
             <annotation>
Index: trunk/src/org/openstreetmap/josm/data/osm/Tagged.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Tagged.java	(revision 17661)
+++ trunk/src/org/openstreetmap/josm/data/osm/Tagged.java	(revision 17662)
@@ -233,3 +233,58 @@
         return OsmUtils.isFalse(get(key));
     }
+
+    /**
+     * Returns a Tagged instance for the given tag map
+     * @param tags the tag map
+     * @return a Tagged instance for the given tag map
+     */
+    static Tagged ofMap(Map<String, String> tags) {
+        return new Tagged() {
+
+            @Override
+            public String get(String key) {
+                return tags.get(key);
+            }
+
+            @Override
+            public Map<String, String> getKeys() {
+                return tags;
+            }
+
+            @Override
+            public Collection<String> keySet() {
+                return tags.keySet();
+            }
+
+            @Override
+            public void put(String key, String value) {
+                tags.put(key, value);
+            }
+
+            @Override
+            public void setKeys(Map<String, String> keys) {
+                tags.putAll(keys);
+            }
+
+            @Override
+            public boolean hasKeys() {
+                return !tags.isEmpty();
+            }
+
+            @Override
+            public int getNumKeys() {
+                return tags.size();
+            }
+
+            @Override
+            public void remove(String key) {
+                tags.remove(key);
+            }
+
+            @Override
+            public void removeAll() {
+                tags.clear();
+            }
+        };
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(revision 17661)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(revision 17662)
@@ -48,4 +48,5 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Tag;
+import org.openstreetmap.josm.data.osm.Tagged;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.search.SearchCompiler;
@@ -135,4 +136,5 @@
     public transient TemplateEntry nameTemplate;
     public transient Match nameTemplateFilter;
+    public transient Match matchExpression;
 
     /**
@@ -288,4 +290,13 @@
     }
 
+    public void setMatch_expression(String filter) throws SAXException {
+        try {
+            this.matchExpression = SearchCompiler.compile(filter);
+        } catch (SearchParseError e) {
+            Logging.error("Error while parsing" + filter + ": " + e.getMessage());
+            throw new SAXException(e);
+        }
+    }
+
     private static class PresetPanel extends JPanel {
         private boolean hasElements;
@@ -646,4 +657,6 @@
         if ((onlyShowable && !isShowable()) || !typeMatches(t)) {
             return false;
+        } else if (matchExpression != null && !matchExpression.match(Tagged.ofMap(tags))) {
+            return false;
         } else {
             return TaggingPresetItem.matches(data, tags);
Index: trunk/test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetTest.java	(revision 17662)
+++ trunk/test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetTest.java	(revision 17662)
@@ -0,0 +1,52 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.tagging.presets;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.OsmUtils;
+import org.openstreetmap.josm.data.osm.search.SearchCompiler;
+import org.openstreetmap.josm.gui.tagging.presets.items.Key;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import java.util.EnumSet;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests of {@code TaggingPreset}
+ */
+class TaggingPresetTest {
+
+    /**
+     * Setup test.
+     */
+    @RegisterExtension
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules();
+
+    /**
+     * Tests {@link TaggingPreset#test(IPrimitive)}
+     */
+    @Test
+    void test() throws Exception {
+        Key key = new Key();
+        key.key = "railway";
+        key.value = "tram_stop";
+        TaggingPreset preset = new TaggingPreset();
+        preset.data.add(key);
+
+        assertFalse(preset.test(OsmUtils.createPrimitive("node foo=bar")));
+        assertTrue(preset.test(OsmUtils.createPrimitive("node railway=tram_stop")));
+
+        preset.types = EnumSet.of(TaggingPresetType.NODE);
+        assertTrue(preset.test(OsmUtils.createPrimitive("node railway=tram_stop")));
+        assertFalse(preset.test(OsmUtils.createPrimitive("way railway=tram_stop")));
+
+        preset.matchExpression = SearchCompiler.compile("-public_transport");
+        assertTrue(preset.test(OsmUtils.createPrimitive("node railway=tram_stop")));
+        assertFalse(preset.test(OsmUtils.createPrimitive("node railway=tram_stop public_transport=stop_position")));
+    }
+}
