source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java

Last change on this file was 19106, checked in by taylor.smock, 2 weeks ago

Cleanup some new PMD warnings from PMD 7.x (followup of r19101)

  • Property svn:eol-style set to native
File size: 27.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.awt.event.MouseEvent;
9import java.io.IOException;
10import java.lang.reflect.InvocationTargetException;
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Enumeration;
14import java.util.HashSet;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Set;
18import java.util.concurrent.atomic.AtomicBoolean;
19
20import javax.swing.AbstractAction;
21import javax.swing.Action;
22import javax.swing.JComponent;
23import javax.swing.JOptionPane;
24import javax.swing.JPopupMenu;
25import javax.swing.SwingUtilities;
26import javax.swing.event.TreeSelectionEvent;
27import javax.swing.event.TreeSelectionListener;
28import javax.swing.tree.DefaultMutableTreeNode;
29import javax.swing.tree.TreePath;
30
31import org.openstreetmap.josm.actions.AbstractSelectAction;
32import org.openstreetmap.josm.actions.AutoScaleAction;
33import org.openstreetmap.josm.actions.JosmAction;
34import org.openstreetmap.josm.actions.ValidateAction;
35import org.openstreetmap.josm.actions.relation.EditRelationAction;
36import org.openstreetmap.josm.command.Command;
37import org.openstreetmap.josm.command.SequenceCommand;
38import org.openstreetmap.josm.data.UndoRedoHandler;
39import org.openstreetmap.josm.data.osm.DataSelectionListener;
40import org.openstreetmap.josm.data.osm.DataSet;
41import org.openstreetmap.josm.data.osm.Node;
42import org.openstreetmap.josm.data.osm.OsmPrimitive;
43import org.openstreetmap.josm.data.osm.WaySegment;
44import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
45import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter;
46import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
47import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
48import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
49import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
50import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
51import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper;
52import org.openstreetmap.josm.data.validation.OsmValidator;
53import org.openstreetmap.josm.data.validation.Severity;
54import org.openstreetmap.josm.data.validation.TestError;
55import org.openstreetmap.josm.data.validation.ValidatorVisitor;
56import org.openstreetmap.josm.gui.MainApplication;
57import org.openstreetmap.josm.gui.PleaseWaitRunnable;
58import org.openstreetmap.josm.gui.PopupMenuHandler;
59import org.openstreetmap.josm.gui.SideButton;
60import org.openstreetmap.josm.gui.dialogs.validator.ValidatorTreePanel;
61import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
62import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
63import org.openstreetmap.josm.gui.layer.OsmDataLayer;
64import org.openstreetmap.josm.gui.layer.ValidatorLayer;
65import org.openstreetmap.josm.gui.preferences.validator.ValidatorPreference;
66import org.openstreetmap.josm.gui.progress.ProgressMonitor;
67import org.openstreetmap.josm.gui.util.GuiHelper;
68import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
69import org.openstreetmap.josm.io.OsmTransferException;
70import org.openstreetmap.josm.spi.preferences.Config;
71import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
72import org.openstreetmap.josm.tools.ImageProvider;
73import org.openstreetmap.josm.tools.InputMapUtils;
74import org.openstreetmap.josm.tools.JosmRuntimeException;
75import org.openstreetmap.josm.tools.Pair;
76import org.openstreetmap.josm.tools.Shortcut;
77import org.xml.sax.SAXException;
78
79/**
80 * A small tool dialog for displaying the current errors. The selection manager
81 * respects clicks into the selection list. Ctrl-click will remove entries from
82 * the list while single click will make the clicked entry the only selection.
83 *
84 * @author frsantos
85 */
86public class ValidatorDialog extends ToggleDialog
87 implements DataSelectionListener, ActiveLayerChangeListener, DataSetListenerAdapter.Listener {
88
89 /** The display tree */
90 public final ValidatorTreePanel tree;
91
92 /** The validate action */
93 public static final ValidateAction validateAction = new ValidateAction();
94
95 /** The fix action */
96 private final transient Action fixAction;
97 /** The ignore action */
98 private final transient Action ignoreAction;
99 /** The ignore-list management action */
100 private final transient Action ignorelistManagementAction;
101 /** The select action */
102 private final transient Action selectAction;
103 /** The lookup action */
104 private final transient LookupAction lookupAction;
105 private final transient JosmAction ignoreForNowAction;
106
107 private final JPopupMenu popupMenu = new JPopupMenu();
108 private final transient PopupMenuHandler popupMenuHandler = new PopupMenuHandler(popupMenu);
109 private final transient DataSetListenerAdapter dataChangedAdapter = new DataSetListenerAdapter(this);
110
111 /** Last selected element */
112 private DefaultMutableTreeNode lastSelectedNode;
113
114 /**
115 * Constructor
116 */
117 public ValidatorDialog() {
118 super(tr("Validation Results"), "validator", tr("Open the validation window."),
119 Shortcut.registerShortcut("subwindow:validator", tr("Windows: {0}", tr("Validation Results")),
120 KeyEvent.VK_V, Shortcut.ALT_SHIFT), 150, false, ValidatorPreference.class);
121
122 tree = new ValidatorTreePanel();
123 tree.addMouseListener(new MouseEventHandler());
124 addTreeSelectionListener(new SelectionWatch());
125 InputMapUtils.unassignCtrlShiftUpDown(tree, JComponent.WHEN_FOCUSED);
126
127 ignoreForNowAction = new JosmAction(tr("Ignore for now"), "dialogs/delete",
128 tr("Ignore and remove from tree."), Shortcut.registerShortcut("tools:validate:ignore-for-now",
129 tr("Ignore and remove from tree."), KeyEvent.VK_MINUS, Shortcut.SHIFT),
130 false, false) {
131
132 @Override
133 public void actionPerformed(ActionEvent e) {
134 TestError error = getSelectedError();
135 if (error != null) {
136 error.setIgnored(true);
137 tree.resetErrors();
138 invalidateValidatorLayers();
139 }
140 }
141 };
142
143 popupMenuHandler.addAction(MainApplication.getMenu().autoScaleActions.get(AutoScaleAction.AutoScaleMode.PROBLEM));
144 popupMenuHandler.addAction(new EditRelationAction());
145 popupMenuHandler.addAction(ignoreForNowAction);
146
147 List<SideButton> buttons = new LinkedList<>();
148
149 selectAction = new AbstractSelectAction() {
150 @Override
151 public void actionPerformed(ActionEvent e) {
152 setSelectedItems();
153 }
154 };
155 selectAction.setEnabled(false);
156 InputMapUtils.addEnterAction(tree, selectAction);
157 buttons.add(new SideButton(selectAction));
158
159 lookupAction = new LookupAction();
160 buttons.add(new SideButton(lookupAction));
161
162 buttons.add(new SideButton(validateAction));
163
164 fixAction = new AbstractAction() {
165 {
166 putValue(NAME, tr("Fix"));
167 putValue(SHORT_DESCRIPTION, tr("Fix the selected issue."));
168 new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true);
169 }
170 @Override
171 public void actionPerformed(ActionEvent e) {
172 fixErrors();
173 }
174 };
175 fixAction.setEnabled(false);
176 buttons.add(new SideButton(fixAction));
177
178 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) {
179 ignoreAction = new AbstractAction() {
180 {
181 putValue(NAME, tr("Ignore"));
182 putValue(SHORT_DESCRIPTION, tr("Ignore the selected issue next time."));
183 new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true);
184 }
185 @Override
186 public void actionPerformed(ActionEvent e) {
187 ignoreErrors();
188 }
189 };
190 ignoreAction.setEnabled(false);
191 buttons.add(new SideButton(ignoreAction));
192
193 ignorelistManagementAction = new IgnorelistManagementAction();
194 buttons.add(new SideButton(ignorelistManagementAction));
195 } else {
196 ignoreAction = null;
197 ignorelistManagementAction = null;
198 }
199
200 createLayout(tree, true, buttons);
201 }
202
203 /**
204 * The action to manage the ignore list.
205 */
206 static class IgnorelistManagementAction extends AbstractAction {
207 IgnorelistManagementAction() {
208 putValue(NAME, tr("Manage Ignore"));
209 putValue(SHORT_DESCRIPTION, tr("Manage the ignore list"));
210 new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true);
211 }
212
213 @Override
214 public void actionPerformed(ActionEvent e) {
215 new ValidatorListManagementDialog("Ignore");
216 }
217 }
218
219 /**
220 * The action to lookup the selection in the error tree.
221 */
222 class LookupAction extends AbstractAction {
223 LookupAction() {
224 putValue(NAME, tr("Lookup"));
225 putValue(SHORT_DESCRIPTION, tr("Looks up the selected primitives in the error list."));
226 new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true);
227 updateEnabledState();
228 }
229
230 @Override
231 public void actionPerformed(ActionEvent e) {
232 final DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
233 if (ds == null) {
234 return;
235 }
236 tree.selectRelatedErrors(ds.getSelected());
237 }
238
239 void updateEnabledState() {
240 final DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
241 if (ds == null || ds.selectionEmpty()) {
242 setEnabled(false);
243 } else {
244 boolean found = tree.getErrors().stream()
245 .anyMatch(e -> e.getPrimitives().stream().anyMatch(OsmPrimitive::isSelected));
246 setEnabled(found);
247 }
248 }
249 }
250
251 @Override
252 public void showNotify() {
253 DatasetEventManager.getInstance().addDatasetListener(dataChangedAdapter, FireMode.IN_EDT_CONSOLIDATED);
254 SelectionEventManager.getInstance().addSelectionListener(this);
255 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
256 if (ds != null) {
257 updateSelection(ds.getAllSelected());
258 }
259 MainApplication.getLayerManager().addAndFireActiveLayerChangeListener(this);
260
261 }
262
263 @Override
264 public void hideNotify() {
265 DatasetEventManager.getInstance().removeDatasetListener(dataChangedAdapter);
266 MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
267 SelectionEventManager.getInstance().removeSelectionListener(this);
268 }
269
270 @Override
271 public void setVisible(boolean v) {
272 if (tree != null) {
273 tree.setVisible(v);
274 }
275 super.setVisible(v);
276 }
277
278 /**
279 * Fix selected errors
280 */
281 private void fixErrors() {
282 TreePath[] selectionPaths = tree.getSelectionPaths();
283 if (selectionPaths == null)
284 return;
285
286 Set<DefaultMutableTreeNode> processedNodes = new HashSet<>();
287
288 List<TestError> errorsToFix = new LinkedList<>();
289 for (TreePath path : selectionPaths) {
290 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
291 if (node != null) {
292 ValidatorTreePanel.visitTestErrors(node, errorsToFix::add, processedNodes);
293 }
294 }
295
296 // run fix task asynchronously
297 MainApplication.worker.submit(new FixTask(errorsToFix));
298 }
299
300 /**
301 * Set selected errors to ignore state
302 */
303 private void ignoreErrors() {
304 int asked = JOptionPane.DEFAULT_OPTION;
305 AtomicBoolean changed = new AtomicBoolean();
306 TreePath[] selectionPaths = tree.getSelectionPaths();
307 if (selectionPaths == null)
308 return;
309
310 Set<DefaultMutableTreeNode> processedNodes = new HashSet<>();
311 for (TreePath path : selectionPaths) {
312 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
313 if (node == null) {
314 continue;
315 }
316
317 Object mainNodeInfo = node.getUserObject();
318 final int depth = node.getDepth();
319 if (!(mainNodeInfo instanceof TestError)) {
320 Set<Pair<String, String>> state = new HashSet<>();
321 // ask if the whole set should be ignored
322 if (asked == JOptionPane.DEFAULT_OPTION) {
323 String[] a = {tr("Whole group"), tr("Single elements"), tr("Nothing")};
324 asked = JOptionPane.showOptionDialog(MainApplication.getMainFrame(), tr("Ignore whole group or individual elements?"),
325 tr("Ignoring elements"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
326 a, a[1]);
327 }
328 if (asked == JOptionPane.YES_NO_OPTION) {
329 ValidatorTreePanel.visitTestErrors(node, err -> {
330 err.setIgnored(true);
331 changed.set(true);
332 state.add(new Pair<>(depth == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup(), err.getMessage()));
333 }, processedNodes);
334 for (Pair<String, String> s : state) {
335 OsmValidator.addIgnoredError(s.a, s.b);
336 }
337 continue;
338 } else if (asked == JOptionPane.CANCEL_OPTION || asked == JOptionPane.CLOSED_OPTION) {
339 continue;
340 }
341 }
342
343 ValidatorTreePanel.visitTestErrors(node, error -> {
344 String state = error.getIgnoreState();
345 if (state != null) {
346 OsmValidator.addIgnoredError(state, error.getMessage());
347 }
348 changed.set(true);
349 error.setIgnored(true);
350 }, processedNodes);
351 }
352 if (changed.get()) {
353 tree.resetErrors();
354 OsmValidator.saveIgnoredErrors();
355 invalidateValidatorLayers();
356 }
357 }
358
359 /**
360 * Sets the selection of the map to the current selected items.
361 */
362 private void setSelectedItems() {
363 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
364 if (tree == null || ds == null)
365 return;
366
367 TreePath[] selectedPaths = tree.getSelectionPaths();
368 if (selectedPaths == null)
369 return;
370
371 Collection<OsmPrimitive> sel = new HashSet<>(40);
372 for (TreePath path : selectedPaths) {
373 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
374 Enumeration<?> children = node.breadthFirstEnumeration();
375 while (children.hasMoreElements()) {
376 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement();
377 Object nodeInfo = childNode.getUserObject();
378 if (nodeInfo instanceof TestError) {
379 TestError error = (TestError) nodeInfo;
380 error.getPrimitives().stream()
381 .filter(OsmPrimitive::isSelectable)
382 .forEach(sel::add);
383 }
384 }
385 }
386 ds.setSelected(sel);
387 }
388
389 /**
390 * Checks for fixes in selected element and, if needed, adds to the sel
391 * parameter all selected elements
392 *
393 * @param sel
394 * The collection where to add all selected elements
395 * @param addSelected
396 * if true, add all selected elements to collection
397 * @return whether the selected elements has any fix
398 */
399 private boolean setSelection(Collection<OsmPrimitive> sel, boolean addSelected) {
400 AtomicBoolean hasFixes = new AtomicBoolean();
401
402 DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
403 if (lastSelectedNode != null && !lastSelectedNode.equals(node)) {
404 ValidatorTreePanel.visitTestErrors(lastSelectedNode, error -> error.setSelected(false));
405 }
406
407 lastSelectedNode = node;
408 if (node != null) {
409 ValidatorTreePanel.visitTestErrors(node, error -> {
410 error.setSelected(true);
411
412 hasFixes.set(hasFixes.get() || error.isFixable());
413 if (addSelected) {
414 error.getPrimitives().stream()
415 .filter(OsmPrimitive::isSelectable)
416 .forEach(sel::add);
417 }
418 });
419 selectAction.setEnabled(true);
420 if (ignoreAction != null) {
421 ignoreAction.setEnabled(!(node.getUserObject() instanceof Severity));
422 }
423 }
424
425 return hasFixes.get();
426 }
427
428 @Override
429 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
430 OsmDataLayer editLayer = e.getSource().getEditLayer();
431 if (editLayer == null) {
432 tree.setErrorList(new ArrayList<>());
433 } else {
434 tree.setErrorList(editLayer.validationErrors);
435 }
436 }
437
438 /**
439 * Add a tree selection listener to the validator tree.
440 * @param listener the TreeSelectionListener
441 * @since 5958
442 */
443 public void addTreeSelectionListener(TreeSelectionListener listener) {
444 tree.addTreeSelectionListener(listener);
445 }
446
447 /**
448 * Remove the given tree selection listener from the validator tree.
449 * @param listener the TreeSelectionListener
450 * @since 5958
451 */
452 public void removeTreeSelectionListener(TreeSelectionListener listener) {
453 tree.removeTreeSelectionListener(listener);
454 }
455
456 /**
457 * Replies the popup menu handler.
458 * @return The popup menu handler
459 * @since 5958
460 */
461 public PopupMenuHandler getPopupMenuHandler() {
462 return popupMenuHandler;
463 }
464
465 /**
466 * Replies the currently selected error, or {@code null}.
467 * @return The selected error, if any.
468 * @since 5958
469 */
470 public TestError getSelectedError() {
471 Object comp = tree.getLastSelectedPathComponent();
472 if (comp instanceof DefaultMutableTreeNode) {
473 Object object = ((DefaultMutableTreeNode) comp).getUserObject();
474 if (object instanceof TestError) {
475 return (TestError) object;
476 }
477 }
478 return null;
479 }
480
481 /**
482 * Watches for double clicks and launches the popup menu.
483 */
484 class MouseEventHandler extends PopupMenuLauncher {
485
486 MouseEventHandler() {
487 super(popupMenu);
488 }
489
490 @Override
491 public void mouseClicked(MouseEvent e) {
492 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
493 if (selPath == null) {
494 tree.clearSelection();
495 }
496
497 fixAction.setEnabled(false);
498 if (ignoreAction != null) {
499 ignoreAction.setEnabled(false);
500 }
501 selectAction.setEnabled(false);
502
503 boolean isDblClick = isDoubleClick(e);
504
505 Collection<OsmPrimitive> sel = isDblClick ? new HashSet<>(40) : null;
506
507 boolean hasFixes = setSelection(sel, isDblClick);
508 fixAction.setEnabled(hasFixes);
509
510 if (isDblClick) {
511 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
512 if (ds != null) {
513 ds.setSelected(sel);
514 }
515 if (Config.getPref().getBoolean("validator.autozoom", false)) {
516 AutoScaleAction.zoomTo(sel);
517 }
518 }
519 }
520
521 @Override
522 public void launch(MouseEvent e) {
523 TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
524 if (selPath == null)
525 return;
526 DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getPathComponent(selPath.getPathCount() - 1);
527 if (!(node.getUserObject() instanceof TestError))
528 return;
529 super.launch(e);
530 }
531 }
532
533 /**
534 * Watches for tree selection.
535 */
536 public class SelectionWatch implements TreeSelectionListener {
537 @Override
538 public void valueChanged(TreeSelectionEvent e) {
539 if (ignoreAction != null) {
540 ignoreAction.setEnabled(false);
541 }
542 selectAction.setEnabled(false);
543
544 Collection<OsmPrimitive> sel = new HashSet<>();
545 boolean hasFixes = setSelection(sel, true);
546 fixAction.setEnabled(hasFixes);
547 popupMenuHandler.setPrimitives(sel);
548 invalidateValidatorLayers();
549 }
550 }
551
552 /**
553 * A visitor that is used to compute the bounds of an error.
554 */
555 public static class ValidatorBoundingXYVisitor extends BoundingXYVisitor implements ValidatorVisitor {
556 @Override
557 public void visit(OsmPrimitive p) {
558 if (p.isUsable()) {
559 p.accept((PrimitiveVisitor) this);
560 }
561 }
562
563 @Override
564 public void visit(WaySegment ws) {
565 if (ws.getLowerIndex() < 0 || ws.getUpperIndex() >= ws.getWay().getNodesCount())
566 return;
567 visit(ws.getFirstNode());
568 visit(ws.getSecondNode());
569 }
570
571 @Override
572 public void visit(List<Node> nodes) {
573 for (Node n: nodes) {
574 visit(n);
575 }
576 }
577
578 @Override
579 public void visit(TestError error) {
580 if (error != null) {
581 error.visitHighlighted(this);
582 }
583 }
584 }
585
586 /**
587 * Called when the selection was changed to update the list of displayed errors
588 * @param newSelection The new selection
589 */
590 public void updateSelection(Collection<? extends OsmPrimitive> newSelection) {
591 if (!Config.getPref().getBoolean(ValidatorPrefHelper.PREF_FILTER_BY_SELECTION, false)) {
592 if (tree.getFilter() != null)
593 tree.setFilter(null);
594 return;
595 }
596
597 if (newSelection.isEmpty())
598 tree.setFilter(null);
599 else
600 tree.setFilter(new HashSet<>(newSelection));
601 }
602
603 @Override
604 public void selectionChanged(SelectionChangeEvent event) {
605 updateSelection(event.getSelection());
606 lookupAction.updateEnabledState();
607 }
608
609 /**
610 * Task for fixing a collection of {@link TestError}s. Can be run asynchronously.
611 */
612 class FixTask extends PleaseWaitRunnable {
613 private final Collection<TestError> testErrors;
614 private final List<Command> fixCommands = new ArrayList<>();
615 private boolean canceled;
616
617 FixTask(Collection<TestError> testErrors) {
618 super(tr("Fixing errors ..."), false /* don't ignore exceptions */);
619 this.testErrors = testErrors == null ? new ArrayList<>() : testErrors;
620 }
621
622 @Override
623 protected void cancel() {
624 this.canceled = true;
625 }
626
627 @Override
628 protected void finish() {
629 // do nothing
630 }
631
632 protected void fixError(TestError error) throws InterruptedException, InvocationTargetException {
633 if (error.isFixable()) {
634 if (error.getPrimitives().stream().noneMatch(p -> p.isDeleted() || p.getDataSet() == null)) {
635 final Command fixCommand = error.getFix();
636 if (fixCommand != null) {
637 SwingUtilities.invokeAndWait(fixCommand::executeCommand);
638 fixCommands.add(fixCommand);
639 }
640 }
641 // It is wanted to ignore an error if it said fixable, even if fixCommand was null
642 // This is to fix #5764 and #5773:
643 // a delete command, for example, may be null if all concerned primitives have already been deleted
644 error.setIgnored(true);
645 }
646 }
647
648 @Override
649 protected void realRun() throws SAXException, IOException, OsmTransferException {
650 ProgressMonitor monitor = getProgressMonitor();
651 try {
652 monitor.setTicksCount(testErrors.size());
653 final DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
654 int i = 0;
655 SwingUtilities.invokeAndWait(ds::beginUpdate);
656 tree.setResetScheduled();
657 try {
658 for (TestError error: testErrors) {
659 i++;
660 monitor.subTask(tr("Fixing ({0}/{1}): ''{2}''", i, testErrors.size(), error.getMessage()));
661 if (this.canceled)
662 return;
663 fixError(error);
664 monitor.worked(1);
665 }
666 } finally {
667 SwingUtilities.invokeAndWait(ds::endUpdate);
668 }
669 monitor.subTask(tr("Updating map ..."));
670 SwingUtilities.invokeAndWait(() -> {
671 if (!fixCommands.isEmpty()) {
672 UndoRedoHandler.getInstance().add(
673 fixCommands.size() > 1 ? new AutofixCommand(fixCommands) : fixCommands.get(0), false);
674 }
675 invalidateValidatorLayers();
676 });
677 } catch (InterruptedException e) {
678 tryUndo();
679 throw new JosmRuntimeException(e);
680 } catch (InvocationTargetException e) {
681 // FIXME: signature of realRun should have a generic checked exception we could throw here
682 throw new JosmRuntimeException(e);
683 } finally {
684 if (monitor.isCanceled()) {
685 tryUndo();
686 }
687 GuiHelper.runInEDTAndWait(tree::resetErrors);
688 monitor.finishTask();
689 }
690 }
691
692 /**
693 * Undo commands as they were not yet added to the UndoRedo Handler
694 */
695 private void tryUndo() {
696 MainApplication.getLayerManager().getActiveDataSet().update(() -> {
697 for (int i = fixCommands.size() - 1; i >= 0; i--) {
698 fixCommands.get(i).undoCommand();
699 }
700 });
701 }
702 }
703
704 /**
705 * Invalidate the error layer
706 */
707 public static void invalidateValidatorLayers() {
708 MainApplication.getLayerManager().getLayersOfType(ValidatorLayer.class).forEach(ValidatorLayer::invalidate);
709 }
710
711 @Override
712 public void processDatasetEvent(AbstractDatasetChangedEvent event) {
713 validateAction.updateEnabledState();
714 lookupAction.updateEnabledState();
715 }
716
717 private static class AutofixCommand extends SequenceCommand {
718 AutofixCommand(Collection<Command> sequenz) {
719 super(tr("auto-fixed validator issues"), sequenz, true);
720 setSequenceComplete(true);
721 }
722
723 @Override
724 public void undoCommand() {
725 getAffectedDataSet().update(super::undoCommand);
726 }
727
728 @Override
729 public boolean executeCommand() {
730 return getAffectedDataSet().update(super::executeCommand);
731 }
732 }
733
734 @Override
735 public void destroy() {
736 super.destroy();
737 if (ignoreForNowAction != null) {
738 ignoreForNowAction.destroy();
739 }
740 }
741
742 @Override
743 public void preferenceChanged(PreferenceChangeEvent e) {
744 super.preferenceChanged(e);
745 // see #23430: update selection so that filters are applied
746 if (ValidatorPrefHelper.PREF_FILTER_BY_SELECTION.equals(e.getKey())) {
747 DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
748 if (ds != null) {
749 updateSelection(ds.getAllSelected());
750 }
751 }
752 }
753
754}
Note: See TracBrowser for help on using the repository browser.