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