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, 3 months 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
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.progress.NullProgressMonitor;
26import org.openstreetmap.josm.gui.progress.ProgressMonitor;
27import org.openstreetmap.josm.tools.GBC;
28import org.openstreetmap.josm.tools.Logging;
29import org.openstreetmap.josm.tools.Utils;
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 */
40public class Test extends AbstractVisitor implements Comparable<Test> {
41
42    protected static final Predicate<OsmPrimitive> IN_DOWNLOADED_AREA = new NotOutsideDataSourceArea();
43
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 */
66    protected List<TestError> errors = new ArrayList<>(30);
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;
73
74    /** the start time to compute elapsed time when test finishes */
75    protected long startTime;
76
77    /**
78     * Constructor
79     * @param name Name of the test
80     * @param description Description of the test
81     */
82    public Test(String name, String description) {
83        this.name = name;
84        this.description = description;
85    }
86
87    /**
88     * Constructor
89     * @param name Name of the test
90     */
91    public Test(String name) {
92        this(name, null);
93    }
94
95    /**
96     * A test that forwards all primitives to {@link #check(OsmPrimitive)}.
97     */
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         */
104        public TagTest(String name, String description) {
105            super(name, description);
106        }
107
108        /**
109         * Constructs a new {@code TagTest} with given name.
110         * @param name The test name
111         */
112        public TagTest(String name) {
113            super(name);
114        }
115
116        /**
117         * Checks the tags of the given primitive.
118         * @param p The primitive to test
119         */
120        public abstract void check(OsmPrimitive p);
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    /**
139     * Initializes any global data used this tester.
140     * @throws Exception When cannot initialize the test
141     */
142    public void initialize() throws Exception {
143        this.startTime = -1;
144    }
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) {
152        this.progressMonitor = Optional.ofNullable(progressMonitor).orElse(NullProgressMonitor.INSTANCE);
153        String startMessage = tr("Running test {0}", name);
154        this.progressMonitor.beginTask(startMessage);
155        Logging.debug(startMessage);
156        this.errors = new ArrayList<>(30);
157        this.startTime = System.currentTimeMillis();
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     */
164    public void setPartialSelection(boolean partialSelection) {
165        this.partialSelection = partialSelection;
166    }
167
168    /**
169     * Gets the validation errors accumulated until this moment.
170     * @return The list of errors
171     */
172    public List<TestError> getErrors() {
173        return errors;
174    }
175
176    /**
177     * Notification of the end of the test. The tester may perform additional
178     * actions and destroy the used structures.
179     * <p>
180     * If you override this method, don't forget to cleanup {@code progressMonitor}
181     * (most overrides call {@code super.endTest()} to do this).
182     */
183    public void endTest() {
184        progressMonitor.finishTask();
185        progressMonitor = null;
186        if (startTime > 0) {
187            // fix #11567 where elapsedTime is < 0
188            long elapsedTime = Math.max(0, System.currentTimeMillis() - startTime);
189            Logging.debug(tr("Test ''{0}'' completed in {1}", getName(), Utils.getDurationString(elapsedTime)));
190        }
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     */
199    public void visit(Collection<OsmPrimitive> selection) {
200        if (progressMonitor != null) {
201            progressMonitor.setTicksCount(selection.size());
202        }
203        for (OsmPrimitive p : selection) {
204            if (isCanceled()) {
205                break;
206            }
207            if (isPrimitiveUsable(p)) {
208                p.accept(this);
209            }
210            if (progressMonitor != null) {
211                progressMonitor.worked(1);
212            }
213        }
214    }
215
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     */
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
225    @Override
226    public void visit(Node n) {
227        // To be overridden in subclasses
228    }
229
230    @Override
231    public void visit(Way w) {
232        // To be overridden in subclasses
233    }
234
235    @Override
236    public void visit(Relation r) {
237        // To be overridden in subclasses
238    }
239
240    /**
241     * Allow the tester to manage its own preferences
242     * @param testPanel The panel to add any preferences component
243     */
244    public void addGui(JPanel testPanel) {
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
258     * @return {@code true} if restart is required, {@code false} otherwise
259     */
260    public boolean ok() {
261        enabled = checkEnabled.isSelected();
262        testBeforeUpload = checkBeforeUpload.isSelected();
263        return false;
264    }
265
266    /**
267     * Fixes the error with the appropriate command
268     *
269     * @param testError error to fix
270     * @return The command to fix the error
271     */
272    public Command fixError(TestError testError) {
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     */
282    public boolean isFixable(TestError testError) {
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     */
290    public boolean testBeforeUpload() {
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     */
298    public void setBeforeUpload(boolean isUpload) {
299        this.isBeforeUpload = isUpload;
300    }
301
302    /**
303     * Returns the test name.
304     * @return The test name
305     */
306    public String getName() {
307        return name;
308    }
309
310    /**
311     * Determines if the test has been canceled.
312     * @return {@code true} if the test has been canceled, {@code false} otherwise
313     */
314    public boolean isCanceled() {
315        return progressMonitor != null ? progressMonitor.isCanceled() : false;
316    }
317
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) {
325        Collection<OsmPrimitive> primitivesToDelete = new ArrayList<>();
326        for (OsmPrimitive p : primitives) {
327            if (!p.isDeleted()) {
328                primitivesToDelete.add(p);
329            }
330        }
331        if (!primitivesToDelete.isEmpty()) {
332            return DeleteCommand.delete(primitivesToDelete);
333        } else {
334            return null;
335        }
336    }
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) {
344        return p.hasTagDifferent("building", "no", "entrance");
345    }
346
347    @Override
348    public int hashCode() {
349        return Objects.hash(name, description);
350    }
351
352    @Override
353    public boolean equals(Object obj) {
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) &&
358               Objects.equals(description, test.description);
359    }
360
361    @Override
362    public int compareTo(Test t) {
363        return name.compareTo(t.name);
364    }
365}
Note: See TracBrowser for help on using the repository browser.