source: josm/trunk/src/org/openstreetmap/josm/data/validation/tests/OpeningHourTest.java

Last change on this file was 18453, checked in by taylor.smock, 2 years ago

Fix #22074, #22075: OpeningHoursParser updates (patch by SimonPoole)

Disable time strict mode

This change makes parsing in strict mode slightly more lenient with
respect to time ranges that extend over midnight, with other words this
will no longer complain if non-conflicting long time ranges are used
instead of using the extended time syntax.


Parsing in non-strict mode remains unchanged.

Update OpeningHoursParser to 0.27.0

The update fixes an issue with parsing open ended date ranges followed
by a weekday.

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.StringReader;
7import java.util.Arrays;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.List;
11import java.util.Locale;
12import java.util.Objects;
13import java.util.stream.Collectors;
14
15import javax.swing.JCheckBox;
16import javax.swing.JPanel;
17
18import org.openstreetmap.josm.command.ChangePropertyCommand;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.preferences.BooleanProperty;
21import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
22import org.openstreetmap.josm.data.validation.Severity;
23import org.openstreetmap.josm.data.validation.Test.TagTest;
24import org.openstreetmap.josm.data.validation.TestError;
25import org.openstreetmap.josm.tools.GBC;
26import org.openstreetmap.josm.tools.Utils;
27
28import ch.poole.openinghoursparser.OpeningHoursParseException;
29import ch.poole.openinghoursparser.OpeningHoursParser;
30import ch.poole.openinghoursparser.Rule;
31import ch.poole.openinghoursparser.Util;
32
33/**
34 * Tests the correct usage of the opening hour syntax of the tags
35 * {@code opening_hours}, {@code collection_times}, {@code service_times} according to
36 * <a href="https://github.com/simonpoole/OpeningHoursParser">OpeningHoursParser</a>.
37 *
38 * @since 6370 (using opening_hours.js), 15978 (using OpeningHoursParser)
39 */
40public class OpeningHourTest extends TagTest {
41
42 private static final Collection<String> KEYS_TO_CHECK = Arrays.asList("opening_hours", "collection_times", "service_times");
43 private static final BooleanProperty PREF_STRICT_MODE =
44 new BooleanProperty(ValidatorPrefHelper.PREFIX + "." + OpeningHourTest.class.getSimpleName() + "." + "strict", false);
45 private final JCheckBox checkboxStrictMode = new JCheckBox(tr("Enable strict mode."));
46
47 /**
48 * Constructs a new {@code OpeningHourTest}.
49 */
50 public OpeningHourTest() {
51 super(tr("Opening hours syntax"),
52 tr("This test checks the correct usage of the opening hours syntax."));
53 }
54
55 /**
56 * Returns the real test error given to JOSM validator.
57 * @param severity The error severity
58 * @param message The error message
59 * @param key The incriminated key, used for display.
60 * @param value The incriminated value, used for comparison with prettified value.
61 * @param prettifiedValue The prettified value
62 * @param p The incriminated OSM primitive.
63 * @return The real test error given to JOSM validator. Can be fixable or not if a prettified values has been determined.
64 */
65 private TestError createTestError(Severity severity, String message, String key, String value, String prettifiedValue, OsmPrimitive p) {
66 final TestError.Builder error = TestError.builder(this, severity, 2901)
67 .message(tr("Opening hours syntax"), message) // todo obtain English message for ignore functionality
68 .primitives(p != null ? new OsmPrimitive[] {p} : new OsmPrimitive[] {});
69 if (p == null || prettifiedValue == null || prettifiedValue.equals(value)) {
70 return error.build();
71 } else {
72 return error.fix(() -> new ChangePropertyCommand(p, key, prettifiedValue)).build();
73 }
74 }
75
76 /**
77 * Checks for a correct usage of the opening hour syntax of the {@code value} given,
78 * and returns a list containing validation errors or an empty list. Null values result in an empty list.
79 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). Used in error message
80 * @param value the opening hour value to be checked.
81 * @return a list of {@link TestError} or an empty list
82 */
83 public List<TestError> checkOpeningHourSyntax(final String key, final String value) {
84 return checkOpeningHourSyntax(key, value, null, Locale.getDefault());
85 }
86
87 /**
88 * Checks for a correct usage of the opening hour syntax of the {@code value} given,
89 * and returns a list containing validation errors or an empty list. Null values result in an empty list.
90 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times").
91 * @param value the opening hour value to be checked.
92 * @param p the primitive to check/fix.
93 * @param locale the locale code used for localizing messages
94 * @return a list of {@link TestError} or an empty list
95 */
96 List<TestError> checkOpeningHourSyntax(final String key, final String value, OsmPrimitive p, Locale locale) {
97 if (Utils.isEmpty(value)) {
98 return Collections.emptyList();
99 }
100
101 ch.poole.openinghoursparser.I18n.setLocale(locale);
102 String prettifiedValue = null;
103 try {
104 final boolean strict = PREF_STRICT_MODE.get();
105 final List<Rule> rules = new OpeningHoursParser(new StringReader(value)).rules(strict, false);
106 prettifiedValue = Util.rulesToOpeningHoursString(rules);
107 if (!Objects.equals(value, prettifiedValue) && !strict) {
108 // parse again in strict mode for detailed message
109 new OpeningHoursParser(new StringReader(value)).rules(true, false);
110 }
111 } catch (OpeningHoursParseException e) {
112 String message = e.getExceptions().stream()
113 .map(OpeningHoursParseException::getMessage)
114 .distinct()
115 .collect(Collectors.joining("; "));
116 return Collections.singletonList(createTestError(Severity.WARNING, message, key, value, prettifiedValue, p));
117 }
118
119 if (!includeOtherSeverityChecks() || Objects.equals(value, prettifiedValue)) {
120 return Collections.emptyList();
121 } else {
122 final String message = tr("{0} value can be prettified", key);
123 return Collections.singletonList(createTestError(Severity.OTHER, message, key, value, prettifiedValue, p));
124 }
125 }
126
127 @Override
128 public void check(final OsmPrimitive p) {
129 addErrorsForPrimitive(p, this.errors);
130 }
131
132 /**
133 * Checks the tags of the given primitive and adds validation errors to the given list.
134 * @param p The primitive to test
135 * @param errors The list to add validation errors to
136 * @since 17643
137 */
138 public void addErrorsForPrimitive(OsmPrimitive p, Collection<TestError> errors) {
139 if (p.isTagged()) {
140 for (String key : KEYS_TO_CHECK) {
141 errors.addAll(checkOpeningHourSyntax(key, p.get(key), p, Locale.getDefault()));
142 }
143 // COVID-19, a few additional values are permitted, see #19048, see https://wiki.openstreetmap.org/wiki/Key:opening_hours:covid19
144 final String keyCovid19 = "opening_hours:covid19";
145 if (p.hasTag(keyCovid19) && !p.hasTag(keyCovid19, "same", "restricted", "open", "off")) {
146 errors.addAll(checkOpeningHourSyntax(keyCovid19, p.get(keyCovid19), p, Locale.getDefault()));
147 }
148 }
149 }
150
151 @Override
152 public void addGui(JPanel testPanel) {
153 super.addGui(testPanel);
154 checkboxStrictMode.setSelected(PREF_STRICT_MODE.get());
155 testPanel.add(checkboxStrictMode, GBC.eol().insets(20, 0, 0, 0));
156 }
157
158 @Override
159 public boolean ok() {
160 super.ok();
161 PREF_STRICT_MODE.put(checkboxStrictMode.isSelected());
162 return false;
163 }
164}
Note: See TracBrowser for help on using the repository browser.