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

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

sonar - squid:S3725 - Java 8's Files.exists should not be used (The Files.exists method has noticeably poor performance in JDK 8, and can slow an application significantly when used to check files that don't actually exist)

  • Property svn:eol-style set to native
File size: 12.9 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.io.File;
7import java.io.FileNotFoundException;
8import java.io.IOException;
9import java.io.PrintWriter;
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.HashMap;
19import java.util.Map;
20import java.util.SortedMap;
21import java.util.TreeMap;
22import java.util.TreeSet;
23
24import javax.swing.JOptionPane;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.actions.ValidateAction;
28import org.openstreetmap.josm.data.validation.tests.Addresses;
29import org.openstreetmap.josm.data.validation.tests.ApiCapabilitiesTest;
30import org.openstreetmap.josm.data.validation.tests.BarriersEntrances;
31import org.openstreetmap.josm.data.validation.tests.Coastlines;
32import org.openstreetmap.josm.data.validation.tests.ConditionalKeys;
33import org.openstreetmap.josm.data.validation.tests.CrossingWays;
34import org.openstreetmap.josm.data.validation.tests.DuplicateNode;
35import org.openstreetmap.josm.data.validation.tests.DuplicateRelation;
36import org.openstreetmap.josm.data.validation.tests.DuplicateWay;
37import org.openstreetmap.josm.data.validation.tests.DuplicatedWayNodes;
38import org.openstreetmap.josm.data.validation.tests.Highways;
39import org.openstreetmap.josm.data.validation.tests.InternetTags;
40import org.openstreetmap.josm.data.validation.tests.Lanes;
41import org.openstreetmap.josm.data.validation.tests.LongSegment;
42import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
43import org.openstreetmap.josm.data.validation.tests.MultipolygonTest;
44import org.openstreetmap.josm.data.validation.tests.NameMismatch;
45import org.openstreetmap.josm.data.validation.tests.OpeningHourTest;
46import org.openstreetmap.josm.data.validation.tests.OverlappingWays;
47import org.openstreetmap.josm.data.validation.tests.PowerLines;
48import org.openstreetmap.josm.data.validation.tests.PublicTransportRouteTest;
49import org.openstreetmap.josm.data.validation.tests.RelationChecker;
50import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
51import org.openstreetmap.josm.data.validation.tests.SimilarNamedWays;
52import org.openstreetmap.josm.data.validation.tests.TagChecker;
53import org.openstreetmap.josm.data.validation.tests.TurnrestrictionTest;
54import org.openstreetmap.josm.data.validation.tests.UnclosedWays;
55import org.openstreetmap.josm.data.validation.tests.UnconnectedWays;
56import org.openstreetmap.josm.data.validation.tests.UntaggedNode;
57import org.openstreetmap.josm.data.validation.tests.UntaggedWay;
58import org.openstreetmap.josm.data.validation.tests.WayConnectedToArea;
59import org.openstreetmap.josm.data.validation.tests.WronglyOrderedWays;
60import org.openstreetmap.josm.gui.layer.ValidatorLayer;
61import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
62import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference;
63import org.openstreetmap.josm.tools.Utils;
64
65/**
66 * A OSM data validator.
67 *
68 * @author Francisco R. Santos <frsantos@gmail.com>
69 */
70public final class OsmValidator {
71
72 private OsmValidator() {
73 // Hide default constructor for utilities classes
74 }
75
76 public static volatile ValidatorLayer errorLayer;
77
78 /** The validate action */
79 public static final ValidateAction validateAction = new ValidateAction();
80
81 /** Grid detail, multiplier of east,north values for valuable cell sizing */
82 public static double griddetail;
83
84 private static final Collection<String> ignoredErrors = new TreeSet<>();
85
86 /**
87 * All registered tests
88 */
89 private static final Collection<Class<? extends Test>> allTests = new ArrayList<>();
90 private static final Map<String, Test> allTestsMap = new HashMap<>();
91
92 /**
93 * All available tests in core
94 */
95 @SuppressWarnings("unchecked")
96 private static final Class<Test>[] CORE_TEST_CLASSES = new Class[] {
97 /* FIXME - unique error numbers for tests aren't properly unique - ignoring will not work as expected */
98 DuplicateNode.class, // ID 1 .. 99
99 OverlappingWays.class, // ID 101 .. 199
100 UntaggedNode.class, // ID 201 .. 299
101 UntaggedWay.class, // ID 301 .. 399
102 SelfIntersectingWay.class, // ID 401 .. 499
103 DuplicatedWayNodes.class, // ID 501 .. 599
104 CrossingWays.Ways.class, // ID 601 .. 699
105 CrossingWays.Boundaries.class, // ID 601 .. 699
106 CrossingWays.Barrier.class, // ID 601 .. 699
107 SimilarNamedWays.class, // ID 701 .. 799
108 Coastlines.class, // ID 901 .. 999
109 WronglyOrderedWays.class, // ID 1001 .. 1099
110 UnclosedWays.class, // ID 1101 .. 1199
111 TagChecker.class, // ID 1201 .. 1299
112 UnconnectedWays.UnconnectedHighways.class, // ID 1301 .. 1399
113 UnconnectedWays.UnconnectedRailways.class, // ID 1301 .. 1399
114 UnconnectedWays.UnconnectedWaterways.class, // ID 1301 .. 1399
115 UnconnectedWays.UnconnectedNaturalOrLanduse.class, // ID 1301 .. 1399
116 UnconnectedWays.UnconnectedPower.class, // ID 1301 .. 1399
117 DuplicateWay.class, // ID 1401 .. 1499
118 NameMismatch.class, // ID 1501 .. 1599
119 MultipolygonTest.class, // ID 1601 .. 1699
120 RelationChecker.class, // ID 1701 .. 1799
121 TurnrestrictionTest.class, // ID 1801 .. 1899
122 DuplicateRelation.class, // ID 1901 .. 1999
123 WayConnectedToArea.class, // ID 2301 .. 2399
124 PowerLines.class, // ID 2501 .. 2599
125 Addresses.class, // ID 2601 .. 2699
126 Highways.class, // ID 2701 .. 2799
127 BarriersEntrances.class, // ID 2801 .. 2899
128 OpeningHourTest.class, // 2901 .. 2999
129 MapCSSTagChecker.class, // 3000 .. 3099
130 Lanes.class, // 3100 .. 3199
131 ConditionalKeys.class, // 3200 .. 3299
132 InternetTags.class, // 3300 .. 3399
133 ApiCapabilitiesTest.class, // 3400 .. 3499
134 LongSegment.class, // 3500 .. 3599
135 PublicTransportRouteTest.class, // 3600 .. 3699
136 };
137
138 public static void addTest(Class<? extends Test> testClass) {
139 allTests.add(testClass);
140 try {
141 allTestsMap.put(testClass.getName(), testClass.getConstructor().newInstance());
142 } catch (ReflectiveOperationException e) {
143 Main.error(e);
144 }
145 }
146
147 static {
148 for (Class<? extends Test> testClass : CORE_TEST_CLASSES) {
149 addTest(testClass);
150 }
151 }
152
153 /**
154 * Initializes {@code OsmValidator}.
155 */
156 public static void initialize() {
157 checkValidatorDir();
158 initializeGridDetail();
159 loadIgnoredErrors(); //FIXME: load only when needed
160 }
161
162 /**
163 * Returns the validator directory.
164 *
165 * @return The validator directory
166 */
167 public static String getValidatorDir() {
168 return new File(Main.pref.getUserDataDirectory(), "validator").getAbsolutePath();
169 }
170
171 /**
172 * Check if validator directory exists (store ignored errors file)
173 */
174 private static void checkValidatorDir() {
175 File pathDir = new File(getValidatorDir());
176 if (!pathDir.exists()) {
177 Utils.mkDirs(pathDir);
178 }
179 }
180
181 private static void loadIgnoredErrors() {
182 ignoredErrors.clear();
183 if (ValidatorPreference.PREF_USE_IGNORE.get()) {
184 Path path = Paths.get(getValidatorDir()).resolve("ignorederrors");
185 if (path.toFile().exists()) {
186 try {
187 ignoredErrors.addAll(Files.readAllLines(path, StandardCharsets.UTF_8));
188 } catch (final FileNotFoundException e) {
189 Main.debug(Main.getErrorMessage(e));
190 } catch (final IOException e) {
191 Main.error(e);
192 }
193 }
194 }
195 }
196
197 public static void addIgnoredError(String s) {
198 ignoredErrors.add(s);
199 }
200
201 public static boolean hasIgnoredError(String s) {
202 return ignoredErrors.contains(s);
203 }
204
205 public static void saveIgnoredErrors() {
206 try (PrintWriter out = new PrintWriter(new File(getValidatorDir(), "ignorederrors"), StandardCharsets.UTF_8.name())) {
207 for (String e : ignoredErrors) {
208 out.println(e);
209 }
210 } catch (IOException e) {
211 Main.error(e);
212 }
213 }
214
215 public static synchronized void initializeErrorLayer() {
216 if (!ValidatorPreference.PREF_LAYER.get())
217 return;
218 if (errorLayer == null) {
219 errorLayer = new ValidatorLayer();
220 Main.getLayerManager().addLayer(errorLayer);
221 }
222 }
223
224 /**
225 * Gets a map from simple names to all tests.
226 * @return A map of all tests, indexed and sorted by the name of their Java class
227 */
228 public static SortedMap<String, Test> getAllTestsMap() {
229 applyPrefs(allTestsMap, false);
230 applyPrefs(allTestsMap, true);
231 return new TreeMap<>(allTestsMap);
232 }
233
234 /**
235 * Returns the instance of the given test class.
236 * @param <T> testClass type
237 * @param testClass The class of test to retrieve
238 * @return the instance of the given test class, if any, or {@code null}
239 * @since 6670
240 */
241 @SuppressWarnings("unchecked")
242 public static <T extends Test> T getTest(Class<T> testClass) {
243 if (testClass == null) {
244 return null;
245 }
246 return (T) allTestsMap.get(testClass.getName());
247 }
248
249 private static void applyPrefs(Map<String, Test> tests, boolean beforeUpload) {
250 for (String testName : Main.pref.getCollection(beforeUpload
251 ? ValidatorPreference.PREF_SKIP_TESTS_BEFORE_UPLOAD : ValidatorPreference.PREF_SKIP_TESTS)) {
252 Test test = tests.get(testName);
253 if (test != null) {
254 if (beforeUpload) {
255 test.testBeforeUpload = false;
256 } else {
257 test.enabled = false;
258 }
259 }
260 }
261 }
262
263 public static Collection<Test> getTests() {
264 return getAllTestsMap().values();
265 }
266
267 public static Collection<Test> getEnabledTests(boolean beforeUpload) {
268 Collection<Test> enabledTests = getTests();
269 for (Test t : new ArrayList<>(enabledTests)) {
270 if (beforeUpload ? t.testBeforeUpload : t.enabled) {
271 continue;
272 }
273 enabledTests.remove(t);
274 }
275 return enabledTests;
276 }
277
278 /**
279 * Gets the list of all available test classes
280 *
281 * @return A collection of the test classes
282 */
283 public static Collection<Class<? extends Test>> getAllAvailableTestClasses() {
284 return Collections.unmodifiableCollection(allTests);
285 }
286
287 /**
288 * Initialize grid details based on current projection system. Values based on
289 * the original value fixed for EPSG:4326 (10000) using heuristics (that is, test&amp;error
290 * until most bugs were discovered while keeping the processing time reasonable)
291 */
292 public static void initializeGridDetail() {
293 String code = Main.getProjection().toCode();
294 if (Arrays.asList(ProjectionPreference.wgs84.allCodes()).contains(code)) {
295 OsmValidator.griddetail = 10000;
296 } else if (Arrays.asList(ProjectionPreference.mercator.allCodes()).contains(code)) {
297 OsmValidator.griddetail = 0.01;
298 } else if (Arrays.asList(ProjectionPreference.lambert.allCodes()).contains(code)) {
299 OsmValidator.griddetail = 0.1;
300 } else {
301 OsmValidator.griddetail = 1.0;
302 }
303 }
304
305 private static boolean testsInitialized;
306
307 /**
308 * Initializes all tests if this operations hasn't been performed already.
309 */
310 public static synchronized void initializeTests() {
311 if (!testsInitialized) {
312 Main.debug("Initializing validator tests");
313 final long startTime = System.currentTimeMillis();
314 initializeTests(getTests());
315 testsInitialized = true;
316 if (Main.isDebugEnabled()) {
317 final long elapsedTime = System.currentTimeMillis() - startTime;
318 Main.debug("Initializing validator tests completed in " + Utils.getDurationString(elapsedTime));
319 }
320 }
321 }
322
323 /**
324 * Initializes all tests
325 * @param allTests The tests to initialize
326 */
327 public static void initializeTests(Collection<? extends Test> allTests) {
328 for (Test test : allTests) {
329 try {
330 if (test.enabled) {
331 test.initialize();
332 }
333 } catch (Exception e) {
334 Main.error(e);
335 JOptionPane.showMessageDialog(Main.parent,
336 tr("Error initializing test {0}:\n {1}", test.getClass()
337 .getSimpleName(), e),
338 tr("Error"),
339 JOptionPane.ERROR_MESSAGE);
340 }
341 }
342 }
343
344}
Note: See TracBrowser for help on using the repository browser.