Ticket #14923: preset-search-v3.patch

File preset-search-v3.patch, 10.0 KB (added by bafonins, 4 months ago)

Fixed the issue and added tests

  • src/org/openstreetmap/josm/actions/search/SearchAction.java

     
    454454                .addKeyword("type:node", "type:node ", tr("all nodes"))
    455455                .addKeyword("type:way", "type:way ", tr("all ways"))
    456456                .addKeyword("type:relation", "type:relation ", tr("all relations"))
     457                .addKeyword("preset:water", "preset:water", tr("all objects that use the water preset"))
     458                .addKeyword("preset:\"fast food\"", "preset:\"fast food\"", tr("all objects that use the fast food preset"))
    457459                .addKeyword("closed", "closed ", tr("all closed ways"))
    458460                .addKeyword("untagged", "untagged ", tr("object without useful tags")),
    459461                GBC.eol());
     
    888890    public List<ActionParameter<?>> getActionParameters() {
    889891        return Collections.<ActionParameter<?>>singletonList(new SearchSettingsActionParameter(SEARCH_EXPRESSION));
    890892    }
    891 }
     893}
     894 No newline at end of file
  • src/org/openstreetmap/josm/actions/search/SearchCompiler.java

     
    2020import java.util.regex.Matcher;
    2121import java.util.regex.Pattern;
    2222import java.util.regex.PatternSyntaxException;
     23import java.util.stream.Collectors;
    2324
    2425import org.openstreetmap.josm.Main;
    2526import org.openstreetmap.josm.actions.search.PushbackTokenizer.Range;
     
    3839import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
    3940import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
    4041import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
     42import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
     43import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
    4144import org.openstreetmap.josm.tools.AlphanumComparator;
    4245import org.openstreetmap.josm.tools.Geometry;
    4346import org.openstreetmap.josm.tools.UncheckedParseException;
     
    115118        private final Collection<String> keywords = Arrays.asList("id", "version", "type", "user", "role",
    116119                "changeset", "nodes", "ways", "tags", "areasize", "waylength", "modified", "deleted", "selected",
    117120                "incomplete", "untagged", "closed", "new", "indownloadedarea",
    118                 "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole");
     121                "allindownloadedarea", "inview", "allinview", "timestamp", "nth", "nth%", "hasRole", "preset");
    119122
    120123        @Override
    121124        public Match get(String keyword, PushbackTokenizer tokenizer) throws ParseError {
     
    151154                        return new Version(tokenizer);
    152155                    case "type":
    153156                        return new ExactType(tokenizer.readTextOrNumber());
     157                    case "preset":
     158                        return new Preset(tokenizer.readTextOrNumber());
    154159                    case "user":
    155160                        return new UserMatch(tokenizer.readTextOrNumber());
    156161                    case "role":
     
    15541559        }
    15551560    }
    15561561
     1562    /**
     1563     * Matches presets.
     1564     */
     1565    private static class Preset extends Match {
     1566        private List<TaggingPreset> presets;
     1567
     1568        Preset(String presetName) throws ParseError {
     1569
     1570            if (presetName == null) {
     1571                throw new ParseError("The name of the preset is required");
     1572            }
     1573
     1574            this.presets = TaggingPresets.getTaggingPresets()
     1575                    .stream()
     1576                    .filter(preset -> presetName.equalsIgnoreCase(preset.getSimpleName()))
     1577                    .collect(Collectors.toList());
     1578
     1579            if (this.presets.isEmpty()) {
     1580                throw new ParseError(tr("Unknown preset name: ") + presetName);
     1581            }
     1582        }
     1583
     1584        /**
     1585         * Since presets can have common names, the primitive is considered to
     1586         * belong to a certain preset if it matches at least one of them.
     1587         */
     1588        @Override
     1589        public boolean match(OsmPrimitive osm) {
     1590            for (TaggingPreset p : this.presets) {
     1591                if (p.test(osm)) {
     1592                    return true;
     1593                }
     1594            }
     1595
     1596            return false;
     1597        }
     1598    }
     1599
    15571600    public static class ParseError extends Exception {
    15581601        public ParseError(String msg) {
    15591602            super(msg);
     
    18031846            return forKey + '"' + escapeStringForSearch(value) + '"';
    18041847        }
    18051848    }
    1806 }
     1849}
     1850 No newline at end of file
  • src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java

     
    173173    }
    174174
    175175    /**
     176     * Returns the non translated name without any prefixes.
     177     * @return returns the non translated name without any prefixes.
     178     */
     179    public String getSimpleName() {
     180        return this.name;
     181    }
     182
     183    /**
    176184     * Returns the preset icon (16px).
    177185     * @return The preset icon, or {@code null} if none defined
    178186     * @since 6403
     
    634642        ToolbarPreferences.ActionParser actionParser = new ToolbarPreferences.ActionParser(null);
    635643        return actionParser.saveAction(new ToolbarPreferences.ActionDefinition(this));
    636644    }
    637 }
     645}
     646 No newline at end of file
  • test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java

     
    1010import java.nio.charset.StandardCharsets;
    1111import java.nio.file.Files;
    1212import java.nio.file.Paths;
     13import java.util.Collections;
    1314
    1415import org.junit.Rule;
    1516import org.junit.Test;
     
    3031import org.openstreetmap.josm.data.osm.User;
    3132import org.openstreetmap.josm.data.osm.Way;
    3233import org.openstreetmap.josm.data.osm.WayData;
     34import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
     35import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
     36import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
     37import org.openstreetmap.josm.gui.tagging.presets.items.Key;
    3338import org.openstreetmap.josm.testutils.JOSMTestRules;
    3439import org.openstreetmap.josm.tools.date.DateUtils;
    3540
     
    490495    public void testEnumExactKeyValueMode() {
    491496        TestUtils.superficialEnumCodeCoverage(ExactKeyValue.Mode.class);
    492497    }
    493 }
     498
     499    /**
     500     * Robustness test for preset searching. Ensures that the query 'preset:' is not accepted.
     501     * @throws ParseError always
     502     */
     503    @Test(expected = ParseError.class)
     504    public void testPresetSearchMissingValue() throws ParseError {
     505        SearchSetting settings = new SearchSetting();
     506        settings.text = "preset:";
     507        settings.mapCSSSearch = false;
     508
     509        SearchCompiler.compile(settings);
     510    }
     511
     512    /**
     513     * Robustness test for preset searching. Validates that it is not possible to search for
     514     * non existing presets.
     515     * @throws ParseError always
     516     */
     517    @Test(expected = ParseError.class)
     518    public void testPresetNotExist() throws ParseError {
     519        String testPresetName = "namethatshouldnotexist";
     520        SearchSetting settings = new SearchSetting();
     521        settings.text = "preset:" + testPresetName;
     522        settings.mapCSSSearch = false;
     523
     524        // load presets
     525        TaggingPresets.readFromPreferences();
     526
     527        SearchCompiler.compile(settings);
     528    }
     529
     530    /**
     531     * Robustness tests for preset searching. Ensures that combined presed names (having more than
     532     * 1 words) must be enclosed in " .
     533     * @throws ParseError always
     534     */
     535    @Test(expected = ParseError.class)
     536    public void testPresetMultipleWords() throws ParseError{
     537        String combinedPresetname = "Fast Food";
     538        SearchSetting settings = new SearchSetting();
     539        settings.text = "preset:" + combinedPresetname;
     540        settings.mapCSSSearch = false;
     541
     542        SearchCompiler.compile(settings);
     543    }
     544
     545    /**
     546     * Ensures that correct primitives are matched against the specified preset.
     547     * @throws ParseError if an error has been encountered while compiling
     548     */
     549    @Test
     550    public void testPreset() throws ParseError {
     551        final String presetName = "testPresetName";
     552        final String key = "test_key1";
     553        final String val = "test_val1";
     554
     555        Key key1 = new Key();
     556        key1.key = key;
     557        key1.value = val;
     558
     559        TaggingPreset testPreset = new TaggingPreset();
     560        testPreset.name = presetName;
     561        testPreset.types = Collections.singleton(TaggingPresetType.NODE);
     562        testPreset.data.add(key1);
     563
     564        TaggingPresets.readFromPreferences();
     565        TaggingPresets.addTaggingPresets(Collections.singleton(testPreset));
     566
     567        String[] queries = {
     568                "preset:" + presetName,
     569                "preset: " + presetName,
     570                "preset:" + "\"" + presetName + "\""
     571        };
     572
     573        for (int i = 0; i < queries.length; i++) {
     574            SearchContext ctx = new SearchContext(queries[i]);
     575            ctx.n1.put(key, val);
     576            ctx.n2.put(key, val);
     577
     578            for (OsmPrimitive osm : new OsmPrimitive[] { ctx.n1, ctx.n2 }) {
     579                ctx.match(osm, true);
     580            }
     581
     582            for (OsmPrimitive osm : new OsmPrimitive[] { ctx.r1, ctx.r2, ctx.w1, ctx.w2 }) {
     583                ctx.match(osm, false);
     584            }
     585        }
     586    }
     587}
     588 No newline at end of file