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

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

see #18455 - fix unit tests

  • 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.Utils;
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<>(30);
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 long startTime;
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.startTime = -1;
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.startTime = System.currentTimeMillis();
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 (startTime > 0) {
200 // fix #11567 where elapsedTime is < 0
201 long elapsedTime = Math.max(0, System.currentTimeMillis() - startTime);
202 Logging.debug(tr("Test ''{0}'' completed in {1}", getName(), Utils.getDurationString(elapsedTime)));
203 }
204 }
205
206 /**
207 * Visits all primitives to be tested. These primitives are always visited
208 * in the same order: nodes first, then ways.
209 *
210 * @param selection The primitives to be tested
211 */
212 public void visit(Collection<OsmPrimitive> selection) {
213 if (progressMonitor != null) {
214 progressMonitor.setTicksCount(selection.size());
215 }
216 long cnt = 0;
217 for (OsmPrimitive p : selection) {
218 if (isCanceled()) {
219 break;
220 }
221 if (isPrimitiveUsable(p)) {
222 p.accept(this);
223 }
224 if (progressMonitor != null) {
225 progressMonitor.worked(1);
226 cnt++;
227 // add frequently changing info to progress monitor so that it
228 // doesn't seem to hang when test takes long
229 if (showElementCount && cnt % 1000 == 0) {
230 progressMonitor.setExtraText(tr("{0} of {1} elements done", cnt, selection.size()));
231 }
232 }
233 }
234 }
235
236 /**
237 * Determines if the primitive is usable for tests.
238 * @param p The primitive
239 * @return {@code true} if the primitive can be tested, {@code false} otherwise
240 */
241 public boolean isPrimitiveUsable(OsmPrimitive p) {
242 return p.isUsable() && (!(p instanceof Way) || (((Way) p).getNodesCount() > 1)); // test only Ways with at least 2 nodes
243 }
244
245 @Override
246 public void visit(Node n) {
247 // To be overridden in subclasses
248 }
249
250 @Override
251 public void visit(Way w) {
252 // To be overridden in subclasses
253 }
254
255 @Override
256 public void visit(Relation r) {
257 // To be overridden in subclasses
258 }
259
260 /**
261 * Allow the tester to manage its own preferences
262 * @param testPanel The panel to add any preferences component
263 */
264 public void addGui(JPanel testPanel) {
265 checkEnabled = new JCheckBox(name, enabled);
266 checkEnabled.setToolTipText(description);
267 testPanel.add(checkEnabled, GBC.std());
268
269 GBC a = GBC.eol();
270 a.anchor = GridBagConstraints.EAST;
271 checkBeforeUpload = new JCheckBox();
272 checkBeforeUpload.setSelected(testBeforeUpload);
273 testPanel.add(checkBeforeUpload, a);
274 }
275
276 /**
277 * Called when the used submits the preferences
278 * @return {@code true} if restart is required, {@code false} otherwise
279 */
280 public boolean ok() {
281 enabled = checkEnabled.isSelected();
282 testBeforeUpload = checkBeforeUpload.isSelected();
283 return false;
284 }
285
286 /**
287 * Fixes the error with the appropriate command
288 *
289 * @param testError error to fix
290 * @return The command to fix the error
291 */
292 public Command fixError(TestError testError) {
293 return null;
294 }
295
296 /**
297 * Returns true if the given error can be fixed automatically
298 *
299 * @param testError The error to check if can be fixed
300 * @return true if the error can be fixed
301 */
302 public boolean isFixable(TestError testError) {
303 return false;
304 }
305
306 /**
307 * Returns true if this plugin must check the uploaded data before uploading
308 * @return true if this plugin must check the uploaded data before uploading
309 */
310 public boolean testBeforeUpload() {
311 return testBeforeUpload;
312 }
313
314 /**
315 * Sets the flag that marks an upload check
316 * @param isUpload if true, the test is before upload
317 */
318 public void setBeforeUpload(boolean isUpload) {
319 this.isBeforeUpload = isUpload;
320 }
321
322 /**
323 * Returns the test name.
324 * @return The test name
325 */
326 public String getName() {
327 return name;
328 }
329
330 /**
331 * Determines if the test has been canceled.
332 * @return {@code true} if the test has been canceled, {@code false} otherwise
333 */
334 public boolean isCanceled() {
335 return progressMonitor != null && progressMonitor.isCanceled();
336 }
337
338 /**
339 * Build a Delete command on all primitives that have not yet been deleted manually by user, or by another error fix.
340 * If all primitives have already been deleted, null is returned.
341 * @param primitives The primitives wanted for deletion
342 * @return a Delete command on all primitives that have not yet been deleted, or null otherwise
343 */
344 protected final Command deletePrimitivesIfNeeded(Collection<? extends OsmPrimitive> primitives) {
345 Collection<OsmPrimitive> primitivesToDelete = new ArrayList<>();
346 for (OsmPrimitive p : primitives) {
347 if (!p.isDeleted()) {
348 primitivesToDelete.add(p);
349 }
350 }
351 if (!primitivesToDelete.isEmpty()) {
352 return DeleteCommand.delete(primitivesToDelete);
353 } else {
354 return null;
355 }
356 }
357
358 /**
359 * Determines if the specified primitive denotes a building.
360 * @param p The primitive to be tested
361 * @return True if building key is set and different from no,entrance
362 */
363 protected static final boolean isBuilding(OsmPrimitive p) {
364 return p.hasTagDifferent("building", "no", "entrance");
365 }
366
367 /**
368 * Determines if the specified primitive denotes a residential area.
369 * @param p The primitive to be tested
370 * @return True if landuse key is equal to residential
371 */
372 protected static final boolean isResidentialArea(OsmPrimitive p) {
373 return p.hasTag("landuse", "residential");
374 }
375
376 /**
377 * Free resources.
378 */
379 public void clear() {
380 errors.clear();
381 }
382
383 protected void setShowElements(boolean b) {
384 showElementCount = b;
385 }
386}
Note: See TracBrowser for help on using the repository browser.