source: josm/trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java@ 16296

Last change on this file since 16296 was 16296, checked in by GerdP, 4 years ago

fix #19053:Validator does not find overlapping buildings anymore

  • improve handling of ignore action for messages produced by MapCSSTagCheckerTest. This requires a change in the entries stored in preferences.xml.
  • partly revert r16247 to re-enable ignore button for groups
  • add code to recognize and remove ignore list entries produced with older version of JOSM. Users have to re-add those items by ignoring the errors again.
  • Property svn:eol-style set to native
File size: 26.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.GraphicsEnvironment;
7import java.io.File;
8import java.io.FileNotFoundException;
9import java.io.IOException;
10import java.nio.charset.StandardCharsets;
11import java.nio.file.Files;
12import java.nio.file.Path;
13import java.nio.file.Paths;
14import java.util.ArrayList;
15import java.util.Arrays;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.EnumMap;
19import java.util.Enumeration;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import java.util.Map.Entry;
26import java.util.Set;
27import java.util.SortedMap;
28import java.util.TreeMap;
29import java.util.TreeSet;
30import java.util.function.Predicate;
31import java.util.regex.Pattern;
32import java.util.stream.Collectors;
33
34import javax.swing.JOptionPane;
35import javax.swing.JTree;
36import javax.swing.tree.DefaultMutableTreeNode;
37import javax.swing.tree.TreeModel;
38import javax.swing.tree.TreeNode;
39
40import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
41import org.openstreetmap.josm.data.projection.ProjectionRegistry;
42import org.openstreetmap.josm.data.validation.tests.Addresses;
43import org.openstreetmap.josm.data.validation.tests.ApiCapabilitiesTest;
44import org.openstreetmap.josm.data.validation.tests.BarriersEntrances;
45import org.openstreetmap.josm.data.validation.tests.Coastlines;
46import org.openstreetmap.josm.data.validation.tests.ConditionalKeys;
47import org.openstreetmap.josm.data.validation.tests.ConnectivityRelations;
48import org.openstreetmap.josm.data.validation.tests.CrossingWays;
49import org.openstreetmap.josm.data.validation.tests.DuplicateNode;
50import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
51import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
52import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
53import org.openstreetmap.josm.data.validation.tests.Highways;
54import org.openstreetmap.josm.data.validation.tests.InternetTags;
55import org.openstreetmap.josm.data.validation.tests.Lanes;
56import org.openstreetmap.josm.data.validation.tests.LongSegment;
57import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
58import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
59import org.openstreetmap.josm.data.validation.tests.NameMismatch;
60import org.openstreetmap.josm.data.validation.tests.OpeningHourTest;
61import org.openstreetmap.josm.data.validation.tests.OverlappingWays;
62import org.openstreetmap.josm.data.validation.tests.PowerLines;
63import org.openstreetmap.josm.data.validation.tests.PublicTransportRouteTest;
64import org.openstreetmap.josm.data.validation.tests.RelationChecker;
65import org.openstreetmap.josm.data.validation.tests.RightAngleBuildingTest;
66import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
67import org.openstreetmap.josm.data.validation.tests.SharpAngles;
68import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
69import org.openstreetmap.josm.data.validation.tests.TagChecker;
70import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest;
71import org.openstreetmap.josm.data.validation.tests.UnclosedWays;
72import org.openstreetmap.josm.data.validation.tests.UnconnectedWays;
73import org.openstreetmap.josm.data.validation.tests.UntaggedNode;
74import org.openstreetmap.josm.data.validation.tests.UntaggedWay;
75import org.openstreetmap.josm.data.validation.tests.WayConnectedToArea;
76import org.openstreetmap.josm.data.validation.tests.WronglyOrderedWays;
77import org.openstreetmap.josm.gui.MainApplication;
78import org.openstreetmap.josm.gui.layer.ValidatorLayer;
79import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
80import org.openstreetmap.josm.gui.util.GuiHelper;
81import org.openstreetmap.josm.spi.preferences.Config;
82import org.openstreetmap.josm.tools.AlphanumComparator;
83import org.openstreetmap.josm.tools.Logging;
84import org.openstreetmap.josm.tools.Stopwatch;
85
86/**
87 * A OSM data validator.
88 *
89 * @author Francisco R. Santos <frsantos@gmail.com>
90 */
91public final class OsmValidator {
92
93 private OsmValidator() {
94 // Hide default constructor for utilities classes
95 }
96
97 private static volatile ValidatorLayer errorLayer;
98
99 /** Grid detail, multiplier of east,north values for valuable cell sizing */
100 private static double griddetail;
101
102 private static final SortedMap<String, String> ignoredErrors = new TreeMap<>();
103 /**
104 * All registered tests
105 */
106 private static final Collection<Class<? extends Test>> allTests = new ArrayList<>();
107 private static final Map<String, Test> allTestsMap = new HashMap<>();
108
109 /**
110 * All available tests in core
111 */
112 @SuppressWarnings("unchecked")
113 private static final Class<Test>[] CORE_TEST_CLASSES = new Class[] {// NOPMD
114 /* FIXME - unique error numbers for tests aren't properly unique - ignoring will not work as expected */
115 DuplicateNode.class, // ID 1 .. 99
116 OverlappingWays.class, // ID 101 .. 199
117 UntaggedNode.class, // ID 201 .. 299
118 UntaggedWay.class, // ID 301 .. 399
119 SelfIntersectingWay.class, // ID 401 .. 499
120 DuplicatedWayNodes.class, // ID 501 .. 599
121 CrossingWays.Ways.class, // ID 601 .. 699
122 CrossingWays.Boundaries.class, // ID 601 .. 699
123 CrossingWays.SelfCrossing.class, // ID 601 .. 699
124 SimilarNamedWays.class, // ID 701 .. 799
125 Coastlines.class, // ID 901 .. 999
126 WronglyOrderedWays.class, // ID 1001 .. 1099
127 UnclosedWays.class, // ID 1101 .. 1199
128 TagChecker.class, // ID 1201 .. 1299
129 UnconnectedWays.UnconnectedHighways.class, // ID 1301 .. 1399
130 UnconnectedWays.UnconnectedRailways.class, // ID 1301 .. 1399
131 UnconnectedWays.UnconnectedWaterways.class, // ID 1301 .. 1399
132 UnconnectedWays.UnconnectedNaturalOrLanduse.class, // ID 1301 .. 1399
133 UnconnectedWays.UnconnectedPower.class, // ID 1301 .. 1399
134 DuplicateWay.class, // ID 1401 .. 1499
135 NameMismatch.class, // ID 1501 .. 1599
136 MultipolygonTest.class, // ID 1601 .. 1699
137 RelationChecker.class, // ID 1701 .. 1799
138 TurnrestrictionTest.class, // ID 1801 .. 1899
139 DuplicateRelation.class, // ID 1901 .. 1999
140 WayConnectedToArea.class, // ID 2301 .. 2399
141 PowerLines.class, // ID 2501 .. 2599
142 Addresses.class, // ID 2601 .. 2699
143 Highways.class, // ID 2701 .. 2799
144 BarriersEntrances.class, // ID 2801 .. 2899
145 OpeningHourTest.class, // 2901 .. 2999
146 MapCSSTagChecker.class, // 3000 .. 3099
147 Lanes.class, // 3100 .. 3199
148 ConditionalKeys.class, // 3200 .. 3299
149 InternetTags.class, // 3300 .. 3399
150 ApiCapabilitiesTest.class, // 3400 .. 3499
151 LongSegment.class, // 3500 .. 3599
152 PublicTransportRouteTest.class, // 3600 .. 3699
153 RightAngleBuildingTest.class, // 3700 .. 3799
154 SharpAngles.class, // 3800 .. 3899
155 ConnectivityRelations.class, // 3900 .. 3999
156 };
157
158 /**
159 * Adds a test to the list of available tests
160 * @param testClass The test class
161 */
162 public static void addTest(Class<? extends Test> testClass) {
163 allTests.add(testClass);
164 try {
165 allTestsMap.put(testClass.getName(), testClass.getConstructor().newInstance());
166 } catch (ReflectiveOperationException e) {
167 Logging.error(e);
168 }
169 }
170
171 /**
172 * Removes a test from the list of available tests. This will not remove
173 * core tests.
174 *
175 * @param testClass The test class
176 * @return {@code true} if the test was removed (see {@link Collection#remove})
177 * @since 15603
178 */
179 public static boolean removeTest(Class<? extends Test> testClass) {
180 boolean removed = false;
181 if (!Arrays.asList(CORE_TEST_CLASSES).contains(testClass)) {
182 removed = allTests.remove(testClass);
183 allTestsMap.remove(testClass.getName());
184 }
185 return removed;
186 }
187
188 static {
189 for (Class<? extends Test> testClass : CORE_TEST_CLASSES) {
190 addTest(testClass);
191 }
192 }
193
194 /**
195 * Initializes {@code OsmValidator}.
196 */
197 public static void initialize() {
198 initializeGridDetail();
199 loadIgnoredErrors();
200 }
201
202 /**
203 * Returns the validator directory.
204 *
205 * @return The validator directory
206 */
207 public static String getValidatorDir() {
208 File dir = new File(Config.getDirs().getUserDataDirectory(true), "validator");
209 try {
210 return dir.getAbsolutePath();
211 } catch (SecurityException e) {
212 Logging.log(Logging.LEVEL_ERROR, null, e);
213 return dir.getPath();
214 }
215 }
216
217 private static void loadIgnoredErrors() {
218 ignoredErrors.clear();
219 if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) {
220 Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(ignoredErrors::putAll);
221 Path path = Paths.get(getValidatorDir()).resolve("ignorederrors");
222 try {
223 if (path.toFile().exists()) {
224 try {
225 TreeSet<String> treeSet = new TreeSet<>();
226 treeSet.addAll(Files.readAllLines(path, StandardCharsets.UTF_8));
227 treeSet.forEach(ignore -> ignoredErrors.putIfAbsent(ignore, ""));
228 removeLegacyEntries(true);
229
230 saveIgnoredErrors();
231 Files.deleteIfExists(path);
232
233 } catch (FileNotFoundException e) {
234 Logging.debug(Logging.getErrorMessage(e));
235 } catch (IOException e) {
236 Logging.error(e);
237 }
238 }
239 } catch (SecurityException e) {
240 Logging.log(Logging.LEVEL_ERROR, "Unable to load ignored errors", e);
241 }
242 removeLegacyEntries(Config.getPref().get(ValidatorPrefHelper.PREF_IGNORELIST_FORMAT).isEmpty());
243 }
244 }
245
246 private static void removeLegacyEntries(boolean force) {
247 // see #19053:
248 boolean wasChanged = false;
249 if (force) {
250 Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator();
251 while (iter.hasNext()) {
252 Entry<String, String> entry = iter.next();
253 if (entry.getKey().startsWith("3000_")) {
254 Logging.warn(tr("Cannot handle ignore list entry {0}", entry));
255 iter.remove();
256 wasChanged = true;
257 }
258 }
259 }
260 String legacyEntry = ignoredErrors.remove("3000");
261 if (legacyEntry != null) {
262 if (!legacyEntry.isEmpty()) {
263 addIgnoredError("3000_" + legacyEntry, legacyEntry);
264 }
265 wasChanged = true;
266 }
267 if (wasChanged) {
268 saveIgnoredErrors();
269 }
270 }
271
272 /**
273 * Adds an ignored error
274 * @param s The ignore group / sub group name
275 * @see TestError#getIgnoreGroup()
276 * @see TestError#getIgnoreSubGroup()
277 */
278 public static void addIgnoredError(String s) {
279 addIgnoredError(s, "");
280 }
281
282 /**
283 * Adds an ignored error
284 * @param s The ignore group / sub group name
285 * @param description What the error actually is
286 * @see TestError#getIgnoreGroup()
287 * @see TestError#getIgnoreSubGroup()
288 */
289 public static void addIgnoredError(String s, String description) {
290 if (description == null) description = "";
291 ignoredErrors.put(s, description);
292 }
293
294 /**
295 * Make sure that we don't keep single entries for a "group ignore".
296 */
297 static void cleanupIgnoredErrors() {
298 cleanup3000();
299 if (ignoredErrors.size() > 1) {
300 List<String> toRemove = new ArrayList<>();
301
302 Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator();
303 String lastKey = iter.next().getKey();
304 while (iter.hasNext()) {
305 String currKey = iter.next().getKey();
306 if (currKey.startsWith(lastKey) && sameCode(currKey, lastKey)) {
307 toRemove.add(currKey);
308 } else {
309 lastKey = currKey;
310 }
311 }
312 toRemove.forEach(ignoredErrors::remove);
313 }
314
315 Map<String, String> tmap = buildIgnore(buildJTreeList());
316 if (!tmap.isEmpty()) {
317 ignoredErrors.clear();
318 ignoredErrors.putAll(tmap);
319 }
320 }
321
322 private static void cleanup3000() {
323 // see #19053
324 Set<String> toRemove = new HashSet<>();
325 for (Entry<String, String> entry : ignoredErrors.entrySet()) {
326 if (entry.getKey().equals("3000_" + entry.getValue()))
327 toRemove.add(entry.getValue());
328 }
329 ignoredErrors.entrySet()
330 .removeIf(e -> toRemove.contains(e.getValue()) && !e.getKey().equals("3000_" + e.getValue()));
331
332 }
333
334 private static boolean sameCode(String key1, String key2) {
335 return extractCodeFromIgnoreKey(key1).equals(extractCodeFromIgnoreKey(key2));
336 }
337
338 /**
339 * Extract the leading digits building the code for the error key.
340 * @param key the error key
341 * @return the leading digits
342 */
343 private static String extractCodeFromIgnoreKey(String key) {
344 int lenCode = 0;
345
346 for (int i = 0; i < key.length(); i++) {
347 if (key.charAt(i) >= '0' && key.charAt(i) <= '9') {
348 lenCode++;
349 } else {
350 break;
351 }
352 }
353 return key.substring(0, lenCode);
354 }
355
356 /**
357 * Check if a error should be ignored
358 * @param s The ignore group / sub group name
359 * @return <code>true</code> to ignore that error
360 */
361 public static boolean hasIgnoredError(String s) {
362 return ignoredErrors.containsKey(s);
363 }
364
365 /**
366 * Get the list of all ignored errors
367 * @return The <code>Collection&lt;String&gt;</code> of errors that are ignored
368 */
369 public static SortedMap<String, String> getIgnoredErrors() {
370 return ignoredErrors;
371 }
372
373 /**
374 * Build a JTree with a list
375 * @return &lt;type&gt;list as a {@code JTree}
376 */
377 public static JTree buildJTreeList() {
378 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Ignore list"));
379 final Pattern elemId1Pattern = Pattern.compile(":(r|w|n)_");
380 final Pattern elemId2Pattern = Pattern.compile("^[0-9]+$");
381 for (Entry<String, String> e: ignoredErrors.entrySet()) {
382 String key = e.getKey();
383 // key starts with a code, it maybe followed by a string (eg. a MapCSS rule) and
384 // optionally with a list of one or more OSM element IDs
385 String description = e.getValue();
386
387 ArrayList<String> ignoredElementList = new ArrayList<>();
388 String[] osmobjects = elemId1Pattern.split(key);
389 for (int i = 1; i < osmobjects.length; i++) {
390 String osmid = osmobjects[i];
391 if (elemId2Pattern.matcher(osmid).matches()) {
392 osmid = '_' + osmid;
393 int index = key.indexOf(osmid);
394 if (index < key.lastIndexOf(']')) continue;
395 char type = key.charAt(index - 1);
396 ignoredElementList.add(type + osmid);
397 }
398 }
399 for (String osmignore : ignoredElementList) {
400 key = key.replace(':' + osmignore, "");
401 }
402
403 DefaultMutableTreeNode trunk;
404 DefaultMutableTreeNode branch;
405
406 if (description != null && !description.isEmpty()) {
407 trunk = inTree(root, description);
408 branch = inTree(trunk, key);
409 trunk.add(branch);
410 } else {
411 trunk = inTree(root, key);
412 branch = trunk;
413 }
414 if (!ignoredElementList.isEmpty()) {
415 String item;
416 if (ignoredElementList.size() == 1) {
417 item = ignoredElementList.iterator().next();
418 } else {
419 // combination of two or more objects, keep them together
420 item = ignoredElementList.toString(); // [ID1, ID2, ..., IDn]
421 }
422 branch.add(new DefaultMutableTreeNode(item));
423 }
424 root.add(trunk);
425 }
426 return new JTree(root);
427 }
428
429 private static DefaultMutableTreeNode inTree(DefaultMutableTreeNode root, String name) {
430 @SuppressWarnings("unchecked")
431 Enumeration<TreeNode> trunks = root.children();
432 while (trunks.hasMoreElements()) {
433 TreeNode ttrunk = trunks.nextElement();
434 if (ttrunk instanceof DefaultMutableTreeNode) {
435 DefaultMutableTreeNode trunk = (DefaultMutableTreeNode) ttrunk;
436 if (name.equals(trunk.getUserObject())) {
437 return trunk;
438 }
439 }
440 }
441 return new DefaultMutableTreeNode(name);
442 }
443
444 /**
445 * Build a {@code HashMap} from a tree of ignored errors
446 * @param tree The JTree of ignored errors
447 * @return A {@code HashMap} of the ignored errors for comparison
448 */
449 public static Map<String, String> buildIgnore(JTree tree) {
450 TreeModel model = tree.getModel();
451 DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
452 return buildIgnore(model, root);
453 }
454
455 private static Map<String, String> buildIgnore(TreeModel model, DefaultMutableTreeNode node) {
456 HashMap<String, String> rHashMap = new HashMap<>();
457
458 for (int i = 0; i < model.getChildCount(node); i++) {
459 DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(node, i);
460 if (model.getChildCount(child) == 0) {
461 // create an entry for the error list
462 String key = node.getUserObject().toString();
463 String description;
464
465 if (!model.getRoot().equals(node)) {
466 description = ((DefaultMutableTreeNode) node.getParent()).getUserObject().toString();
467 } else {
468 description = key; // we get here when reading old file ignorederrors
469 }
470 if (tr("Ignore list").equals(description))
471 description = "";
472 if (!key.matches("^[0-9]+(_.*|$)")) {
473 description = key;
474 key = "";
475 }
476
477 String item = child.getUserObject().toString();
478 String entry = null;
479 if (item.matches("^\\[(r|w|n)_.*")) {
480 // list of elements (produced with list.toString() method)
481 entry = key + ":" + item.substring(1, item.lastIndexOf(']')).replace(", ", ":");
482 } else if (item.matches("^(r|w|n)_.*")) {
483 // single element
484 entry = key + ":" + item;
485 } else if (item.matches("^[0-9]+(_.*|)$")) {
486 // no element ids
487 entry = item;
488 }
489 if (entry != null) {
490 rHashMap.put(entry, description);
491 } else {
492 Logging.warn("ignored unexpected item in validator ignore list management dialog:'" + item + "'");
493 }
494 } else {
495 rHashMap.putAll(buildIgnore(model, child));
496 }
497 }
498 return rHashMap;
499 }
500
501 /**
502 * Reset the error list by deleting {@code validator.ignorelist}
503 */
504 public static void resetErrorList() {
505 saveIgnoredErrors();
506 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null);
507 OsmValidator.initialize();
508 }
509
510 /**
511 * Saves the names of the ignored errors to a preference
512 */
513 public static void saveIgnoredErrors() {
514 List<Map<String, String>> list = new ArrayList<>();
515 cleanupIgnoredErrors();
516 ignoredErrors.remove("3000"); // see #19053
517 list.add(ignoredErrors);
518 int i = 0;
519 while (i < list.size()) {
520 if (list.get(i) == null || list.get(i).isEmpty()) {
521 list.remove(i);
522 continue;
523 }
524 i++;
525 }
526 if (list.isEmpty()) list = null;
527 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, list);
528 Config.getPref().put(ValidatorPrefHelper.PREF_IGNORELIST_FORMAT, "2");
529 }
530
531 /**
532 * Initializes error layer.
533 */
534 public static synchronized void initializeErrorLayer() {
535 if (errorLayer == null && Boolean.TRUE.equals(ValidatorPrefHelper.PREF_LAYER.get())) {
536 errorLayer = new ValidatorLayer();
537 MainApplication.getLayerManager().addLayer(errorLayer);
538 }
539 }
540
541 /**
542 * Resets error layer.
543 * @since 11852
544 */
545 public static synchronized void resetErrorLayer() {
546 errorLayer = null;
547 }
548
549 /**
550 * Gets a map from simple names to all tests.
551 * @return A map of all tests, indexed and sorted by the name of their Java class
552 */
553 public static SortedMap<String, Test> getAllTestsMap() {
554 applyPrefs(allTestsMap, false);
555 applyPrefs(allTestsMap, true);
556 return new TreeMap<>(allTestsMap);
557 }
558
559 /**
560 * Returns the instance of the given test class.
561 * @param <T> testClass type
562 * @param testClass The class of test to retrieve
563 * @return the instance of the given test class, if any, or {@code null}
564 * @since 6670
565 */
566 @SuppressWarnings("unchecked")
567 public static <T extends Test> T getTest(Class<T> testClass) {
568 if (testClass == null) {
569 return null;
570 }
571 return (T) allTestsMap.get(testClass.getName());
572 }
573
574 private static void applyPrefs(Map<String, Test> tests, boolean beforeUpload) {
575 for (String testName : Config.getPref().getList(beforeUpload
576 ? ValidatorPrefHelper.PREF_SKIP_TESTS_BEFORE_UPLOAD : ValidatorPrefHelper.PREF_SKIP_TESTS)) {
577 Test test = tests.get(testName);
578 if (test != null) {
579 if (beforeUpload) {
580 test.testBeforeUpload = false;
581 } else {
582 test.enabled = false;
583 }
584 }
585 }
586 }
587
588 /**
589 * Gets all tests that are possible
590 * @return The tests
591 */
592 public static Collection<Test> getTests() {
593 return getAllTestsMap().values();
594 }
595
596 /**
597 * Gets all tests that are run
598 * @param beforeUpload To get the ones that are run before upload
599 * @return The tests
600 */
601 public static Collection<Test> getEnabledTests(boolean beforeUpload) {
602 Collection<Test> enabledTests = getTests();
603 for (Test t : new ArrayList<>(enabledTests)) {
604 if (beforeUpload ? t.testBeforeUpload : t.enabled) {
605 continue;
606 }
607 enabledTests.remove(t);
608 }
609 return enabledTests;
610 }
611
612 /**
613 * Gets the list of all available test classes
614 *
615 * @return A collection of the test classes
616 */
617 public static Collection<Class<? extends Test>> getAllAvailableTestClasses() {
618 return Collections.unmodifiableCollection(allTests);
619 }
620
621 /**
622 * Initialize grid details based on current projection system. Values based on
623 * the original value fixed for EPSG:4326 (10000) using heuristics (that is, test&amp;error
624 * until most bugs were discovered while keeping the processing time reasonable)
625 */
626 public static void initializeGridDetail() {
627 String code = ProjectionRegistry.getProjection().toCode();
628 if (Arrays.asList(ProjectionPreference.wgs84.allCodes()).contains(code)) {
629 OsmValidator.griddetail = 10_000;
630 } else if (Arrays.asList(ProjectionPreference.mercator.allCodes()).contains(code)) {
631 OsmValidator.griddetail = 0.01;
632 } else if (Arrays.asList(ProjectionPreference.lambert.allCodes()).contains(code)) {
633 OsmValidator.griddetail = 0.1;
634 } else {
635 OsmValidator.griddetail = 1.0;
636 }
637 }
638
639 /**
640 * Returns grid detail, multiplier of east,north values for valuable cell sizing
641 * @return grid detail
642 * @since 11852
643 */
644 public static double getGridDetail() {
645 return griddetail;
646 }
647
648 private static boolean testsInitialized;
649
650 /**
651 * Initializes all tests if this operations hasn't been performed already.
652 */
653 public static synchronized void initializeTests() {
654 if (!testsInitialized) {
655 final String message = "Initializing validator tests";
656 Logging.debug(message);
657 final Stopwatch stopwatch = Stopwatch.createStarted();
658 initializeTests(getTests());
659 testsInitialized = true;
660 Logging.debug(stopwatch.toString("Initializing validator tests"));
661 }
662 }
663
664 /**
665 * Initializes all tests
666 * @param allTests The tests to initialize
667 */
668 public static void initializeTests(Collection<? extends Test> allTests) {
669 for (Test test : allTests) {
670 try {
671 if (test.enabled) {
672 test.initialize();
673 }
674 } catch (Exception e) { // NOPMD
675 String message = tr("Error initializing test {0}:\n {1}", test.getClass().getSimpleName(), e);
676 Logging.error(message);
677 if (!GraphicsEnvironment.isHeadless()) {
678 GuiHelper.runInEDT(() ->
679 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), message, tr("Error"), JOptionPane.ERROR_MESSAGE)
680 );
681 }
682 }
683 }
684 }
685
686 /**
687 * Groups the given collection of errors by severity, then message, then description.
688 * @param errors list of errors to group
689 * @param filterToUse optional filter
690 * @return collection of errors grouped by severity, then message, then description
691 * @since 12667
692 */
693 public static Map<Severity, Map<String, Map<String, List<TestError>>>> getErrorsBySeverityMessageDescription(
694 Collection<TestError> errors, Predicate<? super TestError> filterToUse) {
695 return errors.stream().filter(filterToUse).collect(
696 Collectors.groupingBy(TestError::getSeverity, () -> new EnumMap<>(Severity.class),
697 Collectors.groupingBy(TestError::getMessage, () -> new TreeMap<>(AlphanumComparator.getInstance()),
698 Collectors.groupingBy(e -> e.getDescription() == null ? "" : e.getDescription(),
699 () -> new TreeMap<>(AlphanumComparator.getInstance()),
700 Collectors.toList()
701 ))));
702 }
703
704 /**
705 * For unit tests
706 */
707 static void clearIgnoredErrors() {
708 ignoredErrors.clear();
709 }
710}
Note: See TracBrowser for help on using the repository browser.