Index: src/org/openstreetmap/josm/actions/search/SearchAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/search/SearchAction.java	(revision 12453)
+++ src/org/openstreetmap/josm/actions/search/SearchAction.java	(working copy)
@@ -35,7 +35,9 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
+import javax.swing.SwingUtilities;
 import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
 import javax.swing.text.JTextComponent;
 
 import org.openstreetmap.josm.Main;
@@ -54,6 +56,8 @@
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences.ActionParser;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSelector;
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
 import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
 import org.openstreetmap.josm.tools.GBC;
@@ -231,12 +235,30 @@
 
                     @Override
                     public void mouseClicked(MouseEvent e) {
-                        try {
-                            JTextComponent tf = hcb.getEditorComponent();
-                            tf.getDocument().insertString(tf.getCaretPosition(), ' ' + insertText, null);
-                        } catch (BadLocationException ex) {
-                            throw new JosmRuntimeException(ex.getMessage(), ex);
+                        JTextComponent tf = hcb.getEditorComponent();
+
+                        /*
+                         * Make sure that the focus is transferred to the search text field
+                         * from the selector component.
+                         */
+                        if (!tf.hasFocus()) {
+                            tf.requestFocusInWindow();
                         }
+
+                        /*
+                         * In order to make interaction with the search dialog simpler,
+                         * we make sure that if autocompletion triggers and the text field is
+                         * not in focus, the correct area is selected. We first request focus
+                         * and then execute the selection logic. invokeLater allows us to
+                         * defer the selection until waiting for focus.
+                         */
+                        SwingUtilities.invokeLater(() -> {
+                            try {
+                                tf.getDocument().insertString(tf.getCaretPosition(), ' ' + insertText, null);
+                            } catch (BadLocationException ex) {
+                                throw new JosmRuntimeException(ex.getMessage(), ex);
+                            }
+                        });
                     }
                 });
             }
@@ -244,19 +266,25 @@
         }
     }
 
+    /**
+     * Builds and shows the search dialog.
+     * @param initialValues A set of initial values needed in order to initialize the search dialog.
+     *                      If is {@code null}, then default settings are used.
+     * @return Returns {@link SearchAction} object containing parameters of the search.
+     */
     public static SearchSetting showSearchDialog(SearchSetting initialValues) {
         if (initialValues == null) {
             initialValues = new SearchSetting();
         }
-        // -- prepare the combo box with the search expressions
-        //
+
+        // prepare the combo box with the search expressions
         JLabel label = new JLabel(initialValues instanceof Filter ? tr("Filter string:") : tr("Search string:"));
-        final HistoryComboBox hcbSearchString = new HistoryComboBox();
-        final String tooltip = tr("Enter the search expression");
+        HistoryComboBox hcbSearchString = new HistoryComboBox();
+        String tooltip = tr("Enter the search expression");
         hcbSearchString.setText(initialValues.text);
         hcbSearchString.setToolTipText(tooltip);
+
         // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
-        //
         List<String> searchExpressionHistory = getSearchExpressionHistory();
         Collections.reverse(searchExpressionHistory);
         hcbSearchString.setPossibleItems(searchExpressionHistory);
@@ -273,21 +301,19 @@
         bg.add(remove);
         bg.add(inSelection);
 
-        final JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
+        JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
         JCheckBox allElements = new JCheckBox(tr("all objects"), initialValues.allElements);
         allElements.setToolTipText(tr("Also include incomplete and deleted objects in search."));
         JCheckBox addOnToolbar = new JCheckBox(tr("add toolbar button"), false);
 
-        final JRadioButton standardSearch = new JRadioButton(tr("standard"), !initialValues.regexSearch && !initialValues.mapCSSSearch);
-        final JRadioButton regexSearch = new JRadioButton(tr("regular expression"), initialValues.regexSearch);
-        final JRadioButton mapCSSSearch = new JRadioButton(tr("MapCSS selector"), initialValues.mapCSSSearch);
-        final ButtonGroup bg2 = new ButtonGroup();
+        JRadioButton standardSearch = new JRadioButton(tr("standard"), !initialValues.regexSearch && !initialValues.mapCSSSearch);
+        JRadioButton regexSearch = new JRadioButton(tr("regular expression"), initialValues.regexSearch);
+        JRadioButton mapCSSSearch = new JRadioButton(tr("MapCSS selector"), initialValues.mapCSSSearch);
+        ButtonGroup bg2 = new ButtonGroup();
         bg2.add(standardSearch);
         bg2.add(regexSearch);
         bg2.add(mapCSSSearch);
 
-        JPanel left = new JPanel(new GridBagLayout());
-
         JPanel selectionSettings = new JPanel(new GridBagLayout());
         selectionSettings.setBorder(BorderFactory.createTitledBorder(tr("Selection settings")));
         selectionSettings.add(replace, GBC.eol().anchor(GBC.WEST).fill(GBC.HORIZONTAL));
@@ -299,6 +325,8 @@
         additionalSettings.setBorder(BorderFactory.createTitledBorder(tr("Additional settings")));
         additionalSettings.add(caseSensitive, GBC.eol().anchor(GBC.WEST).fill(GBC.HORIZONTAL));
 
+        JPanel left = new JPanel(new GridBagLayout());
+
         left.add(selectionSettings, GBC.eol().fill(GBC.BOTH));
         left.add(additionalSettings, GBC.eol().fill(GBC.BOTH));
 
@@ -315,14 +343,21 @@
             left.add(searchOptions, GBC.eol().fill(GBC.BOTH));
         }
 
-        final JPanel right = SearchAction.buildHintsSection(hcbSearchString);
-        final JPanel top = new JPanel(new GridBagLayout());
+        JPanel right = SearchAction.buildHintsSection(hcbSearchString);
+        JPanel top = new JPanel(new GridBagLayout());
         top.add(label, GBC.std().insets(0, 0, 5, 0));
         top.add(hcbSearchString, GBC.eol().fill(GBC.HORIZONTAL));
 
-        final JTextComponent editorComponent = hcbSearchString.getEditorComponent();
-        editorComponent.getDocument().addDocumentListener(new AbstractTextComponentValidator(editorComponent) {
+        JTextComponent editorComponent = hcbSearchString.getEditorComponent();
+        Document document = editorComponent.getDocument();
 
+        /*
+         * Setup the logic to validate the contents of the search text field which is executed
+         * every time the content of the field has changed. If the query is incorrect, then
+         * the text field is colored red.
+         */
+        document.addDocumentListener(new AbstractTextComponentValidator(editorComponent) {
+
             @Override
             public void validate() {
                 if (!isValid()) {
@@ -348,10 +383,20 @@
             }
         });
 
-        final JPanel p = new JPanel(new GridBagLayout());
+        /*
+         * Setup the logic to append preset queries to the search text field according to
+         * selected preset by the user. Every query is of the form ' group/sub-group/.../presetName'
+         * if the corresponding group of the preset exists, otherwise it is simply ' presetName'.
+         */
+        TaggingPresetSelector selector = new TaggingPresetSelector(false, false);
+        selector.setBorder(BorderFactory.createTitledBorder(tr("Search by preset")));
+        selector.setDblClickListener(ev -> setPresetDblClickListener(selector, editorComponent));
+
+        JPanel p = new JPanel(new GridBagLayout());
         p.add(top, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 0));
         p.add(left, GBC.std().anchor(GBC.NORTH).insets(5, 10, 10, 0).fill(GBC.VERTICAL));
-        p.add(right, GBC.eol().fill(GBC.BOTH).insets(0, 10, 0, 0));
+        p.add(right, GBC.std().fill(GBC.BOTH).insets(0, 10, 0, 0));
+        p.add(selector, GBC.eol().fill(GBC.BOTH).insets(0, 10, 0, 0));
 
         ExtendedDialog dialog = new ExtendedDialog(
                 Main.parent,
@@ -390,16 +435,22 @@
         if (dialog.showDialog().getValue() != 1) return null;
 
         // User pressed OK - let's perform the search
-        SearchMode mode = replace.isSelected() ? SearchAction.SearchMode.replace
-                : (add.isSelected() ? SearchAction.SearchMode.add
-                        : (remove.isSelected() ? SearchAction.SearchMode.remove : SearchAction.SearchMode.in_selection));
         initialValues.text = hcbSearchString.getText();
-        initialValues.mode = mode;
         initialValues.caseSensitive = caseSensitive.isSelected();
         initialValues.allElements = allElements.isSelected();
         initialValues.regexSearch = regexSearch.isSelected();
         initialValues.mapCSSSearch = mapCSSSearch.isSelected();
 
+        if (inSelection.isSelected()) {
+            initialValues.mode = SearchAction.SearchMode.in_selection;
+        } else if (replace.isSelected()) {
+            initialValues.mode = SearchAction.SearchMode.replace;
+        } else if (add.isSelected()) {
+            initialValues.mode = SearchAction.SearchMode.add;
+        } else {
+            initialValues.mode = SearchAction.SearchMode.remove;
+        }
+
         if (addOnToolbar.isSelected()) {
             ToolbarPreferences.ActionDefinition aDef =
                     new ToolbarPreferences.ActionDefinition(Main.main.menu.search);
@@ -413,6 +464,7 @@
             // add custom search button to toolbar preferences
             Main.toolbar.addCustomButton(res, -1, false);
         }
+
         return initialValues;
     }
 
@@ -458,6 +510,12 @@
                 .addKeyword("untagged", "untagged ", tr("object without useful tags")),
                 GBC.eol());
             hintPanel.add(new SearchKeywordRow(hcbSearchString)
+                    .addKeyword("preset:\"Annotation/Address\"", "preset:\"Annotation/Address\"",
+                            tr("all objects that use the address preset"))
+                    .addKeyword("preset:\"Geography/Nature/*\"", "preset:\"Geography/Nature/*\"",
+                            tr("all objects that use any preset under the Geography/Nature group")),
+                    GBC.eol().anchor(GBC.CENTER));
+            hintPanel.add(new SearchKeywordRow(hcbSearchString)
                 .addTitle(tr("metadata"))
                 .addKeyword("user:", "user:", tr("objects changed by user", "user:anonymous"))
                 .addKeyword("id:", "id:", tr("objects with given ID"), "id:0 (new objects)")
@@ -575,6 +633,43 @@
     }
 
     /**
+     *
+     * @param selector Selector component that the user interacts with
+     * @param searchEditor Editor for search queries
+     */
+    private static void setPresetDblClickListener(TaggingPresetSelector selector, JTextComponent searchEditor) {
+        TaggingPreset selectedPreset = selector.getSelectedPresetAndUpdateClassification();
+
+        if (selectedPreset == null) {
+            return;
+        }
+
+        /*
+         * Make sure that the focus is transferred to the search text field
+         * from the selector component.
+         */
+        searchEditor.requestFocusInWindow();
+
+        /*
+         * In order to make interaction with the search dialog simpler,
+         * we make sure that if autocompletion triggers and the text field is
+         * not in focus, the correct area is selected. We first request focus
+         * and then execute the selection logic. invokeLater allows us to
+         * defer the selection until waiting for focus.
+         */
+        SwingUtilities.invokeLater(() -> {
+            int textOffset = searchEditor.getCaretPosition();
+            String presetSearchQuery = " preset:" +
+                    "\"" + selectedPreset.getRawName() + "\"";
+            try {
+                searchEditor.getDocument().insertString(textOffset, presetSearchQuery, null);
+            } catch (BadLocationException e1) {
+                throw new JosmRuntimeException(e1.getMessage(), e1);
+            }
+        });
+    }
+
+    /**
      * Interfaces implementing this may receive the result of the current search.
      * @author Michael Zangl
      * @since 10457
@@ -749,6 +844,10 @@
         }
     }
 
+    /**
+     * This class defines a set of parameters that is used to
+     * perform search within the search dialog.
+     */
     public static class SearchSetting {
         public String text;
         public SearchMode mode;
@@ -810,6 +909,24 @@
             return Objects.hash(text, mode, caseSensitive, regexSearch, mapCSSSearch, allElements);
         }
 
+        /**
+         * <p>Transforms a string following a certain format, namely "[R | A | D | S][C?,R?,A?,M?] [a-zA-Z]"
+         * where the first part defines the mode of the search, see {@link SearchMode}, the second defines
+         * a set of attributes within the {@code SearchSetting} class and the second is the search query.
+         * <p>
+         * Attributes are as follows:
+         * <ul>
+         *     <li>C - if search is case sensitive
+         *     <li>R - if the regex syntax is used
+         *     <li>A - if all objects are considered
+         *     <li>M - if the mapCSS syntax is used
+         * </ul>
+         * <p>For example, "RC type:node" is a valid string representation of an object that replaces the
+         * current selection, is case sensitive and searches for all objects of type node.
+         * @param s A string representation of a {@code SearchSetting} object
+         *          from which the object must be built.
+         * @return A {@code SearchSetting} defined by the input string.
+         */
         public static SearchSetting readFromString(String s) {
             if (s.isEmpty())
                 return null;
@@ -851,6 +968,11 @@
             return result;
         }
 
+        /**
+         * Builds a string representation of the {@code SearchSetting} object,
+         * see {@link #readFromString(String)} for more details.
+         * @return A string representation of the {@code SearchSetting} object.
+         */
         public String writeToString() {
             if (text == null || text.isEmpty())
                 return "";
@@ -888,4 +1010,4 @@
     public List<ActionParameter<?>> getActionParameters() {
         return Collections.<ActionParameter<?>>singletonList(new SearchSettingsActionParameter(SEARCH_EXPRESSION));
     }
-}
+}
\ No newline at end of file
Index: src/org/openstreetmap/josm/actions/search/SearchCompiler.java
===================================================================
--- src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(revision 12453)
+++ src/org/openstreetmap/josm/actions/search/SearchCompiler.java	(working copy)
@@ -20,6 +20,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.search.PushbackTokenizer.Range;
@@ -38,6 +39,10 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetMenu;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSeparator;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.tools.AlphanumComparator;
 import org.openstreetmap.josm.tools.Geometry;
 import org.openstreetmap.josm.tools.UncheckedParseException;
@@ -115,7 +120,7 @@
         private final Collection<String> keywords = Arrays.asList("id", "version", "type", "user", "role",
                 "changeset", "nodes", "ways", "tags", "areasize", "waylength", "modified", "deleted", "selected",
                 "incomplete", "untagged", "closed", "new", "indownloadedarea",
-                "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole");
+                "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole", "preset");
 
         @Override
         public Match get(String keyword, PushbackTokenizer tokenizer) throws ParseError {
@@ -151,6 +156,8 @@
                         return new Version(tokenizer);
                     case "type":
                         return new ExactType(tokenizer.readTextOrNumber());
+                    case "preset":
+                        return new Preset(tokenizer.readTextOrNumber());
                     case "user":
                         return new UserMatch(tokenizer.readTextOrNumber());
                     case "role":
@@ -1554,6 +1561,65 @@
         }
     }
 
+    /**
+     * Matches presets.
+     */
+    private static class Preset extends Match {
+        private final List<TaggingPreset> presets;
+
+        Preset(String presetName) throws ParseError {
+
+            if (presetName == null || presetName.equals("")) {
+                throw new ParseError("The name of the preset is required");
+            }
+
+            int wildCardIdx = presetName.lastIndexOf("*");
+            int length = presetName.length() - 1;
+
+            /*
+             * Match strictly (simply comparing the names) if there is no '*' symbol
+             * at the end of the name or '*' is a part of the preset name.
+             */
+            boolean matchStrictly = wildCardIdx == -1 || wildCardIdx != length;
+
+            this.presets = TaggingPresets.getTaggingPresets()
+                    .stream()
+                    .filter(preset -> !(preset instanceof TaggingPresetMenu || preset instanceof TaggingPresetSeparator))
+                    .filter(preset -> this.presetNameMatch(presetName, preset, matchStrictly))
+                    .collect(Collectors.toList());
+
+            if (this.presets.isEmpty()) {
+                throw new ParseError(tr("Unknown preset name: ") + presetName);
+            }
+        }
+
+        @Override
+        public boolean match(OsmPrimitive osm) {
+            for (TaggingPreset preset : this.presets) {
+                if (preset.test(osm)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private boolean presetNameMatch(String name, TaggingPreset preset, boolean matchStrictly) {
+            if (matchStrictly) {
+                return name.equalsIgnoreCase(preset.getRawName());
+            }
+
+            try {
+                String groupSuffix = name.substring(0, name.length() - 2); // try to remove '/*'
+                TaggingPresetMenu group = preset.group;
+
+                return group != null && groupSuffix.equalsIgnoreCase(group.getRawName());
+            } catch (StringIndexOutOfBoundsException ex) {
+                return false;
+            }
+        }
+    }
+
     public static class ParseError extends Exception {
         public ParseError(String msg) {
             super(msg);
@@ -1803,4 +1869,4 @@
             return forKey + '"' + escapeStringForSearch(value) + '"';
         }
     }
-}
+}
\ No newline at end of file
Index: src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
===================================================================
--- src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(revision 12453)
+++ src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java	(working copy)
@@ -634,4 +634,4 @@
         ToolbarPreferences.ActionParser actionParser = new ToolbarPreferences.ActionParser(null);
         return actionParser.saveAction(new ToolbarPreferences.ActionDefinition(this));
     }
-}
+}
\ No newline at end of file
Index: test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java	(revision 12453)
+++ test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java	(working copy)
@@ -7,9 +7,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -30,6 +34,11 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WayData;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
+import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetMenu;
+import org.openstreetmap.josm.gui.tagging.presets.items.Key;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
@@ -490,4 +499,184 @@
     public void testEnumExactKeyValueMode() {
         TestUtils.superficialEnumCodeCoverage(ExactKeyValue.Mode.class);
     }
-}
+
+    /**
+     * Robustness test for preset searching. Ensures that the query 'preset:' is not accepted.
+     * @throws ParseError always
+     */
+    @Test(expected = ParseError.class)
+    public void testPresetSearchMissingValue() throws ParseError {
+        SearchSetting settings = new SearchSetting();
+        settings.text = "preset:";
+        settings.mapCSSSearch = false;
+
+        TaggingPresets.readFromPreferences();
+
+        SearchCompiler.compile(settings);
+    }
+
+    /**
+     * Robustness test for preset searching. Validates that it is not possible to search for
+     * non existing presets.
+     * @throws ParseError always
+     */
+    @Test(expected = ParseError.class)
+    public void testPresetNotExist() throws ParseError {
+        String testPresetName = "groupnamethatshouldnotexist/namethatshouldnotexist";
+        SearchSetting settings = new SearchSetting();
+        settings.text = "preset:" + testPresetName;
+        settings.mapCSSSearch = false;
+
+        // load presets
+        TaggingPresets.readFromPreferences();
+
+        SearchCompiler.compile(settings);
+    }
+
+    /**
+     * Robustness tests for preset searching. Ensures that combined preset names (having more than
+     * 1 word) must be enclosed with " .
+     * @throws ParseError always
+     */
+    @Test(expected = ParseError.class)
+    public void testPresetMultipleWords() throws ParseError {
+        TaggingPreset testPreset = new TaggingPreset();
+        testPreset.name = "Test Combined Preset Name";
+        testPreset.group = new TaggingPresetMenu();
+        testPreset.group.name = "TestGroupName";
+
+        String combinedPresetname = testPreset.getRawName();
+        SearchSetting settings = new SearchSetting();
+        settings.text = "preset:" + combinedPresetname;
+        settings.mapCSSSearch = false;
+
+        // load presets
+        TaggingPresets.readFromPreferences();
+
+        SearchCompiler.compile(settings);
+    }
+
+
+    /**
+     * Ensures that correct presets are stored in the {@link org.openstreetmap.josm.actions.search.SearchCompiler.Preset}
+     * class against which the osm primitives are tested.
+     * @throws ParseError if an error has been encountered while compiling
+     * @throws NoSuchFieldException if there is no field called 'presets'
+     * @throws IllegalAccessException if cannot access the field where all matching presets are stored
+     */
+    @Test
+    public void testPresetLookup() throws ParseError, NoSuchFieldException, IllegalAccessException {
+        TaggingPreset testPreset = new TaggingPreset();
+        testPreset.name = "Test Preset Name";
+        testPreset.group = new TaggingPresetMenu();
+        testPreset.group.name = "Test Preset Group Name";
+
+        String query = "preset:" +
+                "\"" + testPreset.getRawName() + "\"";
+        SearchSetting settings = new SearchSetting();
+        settings.text = query;
+        settings.mapCSSSearch = false;
+
+        // load presets and add the test preset
+        TaggingPresets.readFromPreferences();
+        TaggingPresets.addTaggingPresets(Collections.singletonList(testPreset));
+
+        Match match = SearchCompiler.compile(settings);
+
+        // access the private field where all matching presets are stored
+        // and ensure that indeed the correct ones are there
+        Field field = match.getClass().getDeclaredField("presets");
+        field.setAccessible(true);
+        Collection<TaggingPreset> foundPresets = (Collection<TaggingPreset>) field.get(match);
+
+        assertEquals(1, foundPresets.size());
+        assertTrue(foundPresets.contains(testPreset));
+    }
+
+    /**
+     * Ensures that the wildcard search works and that correct presets are stored in
+     * the {@link org.openstreetmap.josm.actions.search.SearchCompiler.Preset} class against which
+     * the osm primitives are tested.
+     * @throws ParseError if an error has been encountered while compiling
+     * @throws NoSuchFieldException if there is no field called 'presets'
+     * @throws IllegalAccessException if cannot access the field where all matching presets are stored
+     */
+    @Test
+    public void testPresetLookupWildcard() throws ParseError, NoSuchFieldException, IllegalAccessException {
+        TaggingPresetMenu group = new TaggingPresetMenu();
+        group.name = "TestPresetGroup";
+
+        TaggingPreset testPreset1 = new TaggingPreset();
+        testPreset1.name = "TestPreset1";
+        testPreset1.group = group;
+
+        TaggingPreset testPreset2 = new TaggingPreset();
+        testPreset2.name = "TestPreset2";
+        testPreset2.group = group;
+
+        TaggingPreset testPreset3 = new TaggingPreset();
+        testPreset3.name = "TestPreset3";
+        testPreset3.group = group;
+
+        String query = "preset:" + "\"" + group.getRawName() + "/*\"";
+        SearchSetting settings = new SearchSetting();
+        settings.text = query;
+        settings.mapCSSSearch = false;
+
+        TaggingPresets.readFromPreferences();
+        TaggingPresets.addTaggingPresets(Arrays.asList(testPreset1, testPreset2, testPreset3));
+
+        Match match = SearchCompiler.compile(settings);
+
+        // access the private field where all matching presets are stored
+        // and ensure that indeed the correct ones are there
+        Field field = match.getClass().getDeclaredField("presets");
+        field.setAccessible(true);
+        Collection<TaggingPreset> foundPresets = (Collection<TaggingPreset>) field.get(match);
+
+        assertEquals(3, foundPresets.size());
+        assertTrue(foundPresets.contains(testPreset1));
+        assertTrue(foundPresets.contains(testPreset2));
+        assertTrue(foundPresets.contains(testPreset3));
+    }
+
+    /**
+     * Ensures that correct primitives are matched against the specified preset.
+     * @throws ParseError if an error has been encountered while compiling
+     */
+    @Test
+    public void testPreset() throws ParseError {
+        final String presetName = "Test Preset Name";
+        final String presetGroupName = "Test Preset Group";
+        final String key = "test_key1";
+        final String val = "test_val1";
+
+        Key key1 = new Key();
+        key1.key = key;
+        key1.value = val;
+
+        TaggingPreset testPreset = new TaggingPreset();
+        testPreset.name = presetName;
+        testPreset.types = Collections.singleton(TaggingPresetType.NODE);
+        testPreset.data.add(key1);
+        testPreset.group = new TaggingPresetMenu();
+        testPreset.group.name = presetGroupName;
+
+        TaggingPresets.readFromPreferences();
+        TaggingPresets.addTaggingPresets(Collections.singleton(testPreset));
+
+        String query = "preset:" + "\"" + testPreset.getRawName() + "\"";
+
+        SearchContext ctx = new SearchContext(query);
+        ctx.n1.put(key, val);
+        ctx.n2.put(key, val);
+
+        for (OsmPrimitive osm : new OsmPrimitive[] {ctx.n1, ctx.n2}) {
+            ctx.match(osm, true);
+        }
+
+        for (OsmPrimitive osm : new OsmPrimitive[] {ctx.r1, ctx.r2, ctx.w1, ctx.w2}) {
+            ctx.match(osm, false);
+        }
+    }
+}
\ No newline at end of file
