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

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

see #15182 - move SearchCompiler from actions.search to data.osm.search

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