Ticket #18679: 18679.patch

File 18679.patch, 16.9 KB (added by simon04, 6 years ago)
  • src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java

    commit fcd8204971c64c1d217ae7a946e254d50b1b5332
    Author: Simon Legner <Simon.Legner@gmail.com>
    Date:   2020-02-05 22:06:43 +0100
    
        fix #18679
    
    diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterManager.java
    index c5e1fd9f6..2c16604eb 100644
    a b  
    77import java.util.ArrayList;
    88import java.util.Arrays;
    99import java.util.Collection;
    10 import java.util.Comparator;
    11 import java.util.Iterator;
     10import java.util.Collections;
    1211import java.util.List;
    1312import java.util.Map;
    1413import java.util.NavigableSet;
    1514import java.util.Objects;
    16 import java.util.Set;
    1715import java.util.TreeMap;
    1816import java.util.TreeSet;
    1917import java.util.function.Consumer;
    2018import java.util.regex.Matcher;
    2119import java.util.regex.Pattern;
    2220import java.util.stream.IntStream;
    23 import java.util.stream.Stream;
    2421
    2522import org.openstreetmap.josm.actions.mapmode.MapMode;
    2623import org.openstreetmap.josm.data.osm.BBox;
     
    9390    /**
    9491     * The buttons currently displayed in map view.
    9592     */
    96     private final Map<String, AutoFilterButton> buttons = new TreeMap<>();
     93    private final Map<Integer, AutoFilterButton> buttons = new TreeMap<>();
    9794
    9895    /**
    9996     * The list of registered auto filter rules.
    private synchronized void updateButtons() {  
    145142        if (enabledRule != null && map != null
    146143                && enabledRule.getMinZoomLevel() <= Selector.GeneralSelector.scale2level(map.mapView.getDist100Pixel())) {
    147144            // Retrieve the values from current rule visible on screen
    148             NavigableSet<String> values = getNumericValues(enabledRule.getKey(), enabledRule.getValueComparator());
     145            NavigableSet<Integer> values = getNumericValues(enabledRule.getKey());
    149146            // Make sure current auto filter button remains visible even if no data is found, to allow user to disable it
    150147            if (currentAutoFilter != null) {
    151148                values.add(currentAutoFilter.getFilter().value);
    private synchronized void updateButtons() {  
    157154        }
    158155    }
    159156
    160     static class CompiledFilter extends Filter implements MatchSupplier {
     157    class CompiledFilter extends Filter implements MatchSupplier {
    161158        final String key;
    162         final String value;
     159        final int value;
    163160
    164         CompiledFilter(String key, String value) {
     161        CompiledFilter(String key, int value) {
    165162            this.key = key;
    166163            this.value = value;
    167164            this.enable = true;
    private synchronized void updateButtons() {  
    174171            return new SearchCompiler.Match() {
    175172                @Override
    176173                public boolean match(OsmPrimitive osm) {
    177                     return getTagValuesForPrimitive(key, osm).anyMatch(value::equals);
     174                    return getTagValuesForPrimitive(key, osm).anyMatch(v -> v == value);
    178175                }
    179176            };
    180177        }
    181178    }
    182179
    183     private synchronized void addNewButtons(NavigableSet<String> values) {
     180    private synchronized void addNewButtons(NavigableSet<Integer> values) {
    184181        if (values.isEmpty()) {
    185182            return;
    186183        }
    187184        int i = 0;
    188185        int maxWidth = 16;
    189186        final AutoFilterButton keyButton = AutoFilterButton.forOsmKey(enabledRule.getKey());
    190         addButton(keyButton, Integer.toString(Integer.MIN_VALUE), i++);
    191         for (final String value : values.descendingSet()) {
     187        addButton(keyButton, Integer.MIN_VALUE, i++);
     188        for (final Integer value : values.descendingSet()) {
    192189            CompiledFilter filter = new CompiledFilter(enabledRule.getKey(), value);
    193190            String label = enabledRule.getValueFormatter().apply(value);
    194191            AutoFilter autoFilter = new AutoFilter(label, filter.text, filter);
    private synchronized void addNewButtons(NavigableSet<String> values) {  
    205202        MainApplication.getMap().mapView.validate();
    206203    }
    207204
    208     private void addButton(AutoFilterButton button, String value, int i) {
     205    private void addButton(AutoFilterButton button, int value, int i) {
    209206        MapView mapView = MainApplication.getMap().mapView;
    210207        buttons.put(value, button);
    211208        mapView.add(button).setLocation(3, 60 + 22*i);
    212209    }
    213210
    214211    private void removeAllButtons() {
    215         for (Iterator<String> it = buttons.keySet().iterator(); it.hasNext();) {
    216             MainApplication.getMap().mapView.remove(buttons.get(it.next()));
    217             it.remove();
    218         }
     212        buttons.values().forEach(MainApplication.getMap().mapView::remove);
     213        buttons.clear();
    219214    }
    220215
    221     private static NavigableSet<String> getNumericValues(String key, Comparator<String> comparator) {
    222         NavigableSet<String> values = new TreeSet<>(comparator);
    223         for (String s : getTagValues(key)) {
    224             try {
    225                 Integer.parseInt(s);
    226                 values.add(s);
    227             } catch (NumberFormatException e) {
    228                 Logging.trace(e);
    229             }
    230         }
    231         return values;
    232     }
    233 
    234     private static Set<String> getTagValues(String key) {
     216    private NavigableSet<Integer> getNumericValues(String key) {
    235217        DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
    236         Set<String> values = new TreeSet<>();
    237         if (ds != null) {
    238             BBox bbox = MainApplication.getMap().mapView.getState().getViewArea().getLatLonBoundsBox().toBBox();
    239             Consumer<OsmPrimitive> consumer = o -> getTagValuesForPrimitive(key, o).forEach(values::add);
    240             ds.searchNodes(bbox).forEach(consumer);
    241             ds.searchWays(bbox).forEach(consumer);
    242             ds.searchRelations(bbox).forEach(consumer);
     218        if (ds == null) {
     219            return Collections.emptyNavigableSet();
    243220        }
     221        BBox bbox = MainApplication.getMap().mapView.getState().getViewArea().getLatLonBoundsBox().toBBox();
     222        NavigableSet<Integer> values = new TreeSet<>();
     223        Consumer<OsmPrimitive> consumer = o -> getTagValuesForPrimitive(key, o).forEach(values::add);
     224        ds.searchNodes(bbox).forEach(consumer);
     225        ds.searchWays(bbox).forEach(consumer);
     226        ds.searchRelations(bbox).forEach(consumer);
    244227        return values;
    245228    }
    246229
    247     static Stream<String> getTagValuesForPrimitive(String key, OsmPrimitive osm) {
     230    private IntStream getTagValuesForPrimitive(String key, OsmPrimitive osm) {
    248231        String value = osm.get(key);
    249232        if (value != null) {
    250233            Pattern p = Pattern.compile("(-?[0-9]+)-(-?[0-9]+)");
    251             return OsmUtils.splitMultipleValues(value).flatMap(v -> {
     234            return OsmUtils.splitMultipleValues(value).flatMapToInt(v -> {
    252235                Matcher m = p.matcher(v);
    253236                if (m.matches()) {
    254237                    int a = Integer.parseInt(m.group(1));
    255238                    int b = Integer.parseInt(m.group(2));
    256                     return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b))
    257                             .mapToObj(Integer::toString);
     239                    return IntStream.rangeClosed(Math.min(a, b), Math.max(a, b));
    258240                } else {
    259                     return Stream.of(v);
     241                    try {
     242                        return IntStream.of(enabledRule.getValueExtractor().applyAsInt(v));
     243                    } catch (NumberFormatException e) {
     244                        Logging.trace(e);
     245                        return IntStream.empty();
     246                    }
    260247                }
    261248            });
    262         } else if (PROP_AUTO_FILTER_DEFAULTS.get() && "layer".equals(key)) {
    263             // assume sensible defaults, see #17496
    264             if (osm.hasTag("bridge") || osm.hasTag("power", "line") || osm.hasTag("location", "overhead")) {
    265                 return Stream.of("1");
    266             } else if (osm.isKeyTrue("tunnel") || osm.hasTag("tunnel", "culvert") || osm.hasTag("location", "underground")) {
    267                 return Stream.of("-1");
    268             } else if (osm.hasTag("tunnel", "building_passage") || osm.hasKey("highway", "railway", "waterway")) {
    269                 return Stream.of("0");
    270             }
    271249        }
    272         return Stream.empty();
     250        return enabledRule.getExtractDefaultValues().apply(osm);
    273251    }
    274252
    275253    @Override
  • src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java

    diff --git a/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java b/src/org/openstreetmap/josm/gui/autofilter/AutoFilterRule.java
    index 858e772e4..c45d3b571 100644
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.autofilter;
    33
    4 import java.util.Comparator;
    54import java.util.Objects;
    65import java.util.function.Function;
    7 import java.util.function.UnaryOperator;
     6import java.util.function.IntFunction;
     7import java.util.function.ToIntFunction;
     8import java.util.stream.IntStream;
     9
     10import org.openstreetmap.josm.data.osm.OsmPrimitive;
    811
    912/**
    1013 * An auto filter rule determines how auto filter can be built from visible map data.
     
    1922
    2023    private final int minZoomLevel;
    2124
    22     private UnaryOperator<String> valueFormatter = s -> s;
     25    private ToIntFunction<String> valueExtractor = Integer::parseInt;
     26
     27    private IntFunction<String> valueFormatter = Integer::toString;
    2328
    24     private Comparator<String> valueComparator = Comparator.comparingInt(s -> Integer.parseInt(valueFormatter.apply(s)));
     29    private Function<OsmPrimitive, IntStream> extractDefaultValues = p -> IntStream.empty();
    2530
    2631    /**
    2732     * Constructs a new {@code AutoFilterRule}.
    public int getMinZoomLevel() {  
    5358     * Returns the OSM value formatter that defines the associated button label.
    5459     * @return the OSM value formatter that defines the associated button label (identity by default)
    5560     */
    56     public Function<String, String> getValueFormatter() {
     61    public IntFunction<String> getValueFormatter() {
    5762        return valueFormatter;
    5863    }
    5964
    public int getMinZoomLevel() {  
    6368     * @return {@code this}
    6469     * @throws NullPointerException if {@code valueFormatter} is null
    6570     */
    66     public AutoFilterRule setValueFormatter(UnaryOperator<String> valueFormatter) {
     71    public AutoFilterRule setValueFormatter(IntFunction<String> valueFormatter) {
    6772        this.valueFormatter = Objects.requireNonNull(valueFormatter);
    6873        return this;
    6974    }
    7075
    71     /**
    72      * Returns the OSM value comparator used to order the buttons.
    73      * @return the OSM value comparator
    74      */
    75     public Comparator<String> getValueComparator() {
    76         return valueComparator;
     76    public ToIntFunction<String> getValueExtractor() {
     77        return valueExtractor;
    7778    }
    7879
    79     /**
    80      * Sets the OSM value comparator used to order the buttons.
    81      * @param valueComparator the OSM value comparator
    82      * @return {@code this}
    83      * @throws NullPointerException if {@code valueComparator} is null
    84      */
    85     public AutoFilterRule setValueComparator(Comparator<String> valueComparator) {
    86         this.valueComparator = valueComparator;
     80    public AutoFilterRule setValueExtractor(ToIntFunction<String> valueExtractor) {
     81        this.valueExtractor = valueExtractor;
     82        return this;
     83    }
     84
     85    public Function<OsmPrimitive, IntStream> getExtractDefaultValues() {
     86        return extractDefaultValues;
     87    }
     88
     89    public AutoFilterRule setExtractDefaultValues(Function<OsmPrimitive, IntStream> extractDefaultValues) {
     90        this.extractDefaultValues = extractDefaultValues;
    8791        return this;
    8892    }
    8993
    public AutoFilterRule setValueComparator(Comparator<String> valueComparator) {  
    9296     * @return the default list of auto filter rules
    9397     */
    9498    public static AutoFilterRule[] defaultRules() {
    95         return new AutoFilterRule[] {
     99        return new AutoFilterRule[]{
    96100            new AutoFilterRule("level", 17),
    97             new AutoFilterRule("layer", 16),
     101            new AutoFilterRule("layer", 16)
     102                    .setExtractDefaultValues(AutoFilterRule::defaultLayer),
    98103            new AutoFilterRule("maxspeed", 16)
    99                 .setValueFormatter(s -> s.replace(" mph", "")),
     104                    .setValueExtractor(s -> Integer.parseInt(s.replace(" mph", ""))),
    100105            new AutoFilterRule("voltage", 5)
    101                 .setValueFormatter(s -> s.replaceAll("000$", "k") + 'V')
    102                 .setValueComparator(Comparator.comparingInt(Integer::parseInt))
     106                    .setValueFormatter(s -> s % 1000 == 0 ? (s / 1000) + "kV" : s + "V"),
     107            new AutoFilterRule("building:levels", 17),
     108            new AutoFilterRule("gauge", 5),
     109            new AutoFilterRule("frequency", 5),
     110            new AutoFilterRule("incline", 13)
     111                    .setValueExtractor(s -> Integer.parseInt(s.replaceAll("%$", "")))
     112                    .setValueFormatter(v -> v + "\u2009%"),
     113            new AutoFilterRule("lanes", 13),
     114            new AutoFilterRule("admin_level", 11)
    103115        };
    104116    }
    105117
     118    private static IntStream defaultLayer(OsmPrimitive osm) {
     119        // assume sensible defaults, see #17496
     120        if (osm.hasTag("bridge") || osm.hasTag("power", "line") || osm.hasTag("location", "overhead")) {
     121            return IntStream.of(1);
     122        } else if (osm.isKeyTrue("tunnel") || osm.hasTag("tunnel", "culvert") || osm.hasTag("location", "underground")) {
     123            return IntStream.of(-1);
     124        } else if (osm.hasTag("tunnel", "building_passage") || osm.hasKey("highway", "railway", "waterway")) {
     125            return IntStream.of(0);
     126        } else {
     127            return IntStream.empty();
     128        }
     129    }
     130
    106131    @Override
    107132    public String toString() {
    108133        return key + '[' + minZoomLevel + ']';
  • test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java b/test/unit/org/openstreetmap/josm/gui/autofilter/AutoFilterManagerTest.java
    index dc9fbf9aa..2b26dfe0b 100644
    a b  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.gui.autofilter;
    33
    4 import static org.junit.Assert.assertEquals;
    5 import static org.junit.Assert.assertNull;
    6 
    7 import java.util.Arrays;
    8 import java.util.TreeSet;
    9 import java.util.stream.Collectors;
    10 import java.util.stream.Stream;
    11 
    12 import org.junit.Rule;
    13 import org.junit.Test;
    14 import org.openstreetmap.josm.data.osm.OsmUtils;
    15 import org.openstreetmap.josm.testutils.JOSMTestRules;
    16 
    17 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    18 
    194/**
    205 * Unit tests of {@link AutoFilterManager} class.
    216 */
    227public class AutoFilterManagerTest {
    23 
    24     /**
    25      * Setup tests
    26      */
    27     @Rule
    28     @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
    29     public JOSMTestRules test = new JOSMTestRules();
    30 
    31     /**
    32      * Unit test of {@link AutoFilterManager#getTagValuesForPrimitive}.
    33      */
    34     @Test
    35     public void testTagValuesForPrimitive() {
    36         final TreeSet<String> values = Stream.of(
    37                 OsmUtils.createPrimitive("way level=-4--5"),
    38                 OsmUtils.createPrimitive("way level=-2"),
    39                 OsmUtils.createPrimitive("node level=0"),
    40                 OsmUtils.createPrimitive("way level=1"),
    41                 OsmUtils.createPrimitive("way level=2;3"),
    42                 OsmUtils.createPrimitive("way level=6-9"),
    43                 OsmUtils.createPrimitive("way level=10;12-13"))
    44                 .flatMap(o -> AutoFilterManager.getTagValuesForPrimitive("level", o))
    45                 .collect(Collectors.toCollection(TreeSet::new));
    46         assertEquals(new TreeSet<>(Arrays.asList("-5", "-4", "-2", "0", "1", "2", "3", "6", "7", "8", "9", "10", "12", "13")), values);
    47 
    48     }
    49 
    50     /**
    51      * Unit test of {@link AutoFilterManager#getTagValuesForPrimitive} provides sensible defaults, see #17496.
    52      */
    53     @Test
    54     public void testTagValuesForPrimitivesDefaults() {
    55         assertNull(getLayer("way foo=bar"));
    56         assertEquals("1", getLayer("way bridge=yes"));
    57         assertEquals("1", getLayer("way power=line"));
    58         assertEquals("-1", getLayer("way tunnel=yes"));
    59         assertEquals("0", getLayer("way tunnel=building_passage"));
    60         assertEquals("0", getLayer("way highway=residential"));
    61         assertEquals("0", getLayer("way railway=rail"));
    62         assertEquals("0", getLayer("way waterway=canal"));
    63     }
    64 
    65     private String getLayer(final String assertion) {
    66         return AutoFilterManager.getTagValuesForPrimitive("layer", OsmUtils.createPrimitive(assertion))
    67                 .findFirst()
    68                 .orElse(null);
    69     }
    708}