001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.event.ActionEvent; 007import java.awt.event.KeyEvent; 008import java.io.IOException; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.List; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.osm.OsmPrimitive; 015import org.openstreetmap.josm.data.validation.OsmValidator; 016import org.openstreetmap.josm.data.validation.Test; 017import org.openstreetmap.josm.data.validation.TestError; 018import org.openstreetmap.josm.data.validation.util.AggregatePrimitivesVisitor; 019import org.openstreetmap.josm.gui.PleaseWaitRunnable; 020import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference; 021import org.openstreetmap.josm.gui.util.GuiHelper; 022import org.openstreetmap.josm.io.OsmTransferException; 023import org.openstreetmap.josm.tools.Shortcut; 024import org.xml.sax.SAXException; 025 026/** 027 * The action that does the validate thing. 028 * <p> 029 * This action iterates through all active tests and give them the data, so that 030 * each one can test it. 031 * 032 * @author frsantos 033 */ 034public class ValidateAction extends JosmAction { 035 036 /** Last selection used to validate */ 037 private transient Collection<OsmPrimitive> lastSelection; 038 039 /** 040 * Constructor 041 */ 042 public ValidateAction() { 043 super(tr("Validation"), "dialogs/validator", tr("Performs the data validation"), 044 Shortcut.registerShortcut("tools:validate", tr("Tool: {0}", tr("Validation")), 045 KeyEvent.VK_V, Shortcut.SHIFT), true); 046 } 047 048 @Override 049 public void actionPerformed(ActionEvent ev) { 050 doValidate(true); 051 } 052 053 /** 054 * Does the validation. 055 * <p> 056 * If getSelectedItems is true, the selected items (or all items, if no one 057 * is selected) are validated. If it is false, last selected items are 058 * revalidated 059 * 060 * @param getSelectedItems If selected or last selected items must be validated 061 */ 062 public void doValidate(boolean getSelectedItems) { 063 if (Main.map == null || !Main.map.isVisible()) 064 return; 065 066 OsmValidator.initializeTests(); 067 OsmValidator.initializeErrorLayer(); 068 069 Collection<Test> tests = OsmValidator.getEnabledTests(false); 070 if (tests.isEmpty()) 071 return; 072 073 Collection<OsmPrimitive> selection; 074 if (getSelectedItems) { 075 selection = Main.main.getCurrentDataSet().getAllSelected(); 076 if (selection.isEmpty()) { 077 selection = Main.main.getCurrentDataSet().allNonDeletedPrimitives(); 078 lastSelection = null; 079 } else { 080 AggregatePrimitivesVisitor v = new AggregatePrimitivesVisitor(); 081 selection = v.visit(selection); 082 lastSelection = selection; 083 } 084 } else { 085 if (lastSelection == null) { 086 selection = Main.main.getCurrentDataSet().allNonDeletedPrimitives(); 087 } else { 088 selection = lastSelection; 089 } 090 } 091 092 ValidationTask task = new ValidationTask(tests, selection, lastSelection); 093 Main.worker.submit(task); 094 } 095 096 @Override 097 public void updateEnabledState() { 098 setEnabled(getEditLayer() != null); 099 } 100 101 @Override 102 public void destroy() { 103 // Hack - this action should stay forever because it could be added to toolbar 104 // Do not call super.destroy() here 105 } 106 107 /** 108 * Asynchronous task for running a collection of tests against a collection 109 * of primitives 110 * 111 */ 112 static class ValidationTask extends PleaseWaitRunnable { 113 private Collection<Test> tests; 114 private final Collection<OsmPrimitive> validatedPrimitives; 115 private final Collection<OsmPrimitive> formerValidatedPrimitives; 116 private boolean canceled; 117 private List<TestError> errors; 118 119 /** 120 * 121 * @param tests the tests to run 122 * @param validatedPrimitives the collection of primitives to validate. 123 * @param formerValidatedPrimitives the last collection of primitives being validates. May be null. 124 */ 125 ValidationTask(Collection<Test> tests, Collection<OsmPrimitive> validatedPrimitives, 126 Collection<OsmPrimitive> formerValidatedPrimitives) { 127 super(tr("Validating"), false /*don't ignore exceptions */); 128 this.validatedPrimitives = validatedPrimitives; 129 this.formerValidatedPrimitives = formerValidatedPrimitives; 130 this.tests = tests; 131 } 132 133 @Override 134 protected void cancel() { 135 this.canceled = true; 136 } 137 138 @Override 139 protected void finish() { 140 if (canceled) return; 141 142 // update GUI on Swing EDT 143 // 144 GuiHelper.runInEDT(new Runnable() { 145 @Override 146 public void run() { 147 Main.map.validatorDialog.tree.setErrors(errors); 148 Main.map.validatorDialog.unfurlDialog(); 149 Main.main.getCurrentDataSet().fireSelectionChanged(); 150 } 151 }); 152 } 153 154 @Override 155 protected void realRun() throws SAXException, IOException, 156 OsmTransferException { 157 if (tests == null || tests.isEmpty()) 158 return; 159 errors = new ArrayList<>(200); 160 getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size()); 161 int testCounter = 0; 162 for (Test test : tests) { 163 if (canceled) 164 return; 165 testCounter++; 166 getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(), test.getName())); 167 test.setPartialSelection(formerValidatedPrimitives != null); 168 test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false)); 169 test.visit(validatedPrimitives); 170 test.endTest(); 171 errors.addAll(test.getErrors()); 172 } 173 tests = null; 174 if (Main.pref.getBoolean(ValidatorPreference.PREF_USE_IGNORE, true)) { 175 getProgressMonitor().subTask(tr("Updating ignored errors ...")); 176 for (TestError error : errors) { 177 if (canceled) return; 178 List<String> s = new ArrayList<>(); 179 s.add(error.getIgnoreState()); 180 s.add(error.getIgnoreGroup()); 181 s.add(error.getIgnoreSubGroup()); 182 for (String state : s) { 183 if (state != null && OsmValidator.hasIgnoredError(state)) { 184 error.setIgnored(true); 185 } 186 } 187 } 188 } 189 } 190 } 191}