source: josm/trunk/src/org/openstreetmap/josm/data/validation/Test.java@ 16069

Last change on this file since 16069 was 16069, checked in by simon04, 4 years ago

see #18752 - Harmonize stopwatch logging

  • Property svn:eol-style set to native
File size: 11.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.awt.GridBagConstraints;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.List;
10import java.util.Optional;
11import java.util.function.Predicate;
12
13import javax.swing.JCheckBox;
14import javax.swing.JPanel;
15
16import org.openstreetmap.josm.command.Command;
17import org.openstreetmap.josm.command.DeleteCommand;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.Relation;
21import org.openstreetmap.josm.data.osm.Way;
22import org.openstreetmap.josm.data.osm.search.SearchCompiler.InDataSourceArea;
23import org.openstreetmap.josm.data.osm.search.SearchCompiler.NotOutsideDataSourceArea;
24import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
25import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
26import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
27import org.openstreetmap.josm.gui.progress.ProgressMonitor;
28import org.openstreetmap.josm.tools.GBC;
29import org.openstreetmap.josm.tools.Logging;
30import org.openstreetmap.josm.tools.Stopwatch;
31
32/**
33 * Parent class for all validation tests.
34 * <p>
35 * A test is a primitive visitor, so that it can access to all data to be
36 * validated. These primitives are always visited in the same order: nodes
37 * first, then ways.
38 *
39 * @author frsantos
40 */
41public class Test implements OsmPrimitiveVisitor {
42
43 protected static final Predicate<OsmPrimitive> IN_DOWNLOADED_AREA = new NotOutsideDataSourceArea();
44 protected static final Predicate<OsmPrimitive> IN_DOWNLOADED_AREA_STRICT = new InDataSourceArea(true);
45
46 /** Name of the test */
47 protected final String name;
48
49 /** Description of the test */
50 protected final String description;
51
52 /** Whether this test is enabled. Enabled by default */
53 public boolean enabled = true;
54
55 /** The preferences check for validation */
56 protected JCheckBox checkEnabled;
57
58 /** The preferences check for validation on upload */
59 protected JCheckBox checkBeforeUpload;
60
61 /** Whether this test must check before upload. Enabled by default */
62 public boolean testBeforeUpload = true;
63
64 /** Whether this test is performing just before an upload */
65 protected boolean isBeforeUpload;
66
67 /** The list of errors */
68 protected List<TestError> errors = new ArrayList<>();
69
70 /** Whether the test is run on a partial selection data */
71 protected boolean partialSelection;
72
73 /** the progress monitor to use */
74 protected ProgressMonitor progressMonitor;
75
76 /** the start time to compute elapsed time when test finishes */
77 protected Stopwatch stopwatch;
78
79 private boolean showElementCount;
80
81 /**
82 * Constructor
83 * @param name Name of the test
84 * @param description Description of the test
85 */
86 public Test(String name, String description) {
87 this.name = name;
88 this.description = description;
89 }
90
91 /**
92 * Constructor
93 * @param name Name of the test
94 */
95 public Test(String name) {
96 this(name, null);
97 }
98
99 /**
100 * A test that forwards all primitives to {@link #check(OsmPrimitive)}.
101 */
102 public abstract static class TagTest extends Test {
103 /**
104 * Constructs a new {@code TagTest} with given name and description.
105 * @param name The test name
106 * @param description The test description
107 */
108 public TagTest(String name, String description) {
109 super(name, description);
110 }
111
112 /**
113 * Constructs a new {@code TagTest} with given name.
114 * @param name The test name
115 */
116 public TagTest(String name) {
117 super(name);
118 }
119
120 @Override
121 public boolean isPrimitiveUsable(OsmPrimitive p) {
122 return super.isPrimitiveUsable(p) && p.isTagged();
123 }
124
125 /**
126 * Checks the tags of the given primitive.
127 * @param p The primitive to test
128 */
129 public abstract void check(OsmPrimitive p);
130
131 @Override
132 public void visit(Node n) {
133 check(n);
134 }
135
136 @Override
137 public void visit(Way w) {
138 check(w);
139 }
140
141 @Override
142 public void visit(Relation r) {
143 check(r);
144 }
145
146 protected boolean includeOtherSeverityChecks() {
147 return isBeforeUpload ? ValidatorPrefHelper.PREF_OTHER_UPLOAD.get() : ValidatorPrefHelper.PREF_OTHER.get();
148 }
149 }
150
151 /**
152 * Initializes any global data used this tester.
153 * @throws Exception When cannot initialize the test
154 */
155 public void initialize() throws Exception {
156 this.stopwatch = Stopwatch.createStarted();
157 }
158
159 /**
160 * Start the test using a given progress monitor
161 *
162 * @param progressMonitor the progress monitor
163 */
164 public void startTest(ProgressMonitor progressMonitor) {
165 this.progressMonitor = Optional.ofNullable(progressMonitor).orElse(NullProgressMonitor.INSTANCE);
166 String startMessage = tr("Running test {0}", name);
167 this.progressMonitor.beginTask(startMessage);
168 Logging.debug(startMessage);
169 this.errors = new ArrayList<>(30);
170 this.stopwatch = Stopwatch.createStarted();
171 }
172
173 /**
174 * Flag notifying that this test is run over a partial data selection
175 * @param partialSelection Whether the test is on a partial selection data
176 */
177 public void setPartialSelection(boolean partialSelection) {
178 this.partialSelection = partialSelection;
179 }
180
181 /**
182 * Gets the validation errors accumulated until this moment.
183 * @return The list of errors
184 */
185 public List<TestError> getErrors() {
186 return errors;
187 }
188
189 /**
190 * Notification of the end of the test. The tester may perform additional
191 * actions and destroy the used structures.
192 * <p>
193 * If you override this method, don't forget to cleanup {@code progressMonitor}
194 * (most overrides call {@code super.endTest()} to do this).
195 */
196 public void endTest() {
197 progressMonitor.finishTask();
198 progressMonitor = null;
199 if (stopwatch.elapsed() > 0) {
200 Logging.debug(stopwatch.toString(getName()));
201 }
202 }
203
204 /**
205 * Visits all primitives to be tested. These primitives are always visited
206 * in the same order: nodes first, then ways.
207 *
208 * @param selection The primitives to be tested
209 */
210 public void visit(Collection<OsmPrimitive> selection) {
211 if (progressMonitor != null) {
212 progressMonitor.setTicksCount(selection.size());
213 }
214 long cnt = 0;
215 for (OsmPrimitive p : selection) {
216 if (isCanceled()) {
217 break;
218 }
219 if (isPrimitiveUsable(p)) {
220 p.accept(this);
221 }
222 if (progressMonitor != null) {
223 progressMonitor.worked(1);
224 cnt++;
225 // add frequently changing info to progress monitor so that it
226 // doesn't seem to hang when test takes long
227 if (showElementCount && cnt % 1000 == 0) {
228 progressMonitor.setExtraText(tr("{0} of {1} elements done", cnt, selection.size()));
229 }
230 }
231 }
232 }
233
234 /**
235 * Determines if the primitive is usable for tests.
236 * @param p The primitive
237 * @return {@code true} if the primitive can be tested, {@code false} otherwise
238 */
239 public boolean isPrimitiveUsable(OsmPrimitive p) {
240 return p.isUsable() && (!(p instanceof Way) || (((Way) p).getNodesCount() > 1)); // test only Ways with at least 2 nodes
241 }
242
243 @Override
244 public void visit(Node n) {
245 // To be overridden in subclasses
246 }
247
248 @Override
249 public void visit(Way w) {
250 // To be overridden in subclasses
251 }
252
253 @Override
254 public void visit(Relation r) {
255 // To be overridden in subclasses
256 }
257
258 /**
259 * Allow the tester to manage its own preferences
260 * @param testPanel The panel to add any preferences component
261 */
262 public void addGui(JPanel testPanel) {
263 checkEnabled = new JCheckBox(name, enabled);
264 checkEnabled.setToolTipText(description);
265 testPanel.add(checkEnabled, GBC.std());
266
267 GBC a = GBC.eol();
268 a.anchor = GridBagConstraints.EAST;
269 checkBeforeUpload = new JCheckBox();
270 checkBeforeUpload.setSelected(testBeforeUpload);
271 testPanel.add(checkBeforeUpload, a);
272 }
273
274 /**
275 * Called when the used submits the preferences
276 * @return {@code true} if restart is required, {@code false} otherwise
277 */
278 public boolean ok() {
279 enabled = checkEnabled.isSelected();
280 testBeforeUpload = checkBeforeUpload.isSelected();
281 return false;
282 }
283
284 /**
285 * Fixes the error with the appropriate command
286 *
287 * @param testError error to fix
288 * @return The command to fix the error
289 */
290 public Command fixError(TestError testError) {
291 return null;
292 }
293
294 /**
295 * Returns true if the given error can be fixed automatically
296 *
297 * @param testError The error to check if can be fixed
298 * @return true if the error can be fixed
299 */
300 public boolean isFixable(TestError testError) {
301 return false;
302 }
303
304 /**
305 * Returns true if this plugin must check the uploaded data before uploading
306 * @return true if this plugin must check the uploaded data before uploading
307 */
308 public boolean testBeforeUpload() {
309 return testBeforeUpload;
310 }
311
312 /**
313 * Sets the flag that marks an upload check
314 * @param isUpload if true, the test is before upload
315 */
316 public void setBeforeUpload(boolean isUpload) {
317 this.isBeforeUpload = isUpload;
318 }
319
320 /**
321 * Returns the test name.
322 * @return The test name
323 */
324 public String getName() {
325 return name;
326 }
327
328 /**
329 * Determines if the test has been canceled.
330 * @return {@code true} if the test has been canceled, {@code false} otherwise
331 */
332 public boolean isCanceled() {
333 return progressMonitor != null && progressMonitor.isCanceled();
334 }
335
336 /**
337 * Build a Delete command on all primitives that have not yet been deleted manually by user, or by another error fix.
338 * If all primitives have already been deleted, null is returned.
339 * @param primitives The primitives wanted for deletion
340 * @return a Delete command on all primitives that have not yet been deleted, or null otherwise
341 */
342 protected final Command deletePrimitivesIfNeeded(Collection<? extends OsmPrimitive> primitives) {
343 Collection<OsmPrimitive> primitivesToDelete = new ArrayList<>();
344 for (OsmPrimitive p : primitives) {
345 if (!p.isDeleted()) {
346 primitivesToDelete.add(p);
347 }
348 }
349 if (!primitivesToDelete.isEmpty()) {
350 return DeleteCommand.delete(primitivesToDelete);
351 } else {
352 return null;
353 }
354 }
355
356 /**
357 * Determines if the specified primitive denotes a building.
358 * @param p The primitive to be tested
359 * @return True if building key is set and different from no,entrance
360 */
361 protected static final boolean isBuilding(OsmPrimitive p) {
362 return p.hasTagDifferent("building", "no", "entrance");
363 }
364
365 /**
366 * Determines if the specified primitive denotes a residential area.
367 * @param p The primitive to be tested
368 * @return True if landuse key is equal to residential
369 */
370 protected static final boolean isResidentialArea(OsmPrimitive p) {
371 return p.hasTag("landuse", "residential");
372 }
373
374 /**
375 * Free resources.
376 */
377 public void clear() {
378 errors.clear();
379 }
380
381 protected void setShowElements(boolean b) {
382 showElementCount = b;
383 }
384
385 /**
386 * @return the name of this class (for ToolTip)
387 * since 15972
388 */
389 public Object getSource() {
390 return "Java: " + this.getClass().getName();
391 }
392}
Note: See TracBrowser for help on using the repository browser.