Ticket #17268: clear_ignored_errors_v31.patch
| File clear_ignored_errors_v31.patch, 25.1 KB (added by , 7 years ago) |
|---|
-
src/org/openstreetmap/josm/data/preferences/sources/ValidatorPrefHelper.java
44 44 /** The preferences for ignored severity other */ 45 45 public static final BooleanProperty PREF_OTHER = new BooleanProperty(PREFIX + ".other", false); 46 46 47 /** The preferences key for the ignorelist */ 48 public static final String PREF_IGNORELIST = PREFIX + ".ignorelist"; 49 47 50 /** 48 51 * The preferences key for enabling the permanent filtering 49 52 * of the displayed errors in the tree regarding the current selection -
src/org/openstreetmap/josm/data/validation/OsmValidator.java
7 7 import java.io.File; 8 8 import java.io.FileNotFoundException; 9 9 import java.io.IOException; 10 import java.io.PrintWriter;11 10 import java.nio.charset.StandardCharsets; 12 11 import java.nio.file.Files; 13 12 import java.nio.file.Path; … … 17 16 import java.util.Collection; 18 17 import java.util.Collections; 19 18 import java.util.EnumMap; 19 import java.util.Enumeration; 20 20 import java.util.HashMap; 21 import java.util.Iterator; 21 22 import java.util.List; 22 23 import java.util.Map; 24 import java.util.Map.Entry; 23 25 import java.util.SortedMap; 24 26 import java.util.TreeMap; 25 27 import java.util.TreeSet; … … 27 29 import java.util.stream.Collectors; 28 30 29 31 import javax.swing.JOptionPane; 32 import javax.swing.JTree; 33 import javax.swing.tree.DefaultMutableTreeNode; 34 import javax.swing.tree.TreeModel; 35 import javax.swing.tree.TreeNode; 30 36 31 37 import org.openstreetmap.josm.data.preferences.sources.ValidatorPrefHelper; 32 38 import org.openstreetmap.josm.data.projection.ProjectionRegistry; … … 88 94 /** Grid detail, multiplier of east,north values for valuable cell sizing */ 89 95 private static double griddetail; 90 96 91 private static final Collection<String> ignoredErrors = new TreeSet<>(); 92 97 private static final SortedMap<String, String> ignoredErrors = new TreeMap<>(); 93 98 /** 94 99 * All registered tests 95 100 */ … … 169 174 public static void initialize() { 170 175 checkValidatorDir(); 171 176 initializeGridDetail(); 172 loadIgnoredErrors(); //FIXME: load only when needed177 loadIgnoredErrors(); 173 178 } 174 179 175 180 /** … … 204 209 private static void loadIgnoredErrors() { 205 210 ignoredErrors.clear(); 206 211 if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) { 212 Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(ignoredErrors::putAll); 207 213 Path path = Paths.get(getValidatorDir()).resolve("ignorederrors"); 208 214 try { 209 215 if (path.toFile().exists()) { 210 216 try { 211 ignoredErrors.addAll(Files.readAllLines(path, StandardCharsets.UTF_8)); 217 TreeSet<String> treeSet = new TreeSet<>(); 218 treeSet.addAll(Files.readAllLines(path, StandardCharsets.UTF_8)); 219 treeSet.forEach(ignore -> ignoredErrors.putIfAbsent(ignore, "")); 220 221 saveIgnoredErrors(); 222 Files.deleteIfExists(path); 223 212 224 } catch (FileNotFoundException e) { 213 225 Logging.debug(Logging.getErrorMessage(e)); 214 226 } catch (IOException e) { … … 228 240 * @see TestError#getIgnoreSubGroup() 229 241 */ 230 242 public static void addIgnoredError(String s) { 231 ignoredErrors.add(s);243 addIgnoredError(s, ""); 232 244 } 233 245 234 246 /** 247 * Adds an ignored error 248 * @param s The ignore group / sub group name 249 * @param description What the error actually is 250 * @see TestError#getIgnoreGroup() 251 * @see TestError#getIgnoreSubGroup() 252 */ 253 public static void addIgnoredError(String s, String description) { 254 if (description == null) description = ""; 255 ignoredErrors.put(s, description); 256 } 257 258 /** 259 * Make sure that we don't keep single entries for a "group ignore" or 260 * multiple different entries for the single entries that are in the same group. 261 */ 262 private static void cleanupIgnoredErrors() { 263 if (ignoredErrors.size() > 1) { 264 List<String> toRemove = new ArrayList<>(); 265 266 Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator(); 267 Entry<String, String> last = iter.next(); 268 while (iter.hasNext()) { 269 Entry<String, String> entry = iter.next(); 270 if (entry.getKey().startsWith(last.getKey())) { 271 toRemove.add(entry.getKey()); 272 } else { 273 last = entry; 274 } 275 } 276 toRemove.forEach(ignoredErrors::remove); 277 } 278 279 Map<String, String> tmap = buildIgnore(buildJTreeList()); 280 if (tmap != null && !tmap.isEmpty()) { 281 ignoredErrors.clear(); 282 ignoredErrors.putAll(tmap); 283 } 284 } 285 286 /** 235 287 * Check if a error should be ignored 236 288 * @param s The ignore group / sub group name 237 289 * @return <code>true</code> to ignore that error 238 290 */ 239 291 public static boolean hasIgnoredError(String s) { 240 return ignoredErrors.contains (s);292 return ignoredErrors.containsKey(s); 241 293 } 242 294 243 295 /** 244 * Saves the names of the ignored errors to a file 296 * Get the list of all ignored errors 297 * @return The <code>Collection<String></code> of errors that are ignored 245 298 */ 299 public static SortedMap<String, String> getIgnoredErrors() { 300 return ignoredErrors; 301 } 302 303 /** 304 * Build a JTree with a list 305 * @return <type>list as a {@code JTree} 306 */ 307 public static JTree buildJTreeList() { 308 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("Ignore list")); 309 for (Entry<String, String> e: ignoredErrors.entrySet()) { 310 String key = e.getKey(); 311 String value = e.getValue(); 312 ArrayList<String> ignoredWayList = new ArrayList<>(); 313 String[] osmobjects = key.split(":(r|w|n)_"); 314 for (int i = 1; i < osmobjects.length; i++) { 315 String osmid = osmobjects[i]; 316 if (osmid.matches("^[0-9]+$")) { 317 osmid = '_' + osmid; 318 int index = key.indexOf(osmid); 319 if (index < key.lastIndexOf(']')) continue; 320 char type = key.charAt(index - 1); 321 ignoredWayList.add(type + osmid); 322 } 323 } 324 for (String osmignore : ignoredWayList) { 325 key = key.replace(':' + osmignore, ""); 326 } 327 328 DefaultMutableTreeNode trunk; 329 DefaultMutableTreeNode branch; 330 331 if (value != null && !value.isEmpty()) { 332 trunk = inTree(root, value); 333 branch = inTree(trunk, key); 334 trunk.add(branch); 335 } else { 336 trunk = inTree(root, key); 337 branch = trunk; 338 } 339 ignoredWayList.forEach(osmignore -> branch.add(new DefaultMutableTreeNode(osmignore))); 340 341 root.add(trunk); 342 } 343 return new JTree(root); 344 } 345 346 private static DefaultMutableTreeNode inTree(DefaultMutableTreeNode root, String name) { 347 @SuppressWarnings("unchecked") 348 Enumeration<TreeNode> trunks = root.children(); 349 while (trunks.hasMoreElements()) { 350 TreeNode ttrunk = trunks.nextElement(); 351 if (ttrunk instanceof DefaultMutableTreeNode) { 352 DefaultMutableTreeNode trunk = (DefaultMutableTreeNode) ttrunk; 353 if (name.equals(trunk.getUserObject())) { 354 return trunk; 355 } 356 } 357 } 358 return new DefaultMutableTreeNode(name); 359 } 360 361 /** 362 * Build a {@code HashMap} from a tree of ignored errors 363 * @param tree The JTree of ignored errors 364 * @return A {@code HashMap} of the ignored errors for comparison 365 */ 366 public static Map<String, String> buildIgnore(JTree tree) { 367 TreeModel model = tree.getModel(); 368 DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); 369 return buildIgnore(model, root); 370 } 371 372 private static Map<String, String> buildIgnore(TreeModel model, DefaultMutableTreeNode node) { 373 HashMap<String, String> rHashMap = new HashMap<>(); 374 375 String osmids = node.getUserObject().toString(); 376 String description = ""; 377 378 if (!model.getRoot().equals(node)) { 379 description = ((DefaultMutableTreeNode) node.getParent()).getUserObject().toString(); 380 } else { 381 description = node.getUserObject().toString(); 382 } 383 if (tr("Ignore list").equals(description)) description = ""; 384 if (!osmids.matches("^[0-9]+(_.*|$)")) { 385 description = osmids; 386 osmids = ""; 387 } 388 389 390 for (int i = 0; i < model.getChildCount(node); i++) { 391 DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(node, i); 392 if (model.getChildCount(child) == 0) { 393 String ignoreName = child.getUserObject().toString(); 394 if (ignoreName.matches("^(r|w|n)_.*")) { 395 osmids += ":" + child.getUserObject().toString(); 396 } else if (ignoreName.matches("^[0-9]+(_.*|)$")) { 397 rHashMap.put(ignoreName, description); 398 } 399 } else { 400 rHashMap.putAll(buildIgnore(model, child)); 401 } 402 } 403 if (!osmids.isEmpty() && osmids.indexOf(':') != 0) rHashMap.put(osmids, description); 404 return rHashMap; 405 } 406 407 /** 408 * Reset the error list by deleting {@code validator.ignorelist} 409 */ 410 public static void resetErrorList() { 411 saveIgnoredErrors(); 412 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null); 413 OsmValidator.initialize(); 414 } 415 416 /** 417 * Saves the names of the ignored errors to a preference 418 */ 246 419 public static void saveIgnoredErrors() { 247 try (PrintWriter out = new PrintWriter(new File(getValidatorDir(), "ignorederrors"), StandardCharsets.UTF_8.name())) { 248 for (String e : ignoredErrors) { 249 out.println(e); 420 List<Map<String, String>> list = new ArrayList<>(); 421 cleanupIgnoredErrors(); 422 list.add(ignoredErrors); 423 int i = 0; 424 while (i < list.size()) { 425 if (list.get(i) == null || list.get(i).isEmpty()) { 426 list.remove(i); 427 continue; 250 428 } 251 } catch (IOException e) { 252 Logging.error(e); 429 i++; 253 430 } 431 if (list.isEmpty()) list = null; 432 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, list); 254 433 } 255 434 256 435 /** -
src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java
63 63 import org.openstreetmap.josm.tools.ImageProvider; 64 64 import org.openstreetmap.josm.tools.InputMapUtils; 65 65 import org.openstreetmap.josm.tools.JosmRuntimeException; 66 import org.openstreetmap.josm.tools.Pair; 66 67 import org.openstreetmap.josm.tools.Shortcut; 67 68 import org.xml.sax.SAXException; 68 69 … … 85 86 private final SideButton fixButton; 86 87 /** The ignore button */ 87 88 private final SideButton ignoreButton; 89 /** The reset ignorelist button */ 90 private final SideButton ignorelistManagement; 88 91 /** The select button */ 89 92 private final SideButton selectButton; 90 93 /** The lookup button */ … … 174 177 }); 175 178 ignoreButton.setEnabled(false); 176 179 buttons.add(ignoreButton); 180 181 ignorelistManagement = new SideButton(new AbstractAction() { 182 { 183 putValue(NAME, tr("Manage Ignore")); 184 putValue(SHORT_DESCRIPTION, tr("Manage the ignore list")); 185 new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true); 186 } 187 188 @Override 189 public void actionPerformed(ActionEvent e) { 190 ValidatorListManagementDialog dialog = new ValidatorListManagementDialog("Ignore"); 191 if (dialog.getValue() == 1) { 192 // TODO save 193 } 194 } 195 }); 196 buttons.add(ignorelistManagement); 177 197 } else { 178 198 ignoreButton = null; 199 ignorelistManagement = null; 179 200 } 201 180 202 createLayout(tree, true, buttons); 181 203 } 182 204 … … 245 267 246 268 Object mainNodeInfo = node.getUserObject(); 247 269 if (!(mainNodeInfo instanceof TestError)) { 248 Set< String> state = new HashSet<>();270 Set<Pair<String, String>> state = new HashSet<>(); 249 271 // ask if the whole set should be ignored 250 272 if (asked == JOptionPane.DEFAULT_OPTION) { 251 273 String[] a = new String[] {tr("Whole group"), tr("Single elements"), tr("Nothing")}; … … 257 279 ValidatorTreePanel.visitTestErrors(node, err -> { 258 280 err.setIgnored(true); 259 281 changed.set(true); 260 state.add(n ode.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup());282 state.add(new Pair<>(node.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup(), err.getMessage())); 261 283 }, processedNodes); 262 for ( Strings : state) {263 OsmValidator.addIgnoredError(s );284 for (Pair<String, String> s : state) { 285 OsmValidator.addIgnoredError(s.a, s.b); 264 286 } 265 287 continue; 266 288 } else if (asked == JOptionPane.CANCEL_OPTION || asked == JOptionPane.CLOSED_OPTION) { … … 271 293 ValidatorTreePanel.visitTestErrors(node, error -> { 272 294 String state = error.getIgnoreState(); 273 295 if (state != null) { 274 OsmValidator.addIgnoredError(state );296 OsmValidator.addIgnoredError(state, error.getMessage()); 275 297 } 276 298 changed.set(true); 277 299 error.setIgnored(true); … … 287 309 /** 288 310 * Sets the selection of the map to the current selected items. 289 311 */ 290 @SuppressWarnings("unchecked")291 312 private void setSelectedItems() { 292 313 DataSet ds = MainApplication.getLayerManager().getActiveDataSet(); 293 314 if (tree == null || ds == null) -
src/org/openstreetmap/josm/gui/dialogs/ValidatorListManagementDialog.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.dialogs; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Dimension; 7 import java.awt.GridBagLayout; 8 import java.awt.Rectangle; 9 import java.awt.event.ActionEvent; 10 import java.awt.event.KeyEvent; 11 import java.awt.event.KeyListener; 12 import java.awt.event.MouseAdapter; 13 import java.awt.event.MouseEvent; 14 import java.util.List; 15 import java.util.Locale; 16 import java.util.Map; 17 18 import javax.swing.AbstractAction; 19 import javax.swing.ImageIcon; 20 import javax.swing.JMenuItem; 21 import javax.swing.JOptionPane; 22 import javax.swing.JPanel; 23 import javax.swing.JPopupMenu; 24 import javax.swing.JScrollPane; 25 import javax.swing.JTree; 26 import javax.swing.tree.DefaultMutableTreeNode; 27 import javax.swing.tree.TreePath; 28 29 import org.openstreetmap.josm.actions.ValidateAction; 30 import org.openstreetmap.josm.data.validation.OsmValidator; 31 import org.openstreetmap.josm.data.validation.TestError; 32 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 33 import org.openstreetmap.josm.gui.ExtendedDialog; 34 import org.openstreetmap.josm.gui.MainApplication; 35 import org.openstreetmap.josm.gui.MapFrame; 36 import org.openstreetmap.josm.gui.util.GuiHelper; 37 import org.openstreetmap.josm.tools.GBC; 38 import org.openstreetmap.josm.tools.ImageProvider; 39 import org.openstreetmap.josm.tools.Logging; 40 41 42 /** 43 * A management window for the validator's ignorelist 44 * @author Taylor Smock 45 * @since xxx 46 */ 47 public class ValidatorListManagementDialog extends ExtendedDialog { 48 enum BUTTONS { 49 OK(0, tr("OK"), new ImageProvider("ok")), 50 CANCEL(1, tr("Cancel"), new ImageProvider("cancel")); 51 52 private int index; 53 private String name; 54 private ImageIcon icon; 55 56 BUTTONS(int index, String name, ImageProvider image) { 57 this.index = index; 58 this.name = name; 59 Dimension dim = new Dimension(); 60 ImageIcon sizeto = new ImageProvider("ok").getResource().getImageIcon(); 61 dim.setSize(-1, sizeto.getIconHeight()); 62 this.icon = image.getResource().getImageIcon(dim); 63 } 64 65 public ImageIcon getImageIcon() { 66 return icon; 67 } 68 69 public int getIndex() { 70 return index; 71 } 72 73 public String getName() { 74 return name; 75 } 76 } 77 78 private static final String[] BUTTON_TEXTS = {BUTTONS.OK.getName(), BUTTONS.CANCEL.getName()}; 79 80 private static final ImageIcon[] BUTTON_IMAGES = {BUTTONS.OK.getImageIcon(), BUTTONS.CANCEL.getImageIcon()}; 81 82 private final JPanel panel = new JPanel(new GridBagLayout()); 83 84 private final JTree ignoreErrors; 85 86 private final String type; 87 88 /** 89 * Create a new {@link ValidatorListManagementDialog} 90 * @param type The type of list to create (first letter may or may not be 91 * capitalized, it is put into all lowercase after building the title) 92 */ 93 public ValidatorListManagementDialog(String type) { 94 super(MainApplication.getMainFrame(), tr("Validator {0} List Management", type), BUTTON_TEXTS, false); 95 this.type = type.toLowerCase(Locale.ENGLISH); 96 setButtonIcons(BUTTON_IMAGES); 97 98 ignoreErrors = buildList(); 99 JScrollPane scroll = GuiHelper.embedInVerticalScrollPane(ignoreErrors); 100 101 panel.add(scroll, GBC.eol().fill(GBC.BOTH).anchor(GBC.CENTER)); 102 setContent(panel); 103 setDefaultButton(1); 104 setupDialog(); 105 setModal(true); 106 showDialog(); 107 } 108 109 @Override 110 public void buttonAction(int buttonIndex, ActionEvent evt) { 111 // Currently OK/Cancel buttons do nothing 112 final int answer; 113 if (buttonIndex == BUTTONS.OK.getIndex()) { 114 Map<String, String> errors = OsmValidator.getIgnoredErrors(); 115 Map<String, String> tree = OsmValidator.buildIgnore(ignoreErrors); 116 if (!errors.equals(tree)) { 117 answer = rerunValidatorPrompt(); 118 if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) { 119 OsmValidator.resetErrorList(); 120 tree.forEach((ignore, description) -> { 121 OsmValidator.addIgnoredError(ignore, description); 122 }); 123 OsmValidator.saveIgnoredErrors(); 124 OsmValidator.initialize(); 125 } 126 } 127 dispose(); 128 } else { 129 super.buttonAction(buttonIndex, evt); 130 } 131 } 132 133 /** 134 * Build a JTree with a list 135 * @return <type>list as a {@code JTree} 136 */ 137 public JTree buildList() { 138 JTree tree; 139 140 if ("ignore".equals(type)) { 141 tree = OsmValidator.buildJTreeList(); 142 } else { 143 Logging.error(tr("Cannot understand the following type: {0}", type)); 144 return null; 145 } 146 tree.setRootVisible(false); 147 tree.setShowsRootHandles(true); 148 tree.addMouseListener(new MouseAdapter() { 149 @Override 150 public void mousePressed(MouseEvent e) { 151 process(e); 152 } 153 154 @Override 155 public void mouseReleased(MouseEvent e) { 156 process(e); 157 } 158 159 private void process(MouseEvent e) { 160 if (e.isPopupTrigger()) { 161 TreePath[] paths = tree.getSelectionPaths(); 162 if (paths == null) return; 163 Rectangle bounds = tree.getUI().getPathBounds(tree, paths[0]); 164 if (bounds != null) { 165 JPopupMenu menu = new JPopupMenu(); 166 JMenuItem delete = new JMenuItem(new AbstractAction(tr("Don't ignore")) { 167 @Override 168 public void actionPerformed(ActionEvent e1) { 169 deleteAction(tree, paths); 170 } 171 }); 172 menu.add(delete); 173 menu.show(e.getComponent(), e.getX(), e.getY()); 174 } 175 } 176 } 177 }); 178 179 tree.addKeyListener(new KeyListener() { 180 181 @Override 182 public void keyTyped(KeyEvent e) { 183 // Do nothing 184 } 185 186 @Override 187 public void keyPressed(KeyEvent e) { 188 // Do nothing 189 } 190 191 @Override 192 public void keyReleased(KeyEvent e) { 193 TreePath[] paths = tree.getSelectionPaths(); 194 if (e.getKeyCode() == KeyEvent.VK_DELETE && paths != null) { 195 deleteAction(tree, paths); 196 } 197 } 198 }); 199 return tree; 200 } 201 202 private void deleteAction(JTree tree, TreePath[] paths) { 203 for (TreePath path : paths) { 204 tree.clearSelection(); 205 tree.addSelectionPath(path); 206 DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); 207 DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); 208 node.removeAllChildren(); 209 while (node.getChildCount() == 0) { 210 node.removeFromParent(); 211 node = parent; 212 if (parent == null || parent.isRoot()) break; 213 parent = (DefaultMutableTreeNode) node.getParent(); 214 } 215 } 216 tree.updateUI(); 217 } 218 219 220 /** 221 * Prompt to rerun the validator when the ignore list changes 222 * @return {@code JOptionPane.YES_OPTION}, {@code JOptionPane.NO_OPTION}, 223 * or {@code JOptionPane.CANCEL_OPTION} 224 */ 225 public int rerunValidatorPrompt() { 226 MapFrame map = MainApplication.getMap(); 227 List<TestError> errors = map.validatorDialog.tree.getErrors(); 228 ValidateAction validateAction = ValidatorDialog.validateAction; 229 if (!validateAction.isEnabled() || errors == null || errors.isEmpty()) return JOptionPane.NO_OPTION; 230 final int answer = ConditionalOptionPaneUtil.showOptionDialog( 231 "rerun_validation_when_ignorelist_changed", 232 MainApplication.getMainFrame(), 233 tr("{0}Should the validation be rerun?{1}", "<hmtl><h3>", "</h3></html>"), 234 tr("Ignored error filter changed"), 235 JOptionPane.YES_NO_CANCEL_OPTION, 236 JOptionPane.QUESTION_MESSAGE, 237 null, 238 null); 239 if (answer == JOptionPane.YES_OPTION) { 240 validateAction.doValidate(true); 241 } 242 return answer; 243 } 244 } -
src/org/openstreetmap/josm/spi/preferences/MapListSetting.java
6 6 import java.util.LinkedHashMap; 7 7 import java.util.List; 8 8 import java.util.Map; 9 import java.util.SortedMap; 9 10 10 11 /** 11 12 * Setting containing a {@link List} of {@link Map}s of {@link String} values. … … 40 41 if (value.contains(null)) 41 42 throw new IllegalArgumentException("Error: Null as list element in preference setting"); 42 43 for (Map<String, String> map : value) { 43 if ( map.containsKey(null))44 if (!(map instanceof SortedMap) && map.containsKey(null)) 44 45 throw new IllegalArgumentException("Error: Null as map key in preference setting"); 45 46 if (map.containsValue(null)) 46 47 throw new IllegalArgumentException("Error: Null as map value in preference setting");
