Ticket #21930: street-parking-keys-with-conditional.diff

File street-parking-keys-with-conditional.diff, 13.3 KB (added by pixunil, 3 years ago)

Patch for validating parking:[SIDE]:[RESTRICTION]:conditional

  • src/org/openstreetmap/josm/data/validation/tests/ConditionalKeys.java

     
    66import java.util.ArrayList;
    77import java.util.Arrays;
    88import java.util.Collection;
     9import java.util.HashMap;
    910import java.util.HashSet;
    1011import java.util.List;
     12import java.util.Map;
    1113import java.util.Set;
    1214import java.util.regex.Matcher;
    1315import java.util.regex.Pattern;
     16import java.util.stream.Collectors;
     17import java.util.stream.Stream;
    1418
    1519import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1620import org.openstreetmap.josm.data.validation.Severity;
     
    2630
    2731    private final OpeningHourTest openingHourTest = new OpeningHourTest();
    2832    private static final Set<String> RESTRICTION_TYPES = new HashSet<>(Arrays.asList("oneway", "toll", "noexit", "maxspeed", "minspeed",
    29             "maxstay", "maxweight", "maxaxleload", "maxheight", "maxwidth", "maxlength", "overtaking", "maxgcweight", "maxgcweightrating",
    30             "fee", "restriction", "interval", "duration", "dog", "maxweightrating"));
     33            "maxweight", "maxaxleload", "maxheight", "maxwidth", "maxlength", "overtaking", "maxgcweight", "maxgcweightrating",
     34            "interval", "duration", "dog", "maxweightrating"));
     35
     36    private static final Set<String> PARKING_RESTRICTION_TYPES = new HashSet<>(Arrays.asList("authentication:disc",
     37            "authentication:ticket", "charge", "fee", "maxstay", "restriction", "restriction:reason"));
     38
     39    private static final Set<String> ALL_RESTRICTION_TYPES = Stream.concat(RESTRICTION_TYPES.stream(),
     40            PARKING_RESTRICTION_TYPES.stream()).collect(Collectors.toSet());
     41
    3142    private static final Set<String> RESTRICTION_VALUES = new HashSet<>(Arrays.asList("yes", "official", "designated", "destination",
    3243            "delivery", "customers", "permissive", "private", "agricultural", "forestry", "no"));
     44
     45    /**
     46     * <a href="http://wiki.openstreetmap.org/wiki/Key:access#Transport_mode_restrictions">transport access mode restriction</a>
     47     */
    3348    private static final Set<String> TRANSPORT_MODES = new HashSet<>(Arrays.asList("access", "foot", "ski", "inline_skates", "ice_skates",
    3449            "horse", "vehicle", "bicycle", "carriage", "trailer", "caravan", "motor_vehicle", "motorcycle", "moped", "mofa",
    3550            "motorcar", "motorhome", "psv", "bus", "taxi", "tourist_bus", "goods", "hgv", "agricultural", "atv", "snowmobile",
     
    3651            "hgv_articulated", "ski:nordic", "ski:alpine", "ski:telemark", "coach", "golf_cart"
    3752            /*,"minibus","share_taxi","hov","car_sharing","emergency","hazmat","disabled"*/));
    3853
     54    private static final Set<String> DIRECTIONS = new HashSet<>(Arrays.asList("forward", "backward", "both_ways"));
     55
     56    private static final Set<String> SIDES = new HashSet<>(Arrays.asList("left", "right", "both"));
     57
     58    private static final Map<String, KeyCharacteristic> KNOWN_CONDITIONAL_KEYS = buildKnownKeys();
     59
    3960    private static final Pattern CONDITIONAL_PATTERN;
    4061    static {
    4162        final String part = Pattern.compile("([^@\\p{Space}][^@]*?)"
     
    5677        openingHourTest.initialize();
    5778    }
    5879
    59     /**
    60      * Check if the key is a key for an access restriction
    61      * @param part The key (or the restriction part of it, e.g. for lanes)
    62      * @return <code>true</code> if it is a restriction
    63      */
    64     public static boolean isRestrictionType(String part) {
    65         return RESTRICTION_TYPES.contains(part);
     80    private enum KeyCharacteristic {
     81        ACCESS,
     82        OTHER
    6683    }
    6784
     85    private static Map<String, KeyCharacteristic> buildKnownKeys() {
     86        //                <restriction-type>[:<transportation mode>][:<direction>]:conditional
     87        Map<String, KeyCharacteristic> knownKeys = new HashMap<>();
     88        for (String restrictionType : ALL_RESTRICTION_TYPES) {
     89            knownKeys.put(String.format("%s:conditional", restrictionType), KeyCharacteristic.OTHER);
     90            for (String mode : TRANSPORT_MODES) {
     91                knownKeys.put(String.format("%s:%s:conditional", restrictionType, mode), KeyCharacteristic.OTHER);
     92                for (String direction : DIRECTIONS) {
     93                    knownKeys.put(String.format("%s:%s:%s:conditional", restrictionType, mode, direction),
     94                        KeyCharacteristic.OTHER);
     95                }
     96            }
     97            for (String direction : DIRECTIONS) {
     98                knownKeys.put(String.format("%s:%s:conditional", restrictionType, direction), KeyCharacteristic.OTHER);
     99            }
     100        }
     101        //                                    <transportation mode> [:<direction>]:conditional
     102        for (String mode : TRANSPORT_MODES) {
     103            knownKeys.put(String.format("%s:conditional", mode), KeyCharacteristic.ACCESS);
     104            for (String direction : DIRECTIONS) {
     105                knownKeys.put(String.format("%s:%s:conditional", mode, direction), KeyCharacteristic.ACCESS);
     106            }
     107        }
     108        // parking:<side>:<restriction-type>[:<transportation mode]               :conditional
     109        for (String side : SIDES) {
     110            for (String restrictionType : PARKING_RESTRICTION_TYPES) {
     111                knownKeys.put(String.format("parking:%s:%s:conditional", side, restrictionType),
     112                    KeyCharacteristic.OTHER);
     113                for (String mode : TRANSPORT_MODES) {
     114                    knownKeys.put(String.format("parking:%s:%s:%s:conditional", side, restrictionType, mode),
     115                        KeyCharacteristic.OTHER);
     116                }
     117            }
     118        }
     119        // parking:<side>                    :<transportation mode>               :conditional
     120        for (String side : SIDES) {
     121            for (String mode : TRANSPORT_MODES) {
     122                knownKeys.put(String.format("parking:%s:%s:conditional", side, mode), KeyCharacteristic.ACCESS);
     123            }
     124        }
     125        return knownKeys;
     126    }
     127
    68128    /**
    69129     * Check if the value is a valid restriction value
    70130     * @param part The value
     
    75135    }
    76136
    77137    /**
    78      * Checks if the key denotes a
    79      * <a href="http://wiki.openstreetmap.org/wiki/Key:access#Transport_mode_restrictions">transport access mode restriction</a>
    80      * @param part The key (or the restriction part of it, e.g. for lanes)
    81      * @return <code>true</code> if it is a restriction
    82      */
    83     public static boolean isTransportationMode(String part) {
    84         return TRANSPORT_MODES.contains(part);
    85     }
    86 
    87     /**
    88      * Check if a key part is a valid direction
    89      * @param part The part of the key
    90      * @return <code>true</code> if it is a direction
    91      */
    92     public static boolean isDirection(String part) {
    93         return "forward".equals(part) || "backward".equals(part);
    94     }
    95 
    96     /**
    97138     * Checks if a given key is a valid access key
    98139     * @param key The conditional key
    99140     * @return <code>true</code> if the key is valid
    100141     */
    101142    public boolean isKeyValid(String key) {
    102         // <restriction-type>[:<transportation mode>][:<direction>]:conditional
    103         // -- or --            <transportation mode> [:<direction>]:conditional
    104         if (!key.endsWith(":conditional")) {
    105             return false;
    106         }
    107         final String[] parts = key.replace(":conditional", "").split(":", -1);
    108         return isKeyValid3Parts(parts) || isKeyValid1Part(parts) || isKeyValid2Parts(parts);
     143        return KNOWN_CONDITIONAL_KEYS.containsKey(key);
    109144    }
    110145
    111     private static boolean isKeyValid3Parts(String... parts) {
    112         return parts.length == 3 && isRestrictionType(parts[0]) && isTransportationMode(parts[1]) && isDirection(parts[2]);
    113     }
    114 
    115     private static boolean isKeyValid2Parts(String... parts) {
    116         return parts.length == 2 && ((isRestrictionType(parts[0]) && (isTransportationMode(parts[1]) || isDirection(parts[1])))
    117                                   || (isTransportationMode(parts[0]) && isDirection(parts[1])));
    118     }
    119 
    120     private static boolean isKeyValid1Part(String... parts) {
    121         return parts.length == 1 && (isRestrictionType(parts[0]) || isTransportationMode(parts[0]));
    122     }
    123 
    124146    /**
    125147     * Check if a value is valid
    126148     * @param key The key the value is for
     
    195217        try {
    196218            for (final ConditionalValue conditional : ConditionalValue.parse(value)) {
    197219                // validate restriction value
    198                 if (isTransportationMode(key.split(":", -1)[0]) && !isRestrictionValue(conditional.restrictionValue)) {
     220                KeyCharacteristic keyCharacteristic = KNOWN_CONDITIONAL_KEYS.get(key);
     221                if (keyCharacteristic == KeyCharacteristic.ACCESS && !isRestrictionValue(conditional.restrictionValue)) {
    199222                    return tr("{0} is not a valid restriction value", conditional.restrictionValue);
    200223                }
    201224                // validate opening hour if the value contains an hour (heuristic)
  • test/unit/org/openstreetmap/josm/data/validation/tests/ConditionalKeysTest.java

     
    77import org.junit.jupiter.api.BeforeEach;
    88import org.junit.jupiter.api.Test;
    99import org.junit.jupiter.api.extension.RegisterExtension;
     10import org.junit.jupiter.params.ParameterizedTest;
     11import org.junit.jupiter.params.provider.ValueSource;
    1012import org.openstreetmap.josm.testutils.JOSMTestRules;
    1113
    1214import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     
    3537    }
    3638
    3739    /**
    38      * Unit test of {@link ConditionalKeys#isKeyValid}.
     40     * Unit tests of {@link ConditionalKeys#isKeyValid}.
    3941     */
    40     @Test
    41     void testKeyValid() {
    42         assertTrue(test.isKeyValid("dog:conditional"));
    43         assertTrue(test.isKeyValid("maxspeed:conditional"));
    44         assertTrue(test.isKeyValid("motor_vehicle:conditional"));
    45         assertTrue(test.isKeyValid("bicycle:conditional"));
    46         assertTrue(test.isKeyValid("overtaking:hgv:conditional"));
    47         assertTrue(test.isKeyValid("maxspeed:hgv:backward:conditional"));
    48         assertTrue(test.isKeyValid("oneway:backward:conditional"));
    49         assertTrue(test.isKeyValid("fee:conditional"));
    50         assertTrue(test.isKeyValid("restriction:conditional"));
    51         assertFalse(test.isKeyValid("maxspeed:hgv:conditional:backward"));
     42    @ValueSource(strings = { "dog:conditional", "maxspeed:conditional", "overtaking:hgv:conditional",
     43            "maxspeed:hgv:backward:conditional", "oneway:backward:conditional", "fee:conditional",
     44            "restriction:conditional" })
     45    @ParameterizedTest
     46    void testKeyIsValid(String key) {
     47        assertTrue(test.isKeyValid(key));
    5248    }
    5349
     50    @ValueSource(strings = { "bicycle:conditional", "motor_vehicle:conditional", "ski:alpine:conditional" })
     51    @ParameterizedTest
     52    void testTransportationModeKeyIsValid(String key) {
     53        assertTrue(test.isKeyValid(key));
     54    }
     55
     56    @ValueSource(strings = { "parking:both:maxstay:conditional", "parking:left:access:conditional",
     57            "parking:right:restriction:conditional", "parking:right:restriction:reason:conditional" })
     58    @ParameterizedTest
     59    void testParkingRestrictionBySideKeyIsValid(String key) {
     60        assertTrue(test.isKeyValid(key));
     61    }
     62
     63    @ValueSource(strings = { "parking:both:maxstay:hgv:conditional",
     64            "parking:right:authentication:ticket:conditional" })
     65    @ParameterizedTest
     66    void testParkingRestrictionBySideWithModeKeyIsValid(String key) {
     67        assertTrue(test.isKeyValid(key));
     68    }
     69
     70    @ValueSource(strings = { ":conditional", // no base key
     71            "maxspeed:hgv:conditional:backward", // direction must be before :conditional
     72            "parking:forward:maxstay:conditional", // parking side must be both, left or right
     73            "parking:both:fee:forward:conditional", // parking can not have a direction
     74            "parking:left:oneway:conditional" // oneway is not a parking restriction
     75    })
     76    @ParameterizedTest
     77    void testKeyIsInvalid(String key) {
     78        assertFalse(test.isKeyValid(key));
     79    }
     80
    5481    /**
    5582     * Unit test of {@link ConditionalKeys#isValueValid}.
    5683     */
     
    6592        assertTrue(test.isValueValid("maxspeed:conditional", "120 @ (06:00-20:00); 100 @ (22:00-06:00)"));
    6693        assertTrue(test.isValueValid("motor_vehicle:conditional", "delivery @ (Mo-Fr 06:00-11:00,17:00-19:00;Sa 03:30-19:00)"));
    6794        assertTrue(test.isValueValid("motor_vehicle:conditional", "no @ (10:00-18:00 AND length>5)"));
     95        assertTrue(test.isValueValid("parking:right:motor_vehicle:conditional", "no @ (10:00-18:00 AND length>5)"));
    6896        assertFalse(test.isValueValid("motor_vehicle:conditional", "foo @ (10:00-18:00 AND length>5)"));
     97        assertFalse(test.isValueValid("parking:right:motor_vehicle:conditional", "foo @ (10:00-18:00 AND length>5)"));
    6998        assertFalse(test.isValueValid("motor_vehicle:conditional", "no @ (10:00until18:00 AND length>5)"));
    7099        assertTrue(test.isValueValid("maxspeed:hgv:conditional", "60 @ (weight>7.5)"));
    71100        assertTrue(test.isValueValid("restriction:conditional", "no_left_turn @ (Mo-Fr 16:00-18:00)"));