- Timestamp:
- 2016-02-18T11:07:38+01:00 (9 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/Main.java
r9751 r9821 15 15 import java.awt.event.WindowEvent; 16 16 import java.io.File; 17 import java.io.IOException; 17 18 import java.lang.ref.WeakReference; 18 19 import java.net.URI; … … 1098 1099 } 1099 1100 } 1101 try { 1102 pref.saveDefaults(); 1103 } catch (IOException ex) { 1104 Main.warn(tr("Failed to save default preferences.")); 1105 } 1100 1106 worker.shutdownNow(); 1101 1107 ImageProvider.shutdown(true); -
trunk/src/org/openstreetmap/josm/data/Preferences.java
r9805 r9821 66 66 import org.openstreetmap.josm.tools.CheckParameterUtil; 67 67 import org.openstreetmap.josm.tools.ColorHelper; 68 import org.openstreetmap.josm.tools.FilteredCollection; 68 69 import org.openstreetmap.josm.tools.I18n; 69 70 import org.openstreetmap.josm.tools.MultiMap; 71 import org.openstreetmap.josm.tools.Predicate; 70 72 import org.openstreetmap.josm.tools.Utils; 71 73 import org.xml.sax.SAXException; … … 102 104 }; 103 105 106 private static final long MAX_AGE_DEFAULT_PREFERENCES = 60 * 60 * 24 * 50; // 50 days (in seconds) 107 104 108 /** 105 109 * Internal storage for the preference directory. … … 137 141 */ 138 142 protected final SortedMap<String, Setting<?>> defaultsMap = new TreeMap<>(); 143 144 private final Predicate<Entry<String, Setting<?>>> NO_DEFAULT_SETTINGS_ENTRY = new Predicate<Entry<String, Setting<?>>>() { 145 @Override 146 public boolean evaluate(Entry<String, Setting<?>> e) { 147 return !e.getValue().equals(defaultsMap.get(e.getKey())); 148 } 149 }; 139 150 140 151 /** … … 293 304 294 305 /** 295 * Returns the user preferences file (preferences.xml) 306 * Returns the user preferences file (preferences.xml). 296 307 * @return The user preferences file (preferences.xml) 297 308 */ … … 301 312 302 313 /** 303 * Returns the user plugin directory 314 * Returns the cache file for default preferences. 315 * @return the cache file for default preferences 316 */ 317 public File getDefaultsCacheFile() { 318 return new File(getCacheDirectory(), "default_preferences.xml"); 319 } 320 321 /** 322 * Returns the user plugin directory. 304 323 * @return The user plugin directory 305 324 */ … … 493 512 */ 494 513 public void save() throws IOException { 495 /* currently unused, but may help to fix configuration issues in future */ 496 putInteger("josm.version", Version.getInstance().getVersion()); 497 498 updateSystemProperties(); 499 500 File prefFile = getPreferenceFile(); 514 save(getPreferenceFile(), 515 new FilteredCollection<>(settingsMap.entrySet(), NO_DEFAULT_SETTINGS_ENTRY), false); 516 } 517 518 public void saveDefaults() throws IOException { 519 save(getDefaultsCacheFile(), defaultsMap.entrySet(), true); 520 } 521 522 public void save(File prefFile, Collection<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException { 523 524 if (!defaults) { 525 /* currently unused, but may help to fix configuration issues in future */ 526 putInteger("josm.version", Version.getInstance().getVersion()); 527 528 updateSystemProperties(); 529 } 530 501 531 File backupFile = new File(prefFile + "_backup"); 502 532 … … 508 538 try (PrintWriter out = new PrintWriter(new OutputStreamWriter( 509 539 new FileOutputStream(prefFile + "_tmp"), StandardCharsets.UTF_8), false)) { 510 out.print(toXML(settings Map, false));540 out.print(toXML(settings, false, defaults)); 511 541 } 512 542 … … 546 576 File pref = getPreferenceFile(); 547 577 PreferencesReader.validateXML(pref); 548 PreferencesReader reader = new PreferencesReader(); 578 PreferencesReader reader = new PreferencesReader(false); 549 579 reader.fromXML(pref); 550 580 settingsMap.clear(); … … 555 585 556 586 /** 587 * Loads default preferences from default settings cache file. 588 * 589 * Discards entries older than {@link #MAX_AGE_DEFAULT_PREFERENCES}. 590 * 591 * @throws IOException if any I/O error occurs while reading the file 592 * @throws SAXException if the settings file does not contain valid XML 593 * @throws XMLStreamException if an XML error occurs while parsing the file (after validation) 594 */ 595 protected void loadDefaults() throws IOException, XMLStreamException, SAXException { 596 File def = getDefaultsCacheFile(); 597 PreferencesReader.validateXML(def); 598 PreferencesReader reader = new PreferencesReader(true); 599 reader.fromXML(def); 600 defaultsMap.clear(); 601 long minTime = System.currentTimeMillis() / 1000 - MAX_AGE_DEFAULT_PREFERENCES; 602 for (Entry<String, Setting<?>> e : reader.getSettings().entrySet()) { 603 if (e.getValue().getTime() >= minTime) { 604 defaultsMap.put(e.getKey(), e.getValue()); 605 } 606 } 607 } 608 609 /** 557 610 * Loads preferences from XML reader. 558 611 * @param in XML reader … … 560 613 */ 561 614 public void fromXML(Reader in) throws XMLStreamException { 562 PreferencesReader reader = new PreferencesReader(); 615 PreferencesReader reader = new PreferencesReader(false); 563 616 reader.fromXML(in); 564 617 settingsMap.clear(); … … 629 682 load(); 630 683 initSuccessful = true; 631 } catch (Exception e) { 684 } catch (IOException | SAXException | XMLStreamException e) { 632 685 Main.error(e); 633 686 File backupFile = new File(prefDir, "preferences.xml.bak"); … … 647 700 Main.error(e1); 648 701 Main.warn(tr("Failed to initialize preferences. Failed to reset preference file to default: {0}", getPreferenceFile())); 702 } 703 } 704 File def = getDefaultsCacheFile(); 705 if (def.exists()) { 706 try { 707 loadDefaults(); 708 } catch (IOException | XMLStreamException | SAXException e) { 709 Main.error(e); 710 Main.warn(tr("Failed to load defaults cache file: {0}", def)); 711 defaultsMap.clear(); 712 if (!def.delete()) { 713 Main.warn(tr("Failed to delete faulty defaults cache file: {0}", def)); 714 } 649 715 } 650 716 } … … 880 946 CheckParameterUtil.ensureParameterNotNull(def); 881 947 Setting<?> oldDef = defaultsMap.get(key); 882 if (oldDef != null && oldDef.getValue() != null && def.getValue() != null && !def.equals(oldDef)) { 948 if (oldDef != null && oldDef.isNew() && oldDef.getValue() != null && def.getValue() != null && !def.equals(oldDef)) { 883 949 Main.info("Defaults for " + key + " differ: " + def + " != " + defaultsMap.get(key)); 884 950 } 885 951 if (def.getValue() != null || oldDef == null) { 886 defaultsMap.put(key, def.copy()); 952 Setting<?> defCopy = def.copy(); 953 defCopy.setTime(System.currentTimeMillis() / 1000); 954 defCopy.setNew(true); 955 defaultsMap.put(key, defCopy); 887 956 } 888 957 Setting<?> prop = settingsMap.get(key); … … 1333 1402 private final StringBuilder b; 1334 1403 private final boolean noPassword; 1404 private final boolean defaults; 1335 1405 private String key; 1336 1406 1337 SettingToXml(StringBuilder b, boolean noPassword) { 1407 SettingToXml(StringBuilder b, boolean noPassword, boolean defaults) { 1338 1408 this.b = b; 1339 1409 this.noPassword = noPassword; 1410 this.defaults = defaults; 1340 1411 } 1341 1412 1342 1413 public void setKey(String key) { 1343 1414 this.key = key; 1415 } 1416 1417 private void addTime(Setting setting) { 1418 if (defaults) { 1419 Long time = setting.getTime(); 1420 if (time == null) throw new IllegalStateException(); 1421 b.append("' time='").append(time); 1422 } 1344 1423 } 1345 1424 … … 1348 1427 if (noPassword && "osm-server.password".equals(key)) 1349 1428 return; // do not store plain password. 1350 /* don't save default values */1351 if (setting.equals(defaultsMap.get(key)))1352 return;1353 1429 b.append(" <tag key='"); 1354 1430 b.append(XmlWriter.encode(key)); 1355 b.append("' value='"); 1356 b.append(XmlWriter.encode(setting.getValue())); 1357 b.append("'/>\n"); 1431 addTime(setting); 1432 if (setting.getValue() != null) { 1433 b.append("' value='"); 1434 b.append(XmlWriter.encode(setting.getValue())); 1435 b.append("'/>\n"); 1436 } else if (defaults) { 1437 b.append("' xsi:nil='true'/>\n"); 1438 } else { 1439 throw new NullPointerException(); 1440 } 1358 1441 } 1359 1442 1360 1443 @Override 1361 1444 public void visit(ListSetting setting) { 1362 /* don't save default values */ 1363 if (setting.equals(defaultsMap.get(key))) 1364 return; 1365 b.append(" <list key='").append(XmlWriter.encode(key)).append("'>\n"); 1366 for (String s : setting.getValue()) { 1367 b.append(" <entry value='").append(XmlWriter.encode(s)).append("'/>\n"); 1368 } 1369 b.append(" </list>\n"); 1445 b.append(" <list key='").append(XmlWriter.encode(key)); 1446 addTime(setting); 1447 if (setting.getValue() != null) { 1448 b.append("'>\n"); 1449 for (String s : setting.getValue()) { 1450 b.append(" <entry value='").append(XmlWriter.encode(s)).append("'/>\n"); 1451 } 1452 b.append(" </list>\n"); 1453 } else if (defaults) { 1454 b.append("' xsi:nil='true'/>\n"); 1455 } else { 1456 throw new NullPointerException(); 1457 } 1370 1458 } 1371 1459 1372 1460 @Override 1373 1461 public void visit(ListListSetting setting) { 1374 /* don't save default values */ 1375 if (setting.equals(defaultsMap.get(key))) 1376 return; 1377 b.append(" <lists key='").append(XmlWriter.encode(key)).append("'>\n"); 1378 for (List<String> list : setting.getValue()) { 1379 b.append(" <list>\n"); 1380 for (String s : list) { 1381 b.append(" <entry value='").append(XmlWriter.encode(s)).append("'/>\n"); 1462 b.append(" <lists key='").append(XmlWriter.encode(key)); 1463 addTime(setting); 1464 if (setting.getValue() != null) { 1465 b.append("'>\n"); 1466 for (List<String> list : setting.getValue()) { 1467 b.append(" <list>\n"); 1468 for (String s : list) { 1469 b.append(" <entry value='").append(XmlWriter.encode(s)).append("'/>\n"); 1470 } 1471 b.append(" </list>\n"); 1382 1472 } 1383 b.append(" </list>\n"); 1384 } 1385 b.append(" </lists>\n"); 1473 b.append(" </lists>\n"); 1474 } else if (defaults) { 1475 b.append("' xsi:nil='true'/>\n"); 1476 } else { 1477 throw new NullPointerException(); 1478 } 1386 1479 } 1387 1480 1388 1481 @Override 1389 1482 public void visit(MapListSetting setting) { 1390 b.append(" <maps key='").append(XmlWriter.encode(key)).append("'>\n"); 1391 for (Map<String, String> struct : setting.getValue()) { 1392 b.append(" <map>\n"); 1393 for (Entry<String, String> e : struct.entrySet()) { 1394 b.append(" <tag key='").append(XmlWriter.encode(e.getKey())) 1395 .append("' value='").append(XmlWriter.encode(e.getValue())).append("'/>\n"); 1483 b.append(" <maps key='").append(XmlWriter.encode(key)); 1484 addTime(setting); 1485 if (setting.getValue() != null) { 1486 b.append("'>\n"); 1487 for (Map<String, String> struct : setting.getValue()) { 1488 b.append(" <map>\n"); 1489 for (Entry<String, String> e : struct.entrySet()) { 1490 b.append(" <tag key='").append(XmlWriter.encode(e.getKey())) 1491 .append("' value='").append(XmlWriter.encode(e.getValue())).append("'/>\n"); 1492 } 1493 b.append(" </map>\n"); 1396 1494 } 1397 b.append(" </map>\n"); 1398 } 1399 b.append(" </maps>\n"); 1495 b.append(" </maps>\n"); 1496 } else if (defaults) { 1497 b.append("' xsi:nil='true'/>\n"); 1498 } else { 1499 throw new NullPointerException(); 1500 } 1400 1501 } 1401 1502 } … … 1407 1508 */ 1408 1509 public String toXML(boolean nopass) { 1409 return toXML(settingsMap , nopass);1510 return toXML(settingsMap.entrySet(), nopass, false); 1410 1511 } 1411 1512 … … 1416 1517 * @return XML 1417 1518 */ 1418 public String toXML(Map<String, Setting<?>> settings, boolean nopass) { 1419 StringBuilder b = new StringBuilder( 1420 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<preferences xmlns=\"") 1421 .append(Main.getXMLBase()).append("/preferences-1.0\" version=\"") 1422 .append(Version.getInstance().getVersion()).append("\">\n"); 1423 SettingToXml toXml = new SettingToXml(b, nopass); 1424 for (Entry<String, Setting<?>> e : settings.entrySet()) { 1519 public String toXML(Collection<Entry<String, Setting<?>>> settings, boolean nopass, boolean defaults) { 1520 String rootElement = defaults ? "preferences-defaults" : "preferences"; 1521 StringBuilder b = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<") 1522 .append(rootElement).append(" xmlns='") 1523 .append(Main.getXMLBase()).append("/preferences-1.0'"); 1524 if (defaults) { 1525 b.append(" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"); 1526 } 1527 b.append(" version='").append(Version.getInstance().getVersion()).append("'>\n"); 1528 SettingToXml toXml = new SettingToXml(b, nopass, defaults); 1529 for (Entry<String, Setting<?>> e : settings) { 1425 1530 toXml.setKey(e.getKey()); 1426 1531 e.getValue().visit(toXml); 1427 1532 } 1428 b.append("</ preferences>\n");1533 b.append("</").append(rootElement).append(">\n"); 1429 1534 return b.toString(); 1430 1535 } -
trunk/src/org/openstreetmap/josm/data/preferences/AbstractSetting.java
r9759 r9821 12 12 public abstract class AbstractSetting<T> implements Setting<T> { 13 13 protected final T value; 14 protected Long time; 15 protected boolean isNew; 14 16 /** 15 17 * Constructs a new {@code AbstractSetting} with the given value … … 18 20 public AbstractSetting(T value) { 19 21 this.value = value; 22 this.time = null; 23 this.isNew = false; 20 24 } 21 25 … … 23 27 public T getValue() { 24 28 return value; 29 } 30 31 @Override 32 public void setTime(Long time) { 33 this.time = time; 34 } 35 36 @Override 37 public Long getTime() { 38 return this.time; 39 } 40 41 @Override 42 public void setNew(boolean isNew) { 43 this.isNew = isNew; 44 } 45 46 @Override 47 public boolean isNew() { 48 return isNew; 25 49 } 26 50 -
trunk/src/org/openstreetmap/josm/data/preferences/PreferencesReader.java
r9798 r9821 38 38 public class PreferencesReader { 39 39 40 private static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"; 41 40 42 private final SortedMap<String, Setting<?>> settings = new TreeMap<>(); 41 43 private int version = 0; 42 44 private XMLStreamReader parser; 45 46 private final boolean defaults; 47 48 /** 49 * Constructs a new {@code PreferencesReader}. 50 * @param defaults true when reading from the cache file for default preferences, 51 * false for the regular preferences config file 52 */ 53 public PreferencesReader(boolean defaults) { 54 this.defaults = defaults; 55 } 43 56 44 57 /** … … 111 124 while (true) { 112 125 if (event == XMLStreamConstants.START_ELEMENT) { 126 String topLevelElementName = defaults ? "preferences-defaults" : "preferences"; 127 String localName = parser.getLocalName(); 128 if (!topLevelElementName.equals(localName)) { 129 throw new XMLStreamException(tr("Expected element ''{0}'', but got ''{1}''", topLevelElementName, localName), parser.getLocation()); 130 } 113 131 try { 114 132 version = Integer.parseInt(parser.getAttributeValue(null, "version")); … … 138 156 switch(localName) { 139 157 case "tag": 140 settings.put(parser.getAttributeValue(null, "key"), new StringSetting(parser.getAttributeValue(null, "value"))); 158 Setting setting; 159 if (defaults && isNil()) { 160 setting = new StringSetting(null); 161 } else { 162 String value = parser.getAttributeValue(null, "value"); 163 if (value == null) { 164 throw new XMLStreamException(tr("value expected"), parser.getLocation()); 165 } 166 setting = new StringSetting(value); 167 } 168 if (defaults) { 169 setting.setTime(Math.round(Double.parseDouble(parser.getAttributeValue(null, "time")))); 170 } 171 settings.put(parser.getAttributeValue(null, "key"), setting); 141 172 jumpToEnd(); 142 173 break; 143 174 case "list": 144 case "collection":145 175 case "lists": 146 176 case "maps": … … 169 199 private void parseToplevelList() throws XMLStreamException { 170 200 String key = parser.getAttributeValue(null, "key"); 201 Long time = null; 202 if (defaults) { 203 time = Math.round(Double.parseDouble(parser.getAttributeValue(null, "time"))); 204 } 171 205 String name = parser.getLocalName(); 172 206 … … 174 208 List<List<String>> lists = null; 175 209 List<Map<String, String>> maps = null; 176 while (true) { 177 int event = parser.next(); 178 if (event == XMLStreamConstants.START_ELEMENT) { 179 String localName = parser.getLocalName(); 180 switch(localName) { 181 case "entry": 182 if (entries == null) { 183 entries = new ArrayList<>(); 210 if (defaults && isNil()) { 211 Setting setting; 212 switch (name) { 213 case "lists": 214 setting = new ListListSetting(null); 215 break; 216 case "maps": 217 setting = new MapListSetting(null); 218 break; 219 default: 220 setting = new ListSetting(null); 221 break; 222 } 223 setting.setTime(time); 224 settings.put(key, setting); 225 jumpToEnd(); 226 } else { 227 while (true) { 228 int event = parser.next(); 229 if (event == XMLStreamConstants.START_ELEMENT) { 230 String localName = parser.getLocalName(); 231 switch(localName) { 232 case "entry": 233 if (entries == null) { 234 entries = new ArrayList<>(); 235 } 236 entries.add(parser.getAttributeValue(null, "value")); 237 jumpToEnd(); 238 break; 239 case "list": 240 if (lists == null) { 241 lists = new ArrayList<>(); 242 } 243 lists.add(parseInnerList()); 244 break; 245 case "map": 246 if (maps == null) { 247 maps = new ArrayList<>(); 248 } 249 maps.add(parseMap()); 250 break; 251 default: 252 throwException("Unexpected element: "+localName); 184 253 } 185 entries.add(parser.getAttributeValue(null, "value")); 186 jumpToEnd(); 187 break; 188 case "list": 189 if (lists == null) { 190 lists = new ArrayList<>(); 191 } 192 lists.add(parseInnerList()); 193 break; 194 case "map": 195 if (maps == null) { 196 maps = new ArrayList<>(); 197 } 198 maps.add(parseMap()); 199 break; 200 default: 201 throwException("Unexpected element: "+localName); 202 } 203 } else if (event == XMLStreamConstants.END_ELEMENT) { 204 break; 205 } 206 } 207 if (entries != null) { 208 settings.put(key, new ListSetting(Collections.unmodifiableList(entries))); 209 } else if (lists != null) { 210 settings.put(key, new ListListSetting(Collections.unmodifiableList(lists))); 211 } else if (maps != null) { 212 settings.put(key, new MapListSetting(Collections.unmodifiableList(maps))); 213 } else { 214 if ("lists".equals(name)) { 215 settings.put(key, new ListListSetting(Collections.<List<String>>emptyList())); 216 } else if ("maps".equals(name)) { 217 settings.put(key, new MapListSetting(Collections.<Map<String, String>>emptyList())); 254 } else if (event == XMLStreamConstants.END_ELEMENT) { 255 break; 256 } 257 } 258 Setting setting; 259 if (entries != null) { 260 setting = new ListSetting(Collections.unmodifiableList(entries)); 261 } else if (lists != null) { 262 setting = new ListListSetting(Collections.unmodifiableList(lists)); 263 } else if (maps != null) { 264 setting = new MapListSetting(Collections.unmodifiableList(maps)); 218 265 } else { 219 settings.put(key, new ListSetting(Collections.<String>emptyList())); 220 } 266 switch (name) { 267 case "lists": 268 setting = new ListListSetting(Collections.<List<String>>emptyList()); 269 break; 270 case "maps": 271 setting = new MapListSetting(Collections.<Map<String, String>>emptyList()); 272 break; 273 default: 274 setting = new ListSetting(Collections.<String>emptyList()); 275 break; 276 } 277 } 278 if (defaults) { 279 setting.setTime(time); 280 } 281 settings.put(key, setting); 221 282 } 222 283 } … … 258 319 } 259 320 321 /** 322 * Check if the current element is nil (meaning the value of the setting is null). 323 * @return true, if the current element is nil 324 * @see https://msdn.microsoft.com/en-us/library/2b314yt2(v=vs.85).aspx 325 */ 326 private boolean isNil() { 327 String nil = parser.getAttributeValue(XSI_NS, "nil"); 328 return "true".equals(nil) || "1".equals(nil); 329 } 330 331 /** 332 * Throw RuntimeException with line and column number. 333 * 334 * Only use this for errors that should not be possible after schema validation. 335 * @param msg the error message 336 */ 260 337 private void throwException(String msg) { 261 338 throw new RuntimeException(msg + tr(" (at line {0}, column {1})", -
trunk/src/org/openstreetmap/josm/data/preferences/Setting.java
r9759 r9821 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.preferences; 3 4 import org.openstreetmap.josm.data.Preferences; 3 5 4 6 /** … … 46 48 */ 47 49 Setting<T> getNullInstance(); 50 51 /** 52 * Set the time for this setting. 53 * 54 * For default preferences. They are saved in a cache file. Keeping the 55 * time allows to discard very old default settings. 56 * @param time the time in seconds since epoch 57 */ 58 void setTime(Long time); 59 60 /** 61 * Get the time for this setting. 62 * @return the time for this setting 63 * @see #setTime(java.lang.Long) 64 */ 65 Long getTime(); 66 67 /** 68 * Mark setting as new. 69 * 70 * For default preferences. A setting is marked as new, if it has been seen 71 * in the current session. 72 * Methods like {@link Preferences#get(java.lang.String, java.lang.String)}, 73 * can be called from different parts of the code with the same key. In this case, 74 * the supplied default value must match. However, this is only an error if the mismatching 75 * default value has been seen in the same session (and not loaded from cache). 76 * @param isNew true, if it is new 77 */ 78 void setNew(boolean isNew); 79 80 /** 81 * Return if the setting has been marked as new. 82 * @return true, if the setting has been marked as new 83 * @see #setNew(boolean) 84 */ 85 boolean isNew(); 48 86 }
Note:
See TracChangeset
for help on using the changeset viewer.