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

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

see #13036 - see #15229 - see #15182 - make Commands depends only on a DataSet, not a Layer. This removes a lot of GUI dependencies

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