source: josm/trunk/src/org/openstreetmap/josm/data/validation/ValidationTask.java@ 19000

Last change on this file since 19000 was 19000, checked in by GerdP, 3 months ago

fix #23519: Don't automatically enlarge "Validation Results" panel

  • implement new preference validator.force.unfurl.window with default value true, if set to false the window is not unfurled
  • code cleanup to remove duplicate or obsolete code
File size: 10.3 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.GraphicsEnvironment;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Set;
13import java.util.function.BiConsumer;
14import java.util.function.Consumer;
15
16import javax.swing.JOptionPane;
17
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.Way;
20import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
21import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor;
22import org.openstreetmap.josm.gui.MainApplication;
23import org.openstreetmap.josm.gui.MapFrame;
24import org.openstreetmap.josm.gui.Notification;
25import org.openstreetmap.josm.gui.PleaseWaitRunnable;
26import org.openstreetmap.josm.gui.dialogs.ValidatorDialog;
27import org.openstreetmap.josm.gui.progress.ProgressMonitor;
28import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
29import org.openstreetmap.josm.gui.util.GuiHelper;
30import org.openstreetmap.josm.tools.Utils;
31
32/**
33 * Asynchronous task for running a collection of tests against a collection of primitives
34 */
35public class ValidationTask extends PleaseWaitRunnable {
36 private final Consumer<List<TestError>> onFinish;
37 private Collection<Test> tests;
38 private final Collection<OsmPrimitive> initialPrimitives;
39 private final Collection<OsmPrimitive> formerValidatedPrimitives;
40 private final boolean beforeUpload;
41 private boolean canceled;
42 private final List<TestError> errors = new ArrayList<>();
43 private BiConsumer<ValidationTask, Test> testConsumer;
44
45 /**
46 * Constructs a new {@code ValidationTask}
47 *
48 * @param tests the tests to run
49 * @param validatedPrimitives the collection of primitives to validate.
50 * @param formerValidatedPrimitives the last collection of primitives being validated. May be null.
51 */
52 public ValidationTask(Collection<Test> tests,
53 Collection<OsmPrimitive> validatedPrimitives,
54 Collection<OsmPrimitive> formerValidatedPrimitives) {
55 this(new PleaseWaitProgressMonitor(tr("Validating")), tests, validatedPrimitives, formerValidatedPrimitives);
56 }
57
58 /**
59 * Constructs a new {@code ValidationTask}
60 *
61 * @param onFinish called when the tests are finished
62 * @param progressMonitor the progress monitor to update with test progress
63 * @param tests the tests to run
64 * @param validatedPrimitives the collection of primitives to validate.
65 * @param formerValidatedPrimitives the last collection of primitives being validated. May be null.
66 * @param beforeUpload {@code true} if this is being run prior to upload
67 * @since 18752
68 */
69 public ValidationTask(Consumer<List<TestError>> onFinish,
70 ProgressMonitor progressMonitor,
71 Collection<Test> tests,
72 Collection<OsmPrimitive> validatedPrimitives,
73 Collection<OsmPrimitive> formerValidatedPrimitives,
74 boolean beforeUpload) {
75 super(tr("Validating"),
76 progressMonitor != null ? progressMonitor : new PleaseWaitProgressMonitor(tr("Validating")),
77 false /*don't ignore exceptions */);
78 this.onFinish = onFinish;
79 this.initialPrimitives = validatedPrimitives;
80 this.formerValidatedPrimitives = formerValidatedPrimitives;
81 this.tests = tests;
82 this.beforeUpload = beforeUpload;
83 }
84
85 /**
86 * Find objects parent objects of given objects which should be checked for geometry problems
87 * or mismatches between child tags and parent tags.
88 * @param primitives the given objects
89 * @return the collection of relevant parent objects
90 */
91 private static Set<OsmPrimitive> getRelevantParents(Collection<OsmPrimitive> primitives) {
92 Set<OsmPrimitive> addedWays = new HashSet<>();
93 Set<OsmPrimitive> addedRelations = new HashSet<>();
94 for (OsmPrimitive p : primitives) {
95 for (OsmPrimitive parent : p.getReferrers()) {
96 if (parent.isDeleted())
97 continue;
98 if (parent instanceof Way)
99 addedWays.add(parent);
100 else
101 addedRelations.add(parent);
102 }
103 }
104
105 // allow to find invalid multipolygon relations caused by moved nodes
106 OsmPrimitive.getParentRelations(addedWays).stream().filter(r -> r.isMultipolygon() && !r.isDeleted())
107 .forEach(addedRelations::add);
108 HashSet<OsmPrimitive> extendedSet = new HashSet<>();
109 extendedSet.addAll(addedWays);
110 extendedSet.addAll(addedRelations);
111 return extendedSet;
112
113 }
114
115 protected ValidationTask(ProgressMonitor progressMonitor,
116 Collection<Test> tests,
117 Collection<OsmPrimitive> validatedPrimitives,
118 Collection<OsmPrimitive> formerValidatedPrimitives) {
119 this(null, progressMonitor, tests, validatedPrimitives, formerValidatedPrimitives, false);
120 }
121
122 @Override
123 protected void cancel() {
124 this.canceled = true;
125 }
126
127 @Override
128 protected void finish() {
129 if (canceled) return;
130
131 // Remove any low severity issues if they are not desired.
132 if (!(Boolean.TRUE.equals(ValidatorPrefHelper.PREF_OTHER.get()) &&
133 (!this.beforeUpload || Boolean.TRUE.equals(ValidatorPrefHelper.PREF_OTHER_UPLOAD.get())))) {
134 // Use >= just in case we add additional levels.
135 this.errors.removeIf(error -> error.getSeverity().getLevel() >= Severity.OTHER.getLevel());
136 }
137
138 if (!GraphicsEnvironment.isHeadless() && MainApplication.getMap() != null) {
139 MapFrame map = MainApplication.getMap();
140 // update GUI on Swing EDT
141 GuiHelper.runInEDT(() -> {
142 // see #23440 why this is inside the EDT
143 if (!map.validatorDialog.isShowing() && errors.isEmpty() && beforeUpload)
144 return;
145 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_UNFURL.get()))
146 map.validatorDialog.unfurlDialog();
147 map.validatorDialog.tree.setErrors(errors);
148 //FIXME: nicer way to find / invalidate the corresponding error layer
149 ValidatorDialog.invalidateValidatorLayers();
150 if (!errors.isEmpty()) {
151 OsmValidator.initializeErrorLayer();
152 }
153 });
154 }
155 if (this.onFinish != null) {
156 this.onFinish.accept(this.errors);
157 }
158 }
159
160 @Override
161 protected void realRun() {
162 if (Utils.isEmpty(tests))
163 return;
164 int testCounter = 0;
165 final boolean isPartial = this.beforeUpload || formerValidatedPrimitives != null;
166 Set<OsmPrimitive> filter = null;
167 Collection<OsmPrimitive> validatedPrimitives = initialPrimitives;
168 if (isPartial) {
169 Set<OsmPrimitive> other = Collections.emptySet();
170 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_ADD_PARENTS.get())) {
171 other = getRelevantParents(initialPrimitives);
172 }
173 HashSet<OsmPrimitive> extendedSet = new HashSet<>();
174 AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor();
175 extendedSet.addAll(v.visit(initialPrimitives));
176 extendedSet.addAll(other);
177 validatedPrimitives = extendedSet;
178 filter = new HashSet<>(initialPrimitives);
179 filter.addAll(other);
180 }
181 getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size());
182
183 for (Test test : tests) {
184 if (canceled)
185 return;
186 testCounter++;
187 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName()));
188 test.setBeforeUpload(this.beforeUpload);
189 // Pre-upload checks only run on a partial selection.
190 test.setPartialSelection(isPartial);
191 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
192 test.visit(validatedPrimitives);
193 test.endTest();
194 if (isPartial && Boolean.TRUE.equals(ValidatorPrefHelper.PREF_REMOVE_IRRELEVANT.get())) {
195 // #23397: remove errors for objects which were not in the initial list of primitives
196 test.removeIrrelevantErrors(filter);
197 }
198
199 errors.addAll(test.getErrors());
200 if (this.testConsumer != null) {
201 this.testConsumer.accept(this, test);
202 }
203 test.clear();
204 test.setBeforeUpload(false);
205 }
206 tests = null;
207 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) {
208 getProgressMonitor().setCustomText("");
209 getProgressMonitor().subTask(tr("Updating ignored errors ..."));
210 for (TestError error : errors) {
211 if (canceled) return;
212 error.updateIgnored();
213 }
214 }
215
216 if (errors.stream().anyMatch(e -> e.getPrimitives().stream().anyMatch(OsmPrimitive::isDisabledAndHidden))) {
217 final String msg = "<b>" + tr("Validation results contain elements hidden by a filter.") + "</b><br/>"
218 + tr("Please review active filters to see the hidden results.");
219 GuiHelper.runInEDT(() -> new Notification(msg)
220 .setDuration(Notification.TIME_LONG)
221 .setIcon(JOptionPane.WARNING_MESSAGE)
222 .setHelpTopic("Dialog/Validator")
223 .show());
224 }
225 }
226
227 /**
228 * Gets the validation errors accumulated until this moment.
229 * @return The list of errors
230 */
231 public List<TestError> getErrors() {
232 return errors;
233 }
234
235 /**
236 * A test consumer to avoid filling up memory. A test consumer <i>may</i> remove tests it has consumed.
237 * @param testConsumer The consumer which takes a {@link ValidationTask} ({@code this}) and the test that finished.
238 * @since 18752
239 */
240 public void setTestConsumer(BiConsumer<ValidationTask, Test> testConsumer) {
241 this.testConsumer = testConsumer;
242 }
243}
Note: See TracBrowser for help on using the repository browser.