Ticket #17268: clear_ignored_errors_v22.patch
File clear_ignored_errors_v22.patch, 25.9 KB (added by , 5 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 50 /** The preferences key for the ignorelist backup */ 51 public static final String PREF_IGNORELIST_BACKUP = PREFIX + ".ignorelist.bak"; 52 53 /** The preferences key for whether or not the ignorelist backup should be cleared on start */ 54 public static final BooleanProperty PREF_IGNORELIST_KEEP_BACKUP = new BooleanProperty(PREFIX + ".ignorelist.bak.keep", false); 55 47 56 /** 48 57 * The preferences key for enabling the permanent filtering 49 58 * 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; … … 18 17 import java.util.Collections; 19 18 import java.util.EnumMap; 20 19 import java.util.HashMap; 20 import java.util.Iterator; 21 21 import java.util.List; 22 22 import java.util.Map; 23 import java.util.Map.Entry; 23 24 import java.util.SortedMap; 24 25 import java.util.TreeMap; 25 26 import java.util.TreeSet; … … 88 89 /** Grid detail, multiplier of east,north values for valuable cell sizing */ 89 90 private static double griddetail; 90 91 91 private static final Collection<String> ignoredErrors = new TreeSet<>(); 92 92 private static final SortedMap<String, String> ignoredErrors = new TreeMap<>(); 93 93 /** 94 94 * All registered tests 95 95 */ … … 169 169 public static void initialize() { 170 170 checkValidatorDir(); 171 171 initializeGridDetail(); 172 loadIgnoredErrors(); //FIXME: load only when needed172 loadIgnoredErrors(); 173 173 } 174 174 175 175 /** … … 204 204 private static void loadIgnoredErrors() { 205 205 ignoredErrors.clear(); 206 206 if (ValidatorPrefHelper.PREF_USE_IGNORE.get()) { 207 Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(ignoredErrors::putAll); 207 208 Path path = Paths.get(getValidatorDir()).resolve("ignorederrors"); 208 209 try { 209 210 if (path.toFile().exists()) { 210 211 try { 211 ignoredErrors.addAll(Files.readAllLines(path, StandardCharsets.UTF_8)); 212 TreeSet<String> treeSet = new TreeSet<>(); 213 treeSet.addAll(Files.readAllLines(path, StandardCharsets.UTF_8)); 214 treeSet.forEach(ignore -> ignoredErrors.putIfAbsent(ignore, "")); 215 216 saveIgnoredErrors(); 217 Files.deleteIfExists(path); 212 218 } catch (FileNotFoundException e) { 213 219 Logging.debug(Logging.getErrorMessage(e)); 214 220 } catch (IOException e) { … … 219 225 Logging.log(Logging.LEVEL_ERROR, "Unable to load ignored errors", e); 220 226 } 221 227 } 228 cleanupIgnoredErrors(); 222 229 } 223 230 224 231 /** … … 228 235 * @see TestError#getIgnoreSubGroup() 229 236 */ 230 237 public static void addIgnoredError(String s) { 231 ignoredErrors.add(s);238 addIgnoredError(s, ""); 232 239 } 233 240 234 241 /** 242 * Adds an ignored error 243 * @param s The ignore group / sub group name 244 * @param description What the error actually is 245 * @see TestError#getIgnoreGroup() 246 * @see TestError#getIgnoreSubGroup() 247 */ 248 public static void addIgnoredError(String s, String description) { 249 if (description == null) description = ""; 250 251 ignoredErrors.put(s, description); 252 } 253 254 /** 255 * make sure that we don't keep single entries for a "group ignore" 256 */ 257 private static void cleanupIgnoredErrors() { 258 if (ignoredErrors.size() > 1) { 259 List<String> toRemove = new ArrayList<>(); 260 261 Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator(); 262 Entry<String, String> last = iter.next(); 263 while (iter.hasNext()) { 264 Entry<String, String> entry = iter.next(); 265 if (entry.getKey().startsWith(last.getKey())) { 266 toRemove.add(entry.getKey()); 267 } else { 268 last = entry; 269 } 270 } 271 toRemove.forEach(ignoredErrors::remove); 272 } 273 } 274 275 /** 235 276 * Check if a error should be ignored 236 277 * @param s The ignore group / sub group name 237 278 * @return <code>true</code> to ignore that error 238 279 */ 239 280 public static boolean hasIgnoredError(String s) { 240 return ignoredErrors.contains (s);281 return ignoredErrors.containsKey(s); 241 282 } 242 283 243 284 /** 244 * Saves the names of the ignored errors to a file 285 * Get the list of all ignored errors 286 * @return The <code>Collection<String></code> of errors that are ignored 245 287 */ 288 public static SortedMap<String, String> getIgnoredErrors() { 289 return ignoredErrors; 290 } 291 292 /** 293 * Reset the error list by deleting {@code validator.ignorelist} 294 */ 295 public static void resetErrorList() { 296 saveIgnoredErrors(); 297 backupErrorList(); 298 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null); 299 OsmValidator.initialize(); 300 } 301 302 /** 303 * Restore the error list by copying {@code validator.ignorelist.bak} to 304 * {@code validator.ignorelist} 305 */ 306 public static void restoreErrorList() { 307 saveIgnoredErrors(); 308 List<Map<String, String>> tlist = Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP); 309 backupErrorList(); 310 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, tlist); 311 OsmValidator.initialize(); 312 } 313 314 private static void backupErrorList() { 315 List<Map<String, String>> tlist = Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, null); 316 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP, tlist); 317 } 318 319 /** 320 * Saves the names of the ignored errors to a preference 321 */ 246 322 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); 323 cleanupIgnoredErrors(); 324 List<Map<String, String>> list = new ArrayList<>(); 325 list.add(ignoredErrors); 326 int i = 0; 327 while (i < list.size()) { 328 if (list.get(i) == null || list.get(i).isEmpty()) { 329 list.remove(i); 330 continue; 250 331 } 251 } catch (IOException e) { 252 Logging.error(e); 332 i++; 253 333 } 334 if (list.isEmpty()) list = null; 335 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST, list); 254 336 } 255 337 256 338 /** -
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 if (!ValidatorPrefHelper.PREF_IGNORELIST_KEEP_BACKUP.get()) { 182 // Clear the backup ignore list 183 Config.getPref().putListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST_BACKUP, null); 184 } 185 ignorelistManagement = new SideButton(new AbstractAction() { 186 { 187 putValue(NAME, tr("Manage Ignore")); 188 putValue(SHORT_DESCRIPTION, tr("Manage the ignore list")); 189 new ImageProvider("dialogs", "fix").getResource().attachImageIcon(this, true); 190 } 191 192 @Override 193 public void actionPerformed(ActionEvent e) { 194 ValidatorListManagementDialog dialog = new ValidatorListManagementDialog("Ignore"); 195 if (dialog.getValue() == 1) { 196 // TODO save 197 } 198 } 199 }); 200 buttons.add(ignorelistManagement); 177 201 } else { 178 202 ignoreButton = null; 203 ignorelistManagement = null; 179 204 } 205 180 206 createLayout(tree, true, buttons); 181 207 } 182 208 … … 245 271 246 272 Object mainNodeInfo = node.getUserObject(); 247 273 if (!(mainNodeInfo instanceof TestError)) { 248 Set< String> state = new HashSet<>();274 Set<Pair<String, String>> state = new HashSet<>(); 249 275 // ask if the whole set should be ignored 250 276 if (asked == JOptionPane.DEFAULT_OPTION) { 251 277 String[] a = new String[] {tr("Whole group"), tr("Single elements"), tr("Nothing")}; … … 257 283 ValidatorTreePanel.visitTestErrors(node, err -> { 258 284 err.setIgnored(true); 259 285 changed.set(true); 260 state.add(n ode.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup());286 state.add(new Pair<>(node.getDepth() == 1 ? err.getIgnoreSubGroup() : err.getIgnoreGroup(), err.getMessage())); 261 287 }, processedNodes); 262 for ( Strings : state) {263 OsmValidator.addIgnoredError(s );288 for (Pair<String, String> s : state) { 289 OsmValidator.addIgnoredError(s.a, s.b); 264 290 } 265 291 continue; 266 292 } else if (asked == JOptionPane.CANCEL_OPTION || asked == JOptionPane.CLOSED_OPTION) { … … 271 297 ValidatorTreePanel.visitTestErrors(node, error -> { 272 298 String state = error.getIgnoreState(); 273 299 if (state != null) { 274 OsmValidator.addIgnoredError(state );300 OsmValidator.addIgnoredError(state, error.getMessage()); 275 301 } 276 302 changed.set(true); 277 303 error.setIgnored(true); … … 287 313 /** 288 314 * Sets the selection of the map to the current selected items. 289 315 */ 290 @SuppressWarnings("unchecked")291 316 private void setSelectedItems() { 292 317 DataSet ds = MainApplication.getLayerManager().getActiveDataSet(); 293 318 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.GridBagLayout; 7 import java.awt.Rectangle; 8 import java.awt.event.ActionEvent; 9 import java.awt.event.MouseAdapter; 10 import java.awt.event.MouseEvent; 11 import java.util.Enumeration; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.Locale; 15 import java.util.Map; 16 import java.util.Map.Entry; 17 import java.util.TreeMap; 18 19 import javax.swing.AbstractAction; 20 import javax.swing.ImageIcon; 21 import javax.swing.JMenuItem; 22 import javax.swing.JOptionPane; 23 import javax.swing.JPanel; 24 import javax.swing.JPopupMenu; 25 import javax.swing.JScrollPane; 26 import javax.swing.JTree; 27 import javax.swing.tree.DefaultMutableTreeNode; 28 import javax.swing.tree.TreeModel; 29 import javax.swing.tree.TreeNode; 30 import javax.swing.tree.TreePath; 31 32 import org.openstreetmap.josm.actions.ValidateAction; 33 import org.openstreetmap.josm.data.validation.OsmValidator; 34 import org.openstreetmap.josm.data.validation.TestError; 35 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 36 import org.openstreetmap.josm.gui.ExtendedDialog; 37 import org.openstreetmap.josm.gui.MainApplication; 38 import org.openstreetmap.josm.gui.MapFrame; 39 import org.openstreetmap.josm.gui.util.GuiHelper; 40 import org.openstreetmap.josm.tools.GBC; 41 import org.openstreetmap.josm.tools.ImageProvider; 42 import org.openstreetmap.josm.tools.Logging; 43 44 45 /** 46 * A management window for the validator's ignorelist 47 * @author Taylor Smock 48 * @since xxx 49 */ 50 public class ValidatorListManagementDialog extends ExtendedDialog { 51 enum BUTTONS { 52 OK(0, tr("OK"), new ImageProvider("ok")), 53 CLEAR(1, tr("Clear All"), new ImageProvider("dialogs", "fix")), 54 RESTORE(2, tr("Restore"), new ImageProvider("copy")), 55 CANCEL(3, tr("Cancel"), new ImageProvider("cancel")); 56 57 private int index; 58 private String name; 59 private ImageIcon icon; 60 61 BUTTONS(int index, String name, ImageProvider image) { 62 this.index = index; 63 this.name = name; 64 this.icon = image.getResource().getImageIcon(); 65 } 66 67 public ImageIcon getImageIcon() { 68 return icon; 69 } 70 71 public int getIndex() { 72 return index; 73 } 74 75 public String getName() { 76 return name; 77 } 78 } 79 80 private static final String[] BUTTON_TEXTS = {BUTTONS.OK.getName(), BUTTONS.CLEAR.getName(), 81 BUTTONS.RESTORE.getName(), BUTTONS.CANCEL.getName() 82 }; 83 84 private static final ImageIcon[] BUTTON_IMAGES = {BUTTONS.OK.getImageIcon(), BUTTONS.CLEAR.getImageIcon(), 85 BUTTONS.RESTORE.getImageIcon(), BUTTONS.CANCEL.getImageIcon() 86 }; 87 88 private final JPanel panel = new JPanel(new GridBagLayout()); 89 90 private final JTree ignoreErrors; 91 92 private final String type; 93 94 /** 95 * Create a new {@link ValidatorListManagementDialog} 96 * @param type The type of list to create (first letter may or may not be 97 * capitalized, it is put into all lowercase after building the title) 98 */ 99 public ValidatorListManagementDialog(String type) { 100 super(MainApplication.getMainFrame(), tr("Validator {0} List Management", type), BUTTON_TEXTS, false); 101 this.type = type.toLowerCase(Locale.ENGLISH); 102 setButtonIcons(BUTTON_IMAGES); 103 104 ignoreErrors = buildList(); 105 JScrollPane scroll = GuiHelper.embedInVerticalScrollPane(ignoreErrors); 106 107 panel.add(scroll, GBC.eol().fill(GBC.BOTH).anchor(GBC.CENTER)); 108 setContent(panel); 109 setDefaultButton(1); 110 setupDialog(); 111 showDialog(); 112 } 113 114 @Override 115 public void buttonAction(int buttonIndex, ActionEvent evt) { 116 // Currently OK/Cancel buttons do nothing 117 final int answer; 118 if (buttonIndex == BUTTONS.RESTORE.getIndex()) { 119 dispose(); 120 answer = rerunValidatorPrompt(); 121 if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) { 122 OsmValidator.restoreErrorList(); 123 } 124 } else if (buttonIndex == BUTTONS.CLEAR.getIndex()) { 125 dispose(); 126 answer = rerunValidatorPrompt(); 127 if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) { 128 OsmValidator.resetErrorList(); 129 } 130 } else if (buttonIndex == BUTTONS.OK.getIndex()) { 131 Map<String, String> errors = OsmValidator.getIgnoredErrors(); 132 Map<String, String> tree = buildIgnore(ignoreErrors); 133 if (!errors.equals(tree)) { 134 answer = rerunValidatorPrompt(); 135 if (answer == JOptionPane.YES_OPTION || answer == JOptionPane.NO_OPTION) { 136 OsmValidator.resetErrorList(); 137 Logging.setLogLevel(Logging.LEVEL_DEBUG); 138 Logging.debug("Starting to rebuild the error list of size {0}", tree.size()); 139 tree.forEach((ignore, description) -> { 140 Logging.debug("Adding {0} with description {1}", ignore, description); 141 OsmValidator.addIgnoredError(ignore, description); 142 }); 143 OsmValidator.saveIgnoredErrors(); 144 OsmValidator.initialize(); 145 } 146 } 147 dispose(); 148 } else { 149 super.buttonAction(buttonIndex, evt); 150 } 151 } 152 153 /** 154 * Build a {@code HashMap} from a tree of ignored errors 155 * @param tree The JTree of ignored errors 156 * @return A {@code HashMap} of the ignored errors for comparison 157 */ 158 public Map<String, String> buildIgnore(JTree tree) { 159 TreeModel model = tree.getModel(); 160 DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot(); 161 return buildIgnore(model, root); 162 } 163 164 private static Map<String, String> buildIgnore(TreeModel model, DefaultMutableTreeNode node) { 165 Logging.setLogLevel(Logging.LEVEL_DEBUG); 166 HashMap<String, String> rHashMap = new HashMap<>(); 167 168 String osmids = node.getUserObject().toString(); 169 String description = ""; 170 171 if (!model.getRoot().equals(node)) description = ((DefaultMutableTreeNode) node.getParent()).getUserObject().toString(); 172 if (!osmids.matches("^[0-9]+_.*")) osmids = ""; 173 174 for (int i = 0; i < model.getChildCount(node); i++) { 175 DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(node, i); 176 if (model.getChildCount(child) == 0) { 177 String ignoreName = child.getUserObject().toString(); 178 if (ignoreName.matches("^(r|w|n)_.*")) { 179 osmids += ":" + child.getUserObject().toString(); 180 } else if (ignoreName.matches("^[0-9]+_.*")) { 181 rHashMap.put(ignoreName, description); 182 } 183 } else { 184 rHashMap.putAll(buildIgnore(model, child)); 185 } 186 } 187 if (!osmids.isEmpty() && osmids.indexOf(':') != 0) rHashMap.put(osmids, description); 188 return rHashMap; 189 } 190 191 private static DefaultMutableTreeNode inTree(DefaultMutableTreeNode root, String name) { 192 @SuppressWarnings("unchecked") 193 Enumeration<TreeNode> trunks = root.children(); 194 while (trunks.hasMoreElements()) { 195 TreeNode ttrunk = trunks.nextElement(); 196 if (ttrunk instanceof DefaultMutableTreeNode) { 197 DefaultMutableTreeNode trunk = (DefaultMutableTreeNode) ttrunk; 198 if (name.equals(trunk.getUserObject())) { 199 return trunk; 200 } 201 } 202 } 203 return new DefaultMutableTreeNode(name); 204 } 205 206 /** 207 * Build a JTree with a list 208 * @return <type>list as a {@code JTree} 209 */ 210 public JTree buildList() { 211 TreeMap<String, String> map = new TreeMap<>(); 212 if ("ignore".equals(type)) { 213 Map<String, String> tmap; 214 tmap = OsmValidator.getIgnoredErrors(); 215 if (tmap.isEmpty()) { 216 OsmValidator.initialize(); 217 tmap = OsmValidator.getIgnoredErrors(); 218 } 219 map.putAll(tmap); 220 } else { 221 Logging.error(tr("Cannot understand the following type: {0}", type)); 222 return null; 223 } 224 DefaultMutableTreeNode root = new DefaultMutableTreeNode(tr("{0} list", type)); 225 226 for (Entry<String, String> e: map.entrySet()) { 227 String key = e.getKey(); 228 String value = e.getValue(); 229 String[] osmobjects = key.split(":(r|w|n)_"); 230 DefaultMutableTreeNode trunk; 231 DefaultMutableTreeNode branch; 232 233 if (value != null && !value.isEmpty()) { 234 trunk = inTree(root, value); 235 branch = inTree(trunk, osmobjects[0]); 236 trunk.add(branch); 237 } else { 238 trunk = inTree(root, osmobjects[0]); 239 branch = trunk; 240 } 241 for (int i = 1; i < osmobjects.length; i++) { 242 String osmid = osmobjects[i]; 243 int index = key.indexOf(osmid); 244 char type = key.charAt(index - 2); 245 DefaultMutableTreeNode leaf = new DefaultMutableTreeNode(type + "_" + osmid); 246 branch.add(leaf); 247 } 248 root.add(trunk); 249 } 250 JTree tree = new JTree(root); 251 tree.setRootVisible(false); 252 tree.setShowsRootHandles(true); 253 tree.addMouseListener(new MouseAdapter() { 254 @Override 255 public void mouseClicked(MouseEvent e) { 256 if (e.isPopupTrigger()) { 257 TreePath[] paths = tree.getSelectionPaths(); 258 if (paths == null) return; 259 Rectangle bounds = tree.getUI().getPathBounds(tree, paths[0]); 260 if (bounds != null && bounds.contains(e.getX(), e.getY())) { 261 JPopupMenu menu = new JPopupMenu(); 262 JMenuItem delete = new JMenuItem(new AbstractAction(tr("Delete")) { 263 @Override 264 public void actionPerformed(ActionEvent e1) { 265 for (TreePath path : paths) { 266 tree.clearSelection(); 267 tree.addSelectionPath(path); 268 DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); 269 DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent(); 270 node.removeAllChildren(); 271 while (node.getChildCount() == 0) { 272 node.removeFromParent(); 273 node = parent; 274 if (parent.isRoot()) break; 275 parent = (DefaultMutableTreeNode) node.getParent(); 276 } 277 } 278 tree.updateUI(); 279 } 280 }); 281 menu.add(delete); 282 menu.show(e.getComponent(), e.getX(), e.getY()); 283 } 284 } 285 } 286 }); 287 return tree; 288 } 289 290 /** 291 * Prompt to rerun the validator when the ignore list changes 292 * @return {@code JOptionPane.YES_OPTION}, {@code JOptionPane.NO_OPTION}, 293 * or {@code JOptionPane.CANCEL_OPTION} 294 */ 295 public int rerunValidatorPrompt() { 296 MapFrame map = MainApplication.getMap(); 297 List<TestError> errors = map.validatorDialog.tree.getErrors(); 298 ValidateAction validateAction = ValidatorDialog.validateAction; 299 if (!validateAction.isEnabled() || errors == null || errors.isEmpty()) return JOptionPane.NO_OPTION; 300 final int answer = ConditionalOptionPaneUtil.showOptionDialog( 301 "rerun_validation_when_ignorelist_changed", 302 MainApplication.getMainFrame(), 303 tr("{0}Should the validation be rerun?{1}", "<hmtl><h3>", "</h3></html>"), 304 tr("Ignored error filter changed"), 305 JOptionPane.YES_NO_CANCEL_OPTION, 306 JOptionPane.QUESTION_MESSAGE, 307 null, 308 null); 309 if (answer == JOptionPane.YES_OPTION) { 310 validateAction.doValidate(true); 311 } 312 return answer; 313 } 314 } -
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");