source: josm/trunk/src/org/openstreetmap/josm/data/Preferences.java@ 2370

Last change on this file since 2370 was 2370, checked in by stoecker, 14 years ago

reworked user agent string to match established format

  • Property svn:eol-style set to native
File size: 24.6 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.io.BufferedReader;
8import java.io.File;
9import java.io.FileInputStream;
10import java.io.FileOutputStream;
11import java.io.IOException;
12import java.io.InputStreamReader;
13import java.io.OutputStreamWriter;
14import java.io.PrintWriter;
15import java.nio.channels.FileChannel;
16import java.util.ArrayList;
17import java.util.Arrays;
18import java.util.Collection;
19import java.util.Collections;
20import java.util.LinkedList;
21import java.util.Map;
22import java.util.Properties;
23import java.util.SortedMap;
24import java.util.TreeMap;
25import java.util.Map.Entry;
26import java.util.regex.Matcher;
27import java.util.regex.Pattern;
28
29import javax.swing.JOptionPane;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.gui.preferences.ProxyPreferences;
33import org.openstreetmap.josm.tools.ColorHelper;
34
35/**
36 * This class holds all preferences for JOSM.
37 *
38 * Other classes can register their beloved properties here. All properties will be
39 * saved upon set-access.
40 *
41 * @author imi
42 */
43public class Preferences {
44
45 /**
46 * Internal storage for the preference directory.
47 * Do not access this variable directly!
48 * @see #getPreferencesDirFile()
49 */
50 private File preferencesDirFile = null;
51
52 public static interface PreferenceChangedListener {
53 void preferenceChanged(String key, String newValue);
54 }
55
56 /**
57 * Class holding one bookmarkentry.
58 * @author imi
59 */
60 public static class Bookmark implements Comparable<Bookmark> {
61 private String name;
62 private Bounds area;
63
64 public Bookmark() {
65 area = null;
66 name = null;
67 }
68
69 public Bookmark(Bounds area) {
70 this.area = area;
71 }
72
73 @Override public String toString() {
74 return name;
75 }
76
77 public int compareTo(Bookmark b) {
78 return name.toLowerCase().compareTo(b.name.toLowerCase());
79 }
80
81 public Bounds getArea() {
82 return area;
83 }
84
85 public String getName() {
86 return name;
87 }
88
89 public void setName(String name) {
90 this.name = name;
91 }
92
93 public void setArea(Bounds area) {
94 this.area = area;
95 }
96 }
97
98 public final ArrayList<PreferenceChangedListener> listener = new ArrayList<PreferenceChangedListener>();
99
100 /**
101 * Map the property name to the property object.
102 */
103 protected final SortedMap<String, String> properties = new TreeMap<String, String>();
104 protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
105
106 /**
107 * Override some values on read. This is intended to be used for technology previews
108 * where we want to temporarily modify things without changing the user's preferences
109 * file.
110 */
111 protected static final SortedMap<String, String> override = new TreeMap<String, String>();
112 static {
113 //override.put("osm-server.version", "0.5");
114 //override.put("osm-server.additional-versions", "");
115 //override.put("osm-server.url", "http://openstreetmap.gryph.de/api");
116 //override.put("plugins", null);
117 }
118
119 /**
120 * Return the location of the user defined preferences file
121 */
122 public String getPreferencesDir() {
123 final String path = getPreferencesDirFile().getPath();
124 if (path.endsWith(File.separator))
125 return path;
126 return path + File.separator;
127 }
128
129 public File getPreferencesDirFile() {
130 if (preferencesDirFile != null)
131 return preferencesDirFile;
132 String path;
133 path = System.getProperty("josm.home");
134 if (path != null) {
135 preferencesDirFile = new File(path);
136 } else {
137 path = System.getenv("APPDATA");
138 if (path != null) {
139 preferencesDirFile = new File(path, "JOSM");
140 } else {
141 preferencesDirFile = new File(System.getProperty("user.home"), ".josm");
142 }
143 }
144 return preferencesDirFile;
145 }
146
147 public File getPreferenceFile() {
148 return new File(getPreferencesDirFile(), "preferences");
149 }
150
151 public File getPluginsDirFile() {
152 return new File(getPreferencesDirFile(), "plugins");
153 }
154
155 /**
156 * @return A list of all existing directories where resources could be stored.
157 */
158 public Collection<String> getAllPossiblePreferenceDirs() {
159 LinkedList<String> locations = new LinkedList<String>();
160 locations.add(Main.pref.getPreferencesDir());
161 String s;
162 if ((s = System.getenv("JOSM_RESOURCES")) != null) {
163 if (!s.endsWith(File.separator)) {
164 s = s + File.separator;
165 }
166 locations.add(s);
167 }
168 if ((s = System.getProperty("josm.resources")) != null) {
169 if (!s.endsWith(File.separator)) {
170 s = s + File.separator;
171 }
172 locations.add(s);
173 }
174 String appdata = System.getenv("APPDATA");
175 if (System.getenv("ALLUSERSPROFILE") != null && appdata != null
176 && appdata.lastIndexOf(File.separator) != -1) {
177 appdata = appdata.substring(appdata.lastIndexOf(File.separator));
178 locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
179 appdata), "JOSM").getPath());
180 }
181 locations.add("/usr/local/share/josm/");
182 locations.add("/usr/local/lib/josm/");
183 locations.add("/usr/share/josm/");
184 locations.add("/usr/lib/josm/");
185 return locations;
186 }
187
188 synchronized public boolean hasKey(final String key) {
189 return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key);
190 }
191
192 synchronized public String get(final String key) {
193 putDefault(key, null);
194 if (override.containsKey(key))
195 return override.get(key);
196 if (!properties.containsKey(key))
197 return "";
198 return properties.get(key);
199 }
200
201 synchronized public String get(final String key, final String def) {
202 putDefault(key, def);
203 if (override.containsKey(key))
204 return override.get(key);
205 final String prop = properties.get(key);
206 if (prop == null || prop.equals(""))
207 return def;
208 return prop;
209 }
210
211 synchronized public Map<String, String> getAllPrefix(final String prefix) {
212 final Map<String,String> all = new TreeMap<String,String>();
213 for (final Entry<String,String> e : properties.entrySet())
214 if (e.getKey().startsWith(prefix)) {
215 all.put(e.getKey(), e.getValue());
216 }
217 for (final Entry<String,String> e : override.entrySet())
218 if (e.getKey().startsWith(prefix))
219 if (e.getValue() == null) {
220 all.remove(e.getKey());
221 } else {
222 all.put(e.getKey(), e.getValue());
223 }
224 return all;
225 }
226
227 synchronized public TreeMap<String, String> getAllColors() {
228 final TreeMap<String,String> all = new TreeMap<String,String>();
229 for (final Entry<String,String> e : defaults.entrySet())
230 if (e.getKey().startsWith("color.") && e.getValue() != null) {
231 all.put(e.getKey().substring(6), e.getValue());
232 }
233 for (final Entry<String,String> e : properties.entrySet())
234 if (e.getKey().startsWith("color.")) {
235 all.put(e.getKey().substring(6), e.getValue());
236 }
237 for (final Entry<String,String> e : override.entrySet())
238 if (e.getKey().startsWith("color."))
239 if (e.getValue() == null) {
240 all.remove(e.getKey().substring(6));
241 } else {
242 all.put(e.getKey().substring(6), e.getValue());
243 }
244 return all;
245 }
246
247 synchronized public Map<String, String> getDefaults() {
248 return defaults;
249 }
250
251 synchronized public void putDefault(final String key, final String def) {
252 if(!defaults.containsKey(key) || defaults.get(key) == null) {
253 defaults.put(key, def);
254 } else if(def != null && !defaults.get(key).equals(def)) {
255 System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
256 }
257 }
258
259 synchronized public boolean getBoolean(final String key) {
260 putDefault(key, null);
261 if (override.containsKey(key))
262 return override.get(key) == null ? false : Boolean.parseBoolean(override.get(key));
263 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
264 }
265
266 synchronized public boolean getBoolean(final String key, final boolean def) {
267 putDefault(key, Boolean.toString(def));
268 if (override.containsKey(key))
269 return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key));
270 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
271 }
272
273 synchronized public boolean put(final String key, String value) {
274 String oldvalue = properties.get(key);
275 if(value != null && value.length() == 0) {
276 value = null;
277 }
278 if(!((oldvalue == null && (value == null || value.equals(defaults.get(key))))
279 || (value != null && oldvalue != null && oldvalue.equals(value))))
280 {
281 if (value == null) {
282 properties.remove(key);
283 } else {
284 properties.put(key, value);
285 }
286 try {
287 save();
288 } catch(IOException e){
289 System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
290 }
291 firePreferenceChanged(key, value);
292 return true;
293 }
294 return false;
295 }
296
297 synchronized public boolean put(final String key, final boolean value) {
298 return put(key, Boolean.toString(value));
299 }
300
301 synchronized public boolean putInteger(final String key, final Integer value) {
302 return put(key, Integer.toString(value));
303 }
304
305 synchronized public boolean putDouble(final String key, final Double value) {
306 return put(key, Double.toString(value));
307 }
308
309 synchronized public boolean putLong(final String key, final Long value) {
310 return put(key, Long.toString(value));
311 }
312
313 private final void firePreferenceChanged(final String key, final String value) {
314 for (final PreferenceChangedListener l : listener) {
315 l.preferenceChanged(key, value);
316 }
317 }
318
319 /**
320 * Called after every put. In case of a problem, do nothing but output the error
321 * in log.
322 */
323 public void save() throws IOException {
324 /* currently unused, but may help to fix configuration issues in future */
325 properties.put("josm.version", Version.getInstance().getVersionString());
326
327 updateSystemProperties();
328 File prefFile = new File(getPreferencesDirFile(), "preferences");
329
330 // Backup old preferences if there are old preferences
331 if(prefFile.exists()) {
332 copyFile(prefFile, new File(prefFile + "_backup"));
333 }
334
335 final PrintWriter out = new PrintWriter(new OutputStreamWriter(
336 new FileOutputStream(prefFile + "_tmp"), "utf-8"), false);
337 for (final Entry<String, String> e : properties.entrySet()) {
338 String s = defaults.get(e.getKey());
339 /* don't save default values */
340 if(s == null || !s.equals(e.getValue())) {
341 out.println(e.getKey() + "=" + e.getValue());
342 }
343 }
344 out.close();
345
346 File tmpFile = new File(prefFile + "_tmp");
347 copyFile(tmpFile, prefFile);
348 tmpFile.delete();
349 }
350
351 /**
352 * Simple file copy function that will overwrite the target file
353 * Taken from http://www.rgagnon.com/javadetails/java-0064.html (CC-NC-BY-SA)
354 * @param in
355 * @param out
356 * @throws IOException
357 */
358 public static void copyFile(File in, File out) throws IOException {
359 FileChannel inChannel = new FileInputStream(in).getChannel();
360 FileChannel outChannel = new FileOutputStream(out).getChannel();
361 try {
362 inChannel.transferTo(0, inChannel.size(),
363 outChannel);
364 }
365 catch (IOException e) {
366 throw e;
367 }
368 finally {
369 if (inChannel != null) {
370 inChannel.close();
371 }
372 if (outChannel != null) {
373 outChannel.close();
374 }
375 }
376 }
377
378
379 public void load() throws IOException {
380 properties.clear();
381 final BufferedReader in = new BufferedReader(new InputStreamReader(
382 new FileInputStream(getPreferencesDir()+"preferences"), "utf-8"));
383 int lineNumber = 0;
384 ArrayList<Integer> errLines = new ArrayList<Integer>();
385 for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) {
386 final int i = line.indexOf('=');
387 if (i == -1 || i == 0) {
388 errLines.add(lineNumber);
389 continue;
390 }
391 properties.put(line.substring(0,i), line.substring(i+1));
392 }
393 if (!errLines.isEmpty())
394 throw new IOException(tr("Malformed config file at lines {0}", errLines));
395 updateSystemProperties();
396 }
397
398 public void init(boolean reset){
399 // get the preferences.
400 File prefDir = getPreferencesDirFile();
401 if (prefDir.exists()) {
402 if(!prefDir.isDirectory()) {
403 System.err.println(tr("Warning: Failed to initialize preferences. Preference directory ''{0}'' isn't a directory.", prefDir.getAbsoluteFile()));
404 JOptionPane.showMessageDialog(
405 Main.parent,
406 tr("<html>Failed to initialize preferences.<br>Preference directory ''{0}'' isn't a directory.</html>", prefDir.getAbsoluteFile()),
407 tr("Error"),
408 JOptionPane.ERROR_MESSAGE
409 );
410 return;
411 }
412 } else {
413 if (! prefDir.mkdirs()) {
414 System.err.println(tr("Warning: Failed to initialize preferences. Failed to create missing preference directory: {0}", prefDir.getAbsoluteFile()));
415 JOptionPane.showMessageDialog(
416 Main.parent,
417 tr("<html>Failed to initialize preferences.<br>Failed to create missing preference directory: {0}</html>",prefDir.getAbsoluteFile()),
418 tr("Error"),
419 JOptionPane.ERROR_MESSAGE
420 );
421 return;
422 }
423 }
424
425 File preferenceFile = getPreferenceFile();
426 try {
427 if (!preferenceFile.exists()) {
428 System.out.println(tr("Warning: Missing preference file ''{0}''. Creating a default preference file.", preferenceFile.getAbsoluteFile()));
429 resetToDefault();
430 save();
431 } else if (reset) {
432 System.out.println(tr("Warning: Replacing existing preference file ''{0}'' with default preference file.", preferenceFile.getAbsoluteFile()));
433 resetToDefault();
434 save();
435 }
436 } catch(IOException e) {
437 e.printStackTrace();
438 JOptionPane.showMessageDialog(
439 Main.parent,
440 tr("<html>Failed to initialize preferences.<br>Failed to reset preference file to default: {0}</html>",getPreferenceFile().getAbsoluteFile()),
441 tr("Error"),
442 JOptionPane.ERROR_MESSAGE
443 );
444 return;
445 }
446 try {
447 load();
448 } catch (IOException e) {
449 e.printStackTrace();
450 File backupFile = new File(prefDir,"preferences.bak");
451 JOptionPane.showMessageDialog(
452 Main.parent,
453 tr("<html>Preferences file had errors.<br> Making backup of old one to <br>{0}<br> and creating a new default preference file.</html>", backupFile.getAbsoluteFile()),
454 tr("Error"),
455 JOptionPane.ERROR_MESSAGE
456 );
457 preferenceFile.renameTo(backupFile);
458 try {
459 resetToDefault();
460 save();
461 } catch(IOException e1) {
462 e1.printStackTrace();
463 System.err.println(tr("Warning: Failed to initialize preferences.Failed to reset preference file to default: {0}", getPreferenceFile()));
464 }
465 }
466 }
467
468 public final void resetToDefault(){
469 properties.clear();
470 if (System.getProperty("os.name").toUpperCase().indexOf("WINDOWS") == -1) {
471 put("laf", "javax.swing.plaf.metal.MetalLookAndFeel");
472 } else {
473 put("laf", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
474 }
475 }
476
477 public File getBookmarksFile() {
478 return new File(getPreferencesDir(),"bookmarks");
479 }
480
481 public Collection<Bookmark> loadBookmarks() throws IOException {
482 File bookmarkFile = getBookmarksFile();
483 if (!bookmarkFile.exists()) {
484 bookmarkFile.createNewFile();
485 }
486 BufferedReader in = new BufferedReader(new InputStreamReader(
487 new FileInputStream(bookmarkFile), "utf-8"));
488
489 LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
490 for (String line = in.readLine(); line != null; line = in.readLine()) {
491 // FIXME: legacy code using ',' sign, should be \u001e only
492 Matcher m = Pattern.compile("^(.+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)$").matcher(line);
493 if (!m.matches() || m.groupCount() != 5) {
494 System.err.println(tr("Error: Unexpected line ''{0}'' in bookmark file ''{1}''",line, bookmarkFile.toString()));
495 continue;
496 }
497 Bookmark b = new Bookmark();
498 b.setName(m.group(1));
499 double[] values= new double[4];
500 for (int i = 0; i < 4; ++i) {
501 try {
502 values[i] = Double.parseDouble(m.group(i+2));
503 } catch(NumberFormatException e) {
504 System.err.println(tr("Error: Illegal double value ''{0}'' on line ''{1}'' in bookmark file ''{2}''",m.group(i+2),line, bookmarkFile.toString()));
505 continue;
506 }
507 }
508 b.setArea(new Bounds(values));
509 bookmarks.add(b);
510 }
511 in.close();
512 Collections.sort(bookmarks);
513 return bookmarks;
514 }
515
516 public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException {
517 File bookmarkFile = new File(Main.pref.getPreferencesDir()+"bookmarks");
518 if (!bookmarkFile.exists()) {
519 bookmarkFile.createNewFile();
520 }
521 PrintWriter out = new PrintWriter(new OutputStreamWriter(
522 new FileOutputStream(bookmarkFile), "utf-8"));
523 for (Bookmark b : bookmarks) {
524 out.print(b.getName()+ "\u001e");
525 Bounds area = b.getArea();
526 out.print(area.getMin().lat() +"\u001e");
527 out.print(area.getMin().lon() +"\u001e");
528 out.print(area.getMax().lat() +"\u001e");
529 out.print(area.getMax().lon());
530 out.println();
531 }
532 out.close();
533 }
534
535 /**
536 * Convenience method for accessing colour preferences.
537 *
538 * @param colName name of the colour
539 * @param def default value
540 * @return a Color object for the configured colour, or the default value if none configured.
541 */
542 synchronized public Color getColor(String colName, Color def) {
543 return getColor(colName, null, def);
544 }
545
546 /**
547 * Convenience method for accessing colour preferences.
548 *
549 * @param colName name of the colour
550 * @param specName name of the special colour settings
551 * @param def default value
552 * @return a Color object for the configured colour, or the default value if none configured.
553 */
554 synchronized public Color getColor(String colName, String specName, Color def) {
555 putDefault("color."+colName, ColorHelper.color2html(def));
556 String colStr = specName != null ? get("color."+specName) : "";
557 if(colStr.equals("")) {
558 colStr = get("color."+colName);
559 }
560 return colStr.equals("") ? def : ColorHelper.html2color(colStr);
561 }
562
563 synchronized public Color getDefaultColor(String colName) {
564 String colStr = defaults.get("color."+colName);
565 return colStr.equals("") ? null : ColorHelper.html2color(colStr);
566 }
567
568 synchronized public boolean putColor(String colName, Color val) {
569 return put("color."+colName, val != null ? ColorHelper.color2html(val) : null);
570 }
571
572 synchronized public int getInteger(String key, int def) {
573 putDefault(key, Integer.toString(def));
574 String v = get(key);
575 if(null == v)
576 return def;
577
578 try {
579 return Integer.parseInt(v);
580 } catch(NumberFormatException e) {
581 // fall out
582 }
583 return def;
584 }
585
586 synchronized public long getLong(String key, long def) {
587 putDefault(key, Long.toString(def));
588 String v = get(key);
589 if(null == v)
590 return def;
591
592 try {
593 return Long.parseLong(v);
594 } catch(NumberFormatException e) {
595 // fall out
596 }
597 return def;
598 }
599
600 synchronized public double getDouble(String key, double def) {
601 putDefault(key, Double.toString(def));
602 String v = get(key);
603 if(null == v)
604 return def;
605
606 try {
607 return Double.parseDouble(v);
608 } catch(NumberFormatException e) {
609 // fall out
610 }
611 return def;
612 }
613
614 synchronized public double getDouble(String key, String def) {
615 putDefault(key, def);
616 String v = get(key);
617 if(v != null && v.length() != 0) {
618 try { return Double.parseDouble(v); } catch(NumberFormatException e) {}
619 }
620 try { return Double.parseDouble(def); } catch(NumberFormatException e) {}
621 return 0.0;
622 }
623
624 synchronized public Collection<String> getCollection(String key, Collection<String> def) {
625 String s = get(key);
626 if(def != null)
627 {
628 String d = null;
629 for(String a : def)
630 {
631 if(d != null) {
632 d += "\u001e" + a;
633 } else {
634 d = a;
635 }
636 }
637 putDefault(key, d);
638 }
639 if(s != null && s.length() != 0)
640 {
641 if(s.indexOf("\u001e") < 0) /* FIXME: legacy code, remove later */
642 {
643 String r =s;
644 if(r.indexOf("§§§") > 0) {
645 r = r.replaceAll("§§§","\u001e");
646 } else {
647 r = r.replace(';','\u001e');
648 }
649 if(!r.equals(s)) /* save the converted string */
650 {
651 put(key,r);
652 s = r;
653 }
654 }
655 return Arrays.asList(s.split("\u001e"));
656 }
657 return def;
658 }
659 synchronized public void removeFromCollection(String key, String value) {
660 ArrayList<String> a = new ArrayList<String>(getCollection(key, null));
661 if(a != null)
662 {
663 a.remove(value);
664 putCollection(key, a);
665 }
666 }
667 synchronized public boolean putCollection(String key, Collection<String> val) {
668 String s = null;
669 if(val != null)
670 {
671 for(String a : val)
672 {
673 if(s != null) {
674 s += "\u001e" + a;
675 } else {
676 s = a;
677 }
678 }
679 }
680 return put(key, s);
681 }
682
683 /**
684 * Updates system properties with the current values in the preferences.
685 *
686 */
687 public void updateSystemProperties() {
688 Properties sysProp = System.getProperties();
689 if (getBoolean(ProxyPreferences.PROXY_ENABLE)) {
690 sysProp.put("proxySet", "true");
691 sysProp.put("http.proxyHost", get(ProxyPreferences.PROXY_HOST));
692 sysProp.put("proxyPort", get(ProxyPreferences.PROXY_PORT));
693 if (!getBoolean(ProxyPreferences.PROXY_ANONYMOUS)) {
694 sysProp.put("proxyUser", get(ProxyPreferences.PROXY_USER));
695 sysProp.put("proxyPassword", get(ProxyPreferences.PROXY_PASS));
696 }
697
698 }
699 sysProp.put("http.agent", Version.getInstance().getAgentString());
700 System.setProperties(sysProp);
701 }
702}
Note: See TracBrowser for help on using the repository browser.