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

Last change on this file since 6623 was 6623, checked in by Don-vip, 10 years ago

fix Sonar issues

File size: 10.1 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation.tests;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.InputStreamReader;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collections;
10import java.util.List;
11
12import javax.script.Invocable;
13import javax.script.ScriptEngine;
14import javax.script.ScriptEngineManager;
15import javax.script.ScriptException;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.command.ChangePropertyCommand;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.validation.FixableTestError;
21import org.openstreetmap.josm.data.validation.Severity;
22import org.openstreetmap.josm.data.validation.Test;
23import org.openstreetmap.josm.data.validation.TestError;
24import org.openstreetmap.josm.io.MirroredInputStream;
25import org.openstreetmap.josm.tools.Utils;
26
27/**
28 * Tests the correct usage of the opening hour syntax of the tags
29 * {@code opening_hours}, {@code collection_times}, {@code service_times} according to
30 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a>.
31 *
32 * @since 6370
33 */
34public class OpeningHourTest extends Test.TagTest {
35
36 /**
37 * Javascript engine
38 */
39 public static final ScriptEngine ENGINE = new ScriptEngineManager().getEngineByName("JavaScript");
40
41 /**
42 * Constructs a new {@code OpeningHourTest}.
43 */
44 public OpeningHourTest() {
45 super(tr("Opening hours syntax"),
46 tr("This test checks the correct usage of the opening hours syntax."));
47 }
48
49 @Override
50 public void initialize() throws Exception {
51 super.initialize();
52 if (ENGINE != null) {
53 ENGINE.eval(new InputStreamReader(new MirroredInputStream("resource://data/validator/opening_hours.js"), Utils.UTF_8));
54 // fake country/state to not get errors on holidays
55 ENGINE.eval("var nominatimJSON = {address: {state: 'Bayern', country_code: 'de'}};");
56 ENGINE.eval("" +
57 "var oh = function (value, mode) {" +
58 " try {" +
59 " var r= new opening_hours(value, nominatimJSON, mode);" +
60 " r.getErrors = function() {return [];};" +
61 " return r;" +
62 " } catch(err) {" +
63 " return {" +
64 " getWarnings: function() {return [];}," +
65 " getErrors: function() {return [err.toString()]}" +
66 " };" +
67 " }" +
68 "};");
69 } else {
70 Main.warn("Unable to initialize OpeningHourTest because no JavaScript engine has been found");
71 }
72 }
73
74 static enum CheckMode {
75 TIME_RANGE(0), POINTS_IN_TIME(1), BOTH(2);
76 final int code;
77
78 CheckMode(int code) {
79 this.code = code;
80 }
81 }
82
83 protected Object parse(String value, CheckMode mode) throws ScriptException, NoSuchMethodException {
84 return ((Invocable) ENGINE).invokeFunction("oh", value, mode.code);
85 }
86
87 @SuppressWarnings("unchecked")
88 protected List<Object> getList(Object obj) throws ScriptException, NoSuchMethodException {
89 if (obj == null || "".equals(obj)) {
90 return Arrays.asList();
91 } else if (obj instanceof String) {
92 final Object[] strings = ((String) obj).split("\\\\n");
93 return Arrays.asList(strings);
94 } else if (obj instanceof List) {
95 return (List<Object>) obj;
96 } else {
97 // recursively call getList() with argument converted to newline-separated string
98 return getList(((Invocable) ENGINE).invokeMethod(obj, "join", "\\n"));
99 }
100 }
101
102 /**
103 * An error concerning invalid syntax for an "opening_hours"-like tag.
104 */
105 public class OpeningHoursTestError {
106 final Severity severity;
107 final String message, prettifiedValue;
108
109 /**
110 * Constructs a new {@code OpeningHoursTestError} with a known pretiffied value.
111 * @param message The error message
112 * @param severity The error severity
113 * @param prettifiedValue The prettified value
114 */
115 public OpeningHoursTestError(String message, Severity severity, String prettifiedValue) {
116 this.message = message;
117 this.severity = severity;
118 this.prettifiedValue = prettifiedValue;
119 }
120
121 /**
122 * Returns the real test error given to JOSM validator.
123 * @param p The incriminated OSM primitive.
124 * @param key The incriminated key, used for display.
125 * @return The real test error given to JOSM validator. Can be fixable or not if a prettified values has been determined.
126 */
127 public TestError getTestError(final OsmPrimitive p, final String key) {
128 if (prettifiedValue == null) {
129 return new TestError(OpeningHourTest.this, severity, message, 2901, p);
130 } else {
131 return new FixableTestError(OpeningHourTest.this, severity, message, 2901, p,
132 new ChangePropertyCommand(p, key, prettifiedValue));
133 }
134 }
135
136 /**
137 * Returns the error message.
138 * @return The error message.
139 */
140 public String getMessage() {
141 return message;
142 }
143
144 /**
145 * Returns the prettified value.
146 * @return The prettified value.
147 */
148 public String getPrettifiedValue() {
149 return prettifiedValue;
150 }
151
152 /**
153 * Returns the error severity.
154 * @return The error severity.
155 */
156 public Severity getSeverity() {
157 return severity;
158 }
159
160 @Override
161 public String toString() {
162 return getMessage() + " => " + getPrettifiedValue();
163 }
164 }
165
166 /**
167 * Checks for a correct usage of the opening hour syntax of the {@code value} given according to
168 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns a list containing
169 * validation errors or an empty list. Null values result in an empty list.
170 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). Used in error message
171 * @param value the opening hour value to be checked.
172 * @param mode whether to validate {@code value} as a time range, or points in time, or both.
173 * @return a list of {@link TestError} or an empty list
174 */
175 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value, CheckMode mode) {
176 return checkOpeningHourSyntax(key, value, mode, false);
177 }
178
179 /**
180 * Checks for a correct usage of the opening hour syntax of the {@code value} given according to
181 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns a list containing
182 * validation errors or an empty list. Null values result in an empty list.
183 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times").
184 * @param value the opening hour value to be checked.
185 * @param mode whether to validate {@code value} as a time range, or points in time, or both.
186 * @param ignoreOtherSeverity whether to ignore errors with {@link Severity#OTHER}.
187 * @return a list of {@link TestError} or an empty list
188 */
189 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value, CheckMode mode, boolean ignoreOtherSeverity) {
190 if (ENGINE == null || value == null || value.trim().isEmpty()) {
191 return Collections.emptyList();
192 }
193 final List<OpeningHoursTestError> errors = new ArrayList<OpeningHoursTestError>();
194 try {
195 final Object r = parse(value, mode);
196 String prettifiedValue = null;
197 try {
198 prettifiedValue = (String) ((Invocable) ENGINE).invokeMethod(r, "prettifyValue");
199 } catch (Exception e) {
200 Main.debug(e.getMessage());
201 }
202 for (final Object i : getList(((Invocable) ENGINE).invokeMethod(r, "getErrors"))) {
203 errors.add(new OpeningHoursTestError(key + " - " + i.toString().trim(), Severity.ERROR, prettifiedValue));
204 }
205 for (final Object i : getList(((Invocable) ENGINE).invokeMethod(r, "getWarnings"))) {
206 errors.add(new OpeningHoursTestError(i.toString().trim(), Severity.WARNING, prettifiedValue));
207 }
208 if (!ignoreOtherSeverity && errors.isEmpty() && prettifiedValue != null && !value.equals(prettifiedValue)) {
209 errors.add(new OpeningHoursTestError(tr("opening_hours value can be prettified"), Severity.OTHER, prettifiedValue));
210 }
211 } catch (ScriptException ex) {
212 Main.error(ex);
213 } catch (NoSuchMethodException ex) {
214 Main.error(ex);
215 }
216 return errors;
217 }
218
219 /**
220 * Checks for a correct usage of the opening hour syntax of the {@code value} given, in time range mode, according to
221 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns a list containing
222 * validation errors or an empty list. Null values result in an empty list.
223 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). Used in error message
224 * @param value the opening hour value to be checked.
225 * @return a list of {@link TestError} or an empty list
226 */
227 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value) {
228 return checkOpeningHourSyntax(key, value, "opening_hours".equals(key) ? CheckMode.TIME_RANGE : CheckMode.BOTH);
229 }
230
231 protected void check(final OsmPrimitive p, final String key, CheckMode mode) {
232 for (OpeningHoursTestError e : checkOpeningHourSyntax(key, p.get(key), mode)) {
233 errors.add(e.getTestError(p, key));
234 }
235 }
236
237 @Override
238 public void check(final OsmPrimitive p) {
239 check(p, "opening_hours", CheckMode.TIME_RANGE);
240 check(p, "collection_times", CheckMode.BOTH);
241 check(p, "service_times", CheckMode.BOTH);
242 }
243}
Note: See TracBrowser for help on using the repository browser.