Changeset 15978 in josm for trunk/src/org/openstreetmap
- Timestamp:
- 2020-03-01T21:33:56+01:00 (5 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/data/validation/tests
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/ConditionalKeys.java
r15743 r15978 9 9 import java.util.HashSet; 10 10 import java.util.List; 11 import java.util.Locale; 11 12 import java.util.Set; 12 13 import java.util.regex.Matcher; … … 17 18 import org.openstreetmap.josm.data.validation.Test; 18 19 import org.openstreetmap.josm.data.validation.TestError; 19 import org.openstreetmap.josm.tools.LanguageInfo;20 20 import org.openstreetmap.josm.tools.Logging; 21 21 import org.openstreetmap.josm.tools.SubclassFilteredCollection; … … 205 205 if (condition.matches(".*[0-9]:[0-9]{2}.*")) { 206 206 final List<OpeningHourTest.OpeningHoursTestError> errors = openingHourTest.checkOpeningHourSyntax( 207 "", condition, OpeningHourTest.CheckMode.TIME_RANGE, true, LanguageInfo.getJOSMLocaleCode());207 "", condition, true, Locale.getDefault()); 208 208 if (!errors.isEmpty()) { 209 209 return errors.get(0).getMessage(); -
trunk/src/org/openstreetmap/josm/data/validation/tests/OpeningHourTest.java
r15815 r15978 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.io.Reader; 7 import java.util.ArrayList; 8 import java.util.Arrays; 6 import java.io.StringReader; 9 7 import java.util.Collections; 10 8 import java.util.List; 9 import java.util.Locale; 10 import java.util.Objects; 11 11 12 import javax.script.Invocable; 13 import javax.script.ScriptEngine; 14 import javax.script.ScriptException; 15 import javax.swing.JOptionPane; 16 12 import ch.poole.openinghoursparser.OpeningHoursParser; 13 import ch.poole.openinghoursparser.ParseException; 14 import ch.poole.openinghoursparser.Rule; 15 import ch.poole.openinghoursparser.Util; 17 16 import org.openstreetmap.josm.command.ChangePropertyCommand; 18 17 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 20 19 import org.openstreetmap.josm.data.validation.Test.TagTest; 21 20 import org.openstreetmap.josm.data.validation.TestError; 22 import org.openstreetmap.josm.gui.Notification;23 import org.openstreetmap.josm.gui.util.GuiHelper;24 import org.openstreetmap.josm.io.CachedFile;25 import org.openstreetmap.josm.tools.LanguageInfo;26 import org.openstreetmap.josm.tools.Logging;27 import org.openstreetmap.josm.tools.Utils;28 21 29 22 /** 30 23 * Tests the correct usage of the opening hour syntax of the tags 31 24 * {@code opening_hours}, {@code collection_times}, {@code service_times} according to 32 * <a href="https://github.com/ ypid/opening_hours.js">opening_hours.js</a>.25 * <a href="https://github.com/simonpoole/OpeningHoursParser">OpeningHoursParser</a>. 33 26 * 34 * @since 6370 27 * @since 6370 (using opening_hours.js), 15978 (using OpeningHoursParser) 35 28 */ 36 29 public class OpeningHourTest extends TagTest { 37 38 /**39 * Javascript engine40 */41 public static final ScriptEngine ENGINE = Utils.getJavaScriptEngine();42 30 43 31 /** … … 47 35 super(tr("Opening hours syntax"), 48 36 tr("This test checks the correct usage of the opening hours syntax.")); 49 }50 51 @Override52 public void initialize() throws Exception {53 super.initialize();54 if (ENGINE != null) {55 try (CachedFile cf = new CachedFile("resource://data/validator/opening_hours.js");56 Reader reader = cf.getContentReader()) {57 ENGINE.eval("var console={};console.debug=print;console.log=print;console.warn=print;console.error=print;");58 ENGINE.eval(reader);59 // fake country/state to not get errors on holidays60 ENGINE.eval("var nominatimJSON = {address: {country_code: 'xa'}};");61 ENGINE.eval(62 "var oh = function (value, tag_key, mode, locale) {" +63 " try {" +64 " var conf = {tag_key: tag_key, locale: locale, additional_rule_separator: false};" +65 " if (mode > -1) {" +66 " conf.mode = mode;" +67 " }" +68 " var r = new opening_hours(value, nominatimJSON, conf);" +69 " r.getErrors = function() {return [];};" +70 " return r;" +71 " } catch (err) {" +72 " return {" +73 " prettifyValue: function() {return null;}," +74 " getWarnings: function() {return [];}," +75 " getErrors: function() {return [err.toString()]}" +76 " };" +77 " }" +78 "};");79 }80 } else {81 Logging.warn("Unable to initialize OpeningHourTest because no JavaScript engine has been found");82 }83 37 } 84 38 … … 104 58 105 59 /** 106 * Parses the opening hour syntax of the {@code value} given according to107 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns an object on which108 * methods can be called to extract information.109 * @param value the opening hour value to be checked110 * @param tagKey the OSM key (should be "opening_hours", "collection_times" or "service_times")111 * @param mode whether to validate {@code value} as a time range, or points in time, or both. Can be null112 * @param locale the locale code used for localizing messages113 * @return The value returned by the underlying method. Usually a {@code jdk.nashorn.api.scripting.ScriptObjectMirror}114 * @throws ScriptException if an error occurs during invocation of the underlying method115 * @throws NoSuchMethodException if underlying method with given name or matching argument types cannot be found116 * @since 13147117 */118 public Object parse(String value, String tagKey, CheckMode mode, String locale) throws ScriptException, NoSuchMethodException {119 return ((Invocable) ENGINE).invokeFunction("oh", value, tagKey, mode != null ? mode.code : -1, locale);120 }121 122 @SuppressWarnings("unchecked")123 protected List<Object> getList(Object obj) throws ScriptException, NoSuchMethodException {124 if (obj == null || "".equals(obj)) {125 return Arrays.asList();126 } else if (obj instanceof String) {127 final Object[] strings = ((String) obj).split("\\\\n");128 return Arrays.asList(strings);129 } else if (obj instanceof List) {130 return (List<Object>) obj;131 } else {132 // recursively call getList() with argument converted to newline-separated string133 return getList(((Invocable) ENGINE).invokeMethod(obj, "join", "\\n"));134 }135 }136 137 /**138 60 * An error concerning invalid syntax for an "opening_hours"-like tag. 139 61 */ … … 144 66 145 67 /** 146 * Constructs a new {@code OpeningHoursTestError} with a known pret iffied value.68 * Constructs a new {@code OpeningHoursTestError} with a known prettified value. 147 69 * @param message The error message 148 70 * @param severity The error severity … … 203 125 204 126 /** 205 * Checks for a correct usage of the opening hour syntax of the {@code value} given according to 206 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns a list containing 207 * validation errors or an empty list. Null values result in an empty list. 127 * Checks for a correct usage of the opening hour syntax of the {@code value} given, 128 * and returns a list containing validation errors or an empty list. Null values result in an empty list. 208 129 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). Used in error message 209 130 * @param value the opening hour value to be checked. … … 211 132 */ 212 133 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value) { 213 return checkOpeningHourSyntax(key, value, null, false, LanguageInfo.getJOSMLocaleCode());134 return checkOpeningHourSyntax(key, value, false, Locale.getDefault()); 214 135 } 215 136 216 137 /** 217 * Checks for a correct usage of the opening hour syntax of the {@code value} given according to 218 * <a href="https://github.com/ypid/opening_hours.js">opening_hours.js</a> and returns a list containing 219 * validation errors or an empty list. Null values result in an empty list. 138 * Checks for a correct usage of the opening hour syntax of the {@code value} given, 139 * and returns a list containing validation errors or an empty list. Null values result in an empty list. 220 140 * @param key the OSM key (should be "opening_hours", "collection_times" or "service_times"). 221 141 * @param value the opening hour value to be checked. 222 * @param mode whether to validate {@code value} as a time range, or points in time, or both. Can be null223 142 * @param ignoreOtherSeverity whether to ignore errors with {@link Severity#OTHER}. 224 143 * @param locale the locale code used for localizing messages 225 144 * @return a list of {@link TestError} or an empty list 226 145 */ 227 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value, CheckMode mode, 228 boolean ignoreOtherSeverity, String locale) { 229 if (ENGINE == null || value == null || value.isEmpty()) { 146 public List<OpeningHoursTestError> checkOpeningHourSyntax(final String key, final String value, boolean ignoreOtherSeverity, Locale locale) { 147 if (value == null || value.isEmpty()) { 230 148 return Collections.emptyList(); 231 149 } 232 final List<OpeningHoursTestError> errors = new ArrayList<>(); 150 151 ch.poole.openinghoursparser.I18n.setLocale(locale); 152 String prettifiedValue = null; 233 153 try { 234 final Object r = parse(value, key, mode, locale); 235 String prettifiedValue = null; 236 try { 237 prettifiedValue = getOpeningHoursPrettifiedValues(r); 238 } catch (ScriptException | NoSuchMethodException e) { 239 Logging.warn(e); 154 final List<Rule> rules = new OpeningHoursParser(new StringReader(value)).rules(false); 155 prettifiedValue = Util.rulesToOpeningHoursString(rules); 156 if (!Objects.equals(value, prettifiedValue)) { 157 // parse again in strict mode for detailed message 158 new OpeningHoursParser(new StringReader(value)).rules(true); 240 159 } 241 for (final Object i : getOpeningHoursErrors(r)) { 242 errors.add(new OpeningHoursTestError(getErrorMessage(key, i), Severity.ERROR, prettifiedValue)); 243 } 244 for (final Object i : getOpeningHoursWarnings(r)) { 245 errors.add(new OpeningHoursTestError(getErrorMessage(key, i), Severity.WARNING, prettifiedValue)); 246 } 247 if (!ignoreOtherSeverity && errors.isEmpty() && prettifiedValue != null && !value.equals(prettifiedValue)) { 248 errors.add(new OpeningHoursTestError(tr("opening_hours value can be prettified"), Severity.OTHER, prettifiedValue)); 249 } 250 } catch (ScriptException | NoSuchMethodException ex) { 251 Logging.error(ex); 252 GuiHelper.runInEDT(() -> new Notification(Utils.getRootCause(ex).getMessage()).setIcon(JOptionPane.ERROR_MESSAGE).show()); 160 } catch (ParseException e) { 161 return Collections.singletonList(new OpeningHoursTestError(e.getMessage(), Severity.WARNING, prettifiedValue)); 253 162 } 254 return errors;255 }256 163 257 /** 258 * Returns the prettified value returned by the opening hours parser. 259 * @param r result of {@link #parse} 260 * @return the prettified value returned by the opening hours parser 261 * @throws NoSuchMethodException if method "prettifyValue" or matching argument types cannot be found 262 * @throws ScriptException if an error occurs during invocation of the JavaScript method 263 * @since 13296 264 */ 265 public final String getOpeningHoursPrettifiedValues(Object r) throws NoSuchMethodException, ScriptException { 266 return (String) ((Invocable) ENGINE).invokeMethod(r, "prettifyValue"); 267 } 268 269 /** 270 * Returns the list of errors returned by the opening hours parser. 271 * @param r result of {@link #parse} 272 * @return the list of errors returned by the opening hours parser 273 * @throws NoSuchMethodException if method "getErrors" or matching argument types cannot be found 274 * @throws ScriptException if an error occurs during invocation of the JavaScript method 275 * @since 13296 276 */ 277 public final List<Object> getOpeningHoursErrors(Object r) throws NoSuchMethodException, ScriptException { 278 return getList(((Invocable) ENGINE).invokeMethod(r, "getErrors")); 279 } 280 281 /** 282 * Returns the list of warnings returned by the opening hours parser. 283 * @param r result of {@link #parse} 284 * @return the list of warnings returned by the opening hours parser 285 * @throws NoSuchMethodException if method "getWarnings" or matching argument types cannot be found 286 * @throws ScriptException if an error occurs during invocation of the JavaScript method 287 * @since 13296 288 */ 289 public final List<Object> getOpeningHoursWarnings(Object r) throws NoSuchMethodException, ScriptException { 290 return getList(((Invocable) ENGINE).invokeMethod(r, "getWarnings")); 291 } 292 293 /** 294 * Translates and shortens the error/warning message. 295 * @param o error/warning message returned by {@link #getOpeningHoursErrors} or {@link #getOpeningHoursWarnings} 296 * @return translated/shortened error/warning message 297 * @since 13298 298 */ 299 public static String getErrorMessage(Object o) { 300 return o.toString().trim() 301 .replace("Unexpected token:", tr("Unexpected token:")) 302 .replace("Unexpected token (school holiday parser):", tr("Unexpected token (school holiday parser):")) 303 .replace("Unexpected token in number range:", tr("Unexpected token in number range:")) 304 .replace("Unexpected token in week range:", tr("Unexpected token in week range:")) 305 .replace("Unexpected token in weekday range:", tr("Unexpected token in weekday range:")) 306 .replace("Unexpected token in month range:", tr("Unexpected token in month range:")) 307 .replace("Unexpected token in year range:", tr("Unexpected token in year range:")) 308 .replace("This means that the syntax is not valid at that point or it is currently not supported.", tr("Invalid/unsupported syntax.")); 309 } 310 311 /** 312 * Translates and shortens the error/warning message. 313 * @param key OSM key 314 * @param o error/warning message returned by {@link #getOpeningHoursErrors} or {@link #getOpeningHoursWarnings} 315 * @return translated/shortened error/warning message 316 */ 317 static String getErrorMessage(String key, Object o) { 318 return key + " - " + getErrorMessage(o); 164 if (ignoreOtherSeverity || Objects.equals(value, prettifiedValue)) { 165 return Collections.emptyList(); 166 } else { 167 return Collections.singletonList( 168 new OpeningHoursTestError(tr("{0} value can be prettified", key), Severity.OTHER, prettifiedValue)); 169 } 319 170 } 320 171
Note:
See TracChangeset
for help on using the changeset viewer.