001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.validation; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.List; 009 010import javax.swing.JOptionPane; 011 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 014import org.openstreetmap.josm.gui.MainApplication; 015import org.openstreetmap.josm.gui.MapFrame; 016import org.openstreetmap.josm.gui.Notification; 017import org.openstreetmap.josm.gui.PleaseWaitRunnable; 018import org.openstreetmap.josm.gui.layer.ValidatorLayer; 019import org.openstreetmap.josm.gui.progress.ProgressMonitor; 020import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 021import org.openstreetmap.josm.gui.util.GuiHelper; 022import org.openstreetmap.josm.tools.Utils; 023 024/** 025 * Asynchronous task for running a collection of tests against a collection of primitives 026 */ 027public class ValidationTask extends PleaseWaitRunnable { 028 private Collection<Test> tests; 029 private final Collection<OsmPrimitive> validatedPrimitives; 030 private final Collection<OsmPrimitive> formerValidatedPrimitives; 031 private boolean canceled; 032 private List<TestError> errors; 033 034 /** 035 * Constructs a new {@code ValidationTask} 036 * 037 * @param tests the tests to run 038 * @param validatedPrimitives the collection of primitives to validate. 039 * @param formerValidatedPrimitives the last collection of primitives being validates. May be null. 040 */ 041 public ValidationTask(Collection<Test> tests, 042 Collection<OsmPrimitive> validatedPrimitives, 043 Collection<OsmPrimitive> formerValidatedPrimitives) { 044 this(new PleaseWaitProgressMonitor(tr("Validating")), tests, validatedPrimitives, formerValidatedPrimitives); 045 } 046 047 protected ValidationTask(ProgressMonitor progressMonitor, 048 Collection<Test> tests, 049 Collection<OsmPrimitive> validatedPrimitives, 050 Collection<OsmPrimitive> formerValidatedPrimitives) { 051 super(tr("Validating"), progressMonitor, false /*don't ignore exceptions */); 052 this.validatedPrimitives = validatedPrimitives; 053 this.formerValidatedPrimitives = formerValidatedPrimitives; 054 this.tests = tests; 055 } 056 057 @Override 058 protected void cancel() { 059 this.canceled = true; 060 } 061 062 @Override 063 protected void finish() { 064 if (canceled) return; 065 066 // update GUI on Swing EDT 067 GuiHelper.runInEDT(() -> { 068 MapFrame map = MainApplication.getMap(); 069 map.validatorDialog.unfurlDialog(); 070 map.validatorDialog.tree.setErrors(errors); 071 //FIXME: nicer way to find / invalidate the corresponding error layer 072 MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate); 073 if (!errors.isEmpty()) { 074 OsmValidator.initializeErrorLayer(); 075 } 076 }); 077 } 078 079 @Override 080 protected void realRun() { 081 if (Utils.isEmpty(tests)) 082 return; 083 errors = new ArrayList<>(); 084 getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size()); 085 int testCounter = 0; 086 for (Test test : tests) { 087 if (canceled) 088 return; 089 testCounter++; 090 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName())); 091 test.setBeforeUpload(false); 092 test.setPartialSelection(formerValidatedPrimitives != null); 093 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false)); 094 test.visit(validatedPrimitives); 095 test.endTest(); 096 errors.addAll(test.getErrors()); 097 test.clear(); 098 } 099 tests = null; 100 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) { 101 getProgressMonitor().setCustomText(""); 102 getProgressMonitor().subTask(tr("Updating ignored errors ...")); 103 for (TestError error : errors) { 104 if (canceled) return; 105 error.updateIgnored(); 106 } 107 } 108 109 if (errors.stream().anyMatch(e -> e.getPrimitives().stream().anyMatch(OsmPrimitive::isDisabledAndHidden))) { 110 final String msg = "<b>" + tr("Validation results contain elements hidden by a filter.") + "</b><br/>" 111 + tr("Please review active filters to see the hidden results."); 112 GuiHelper.runInEDT(() -> new Notification(msg) 113 .setDuration(Notification.TIME_LONG) 114 .setIcon(JOptionPane.WARNING_MESSAGE) 115 .setHelpTopic("Dialog/Validator") 116 .show()); 117 } 118 } 119 120 /** 121 * Gets the validation errors accumulated until this moment. 122 * @return The list of errors 123 */ 124 public List<TestError> getErrors() { 125 return errors; 126 } 127}