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

Last change on this file since 16438 was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

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