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

Last change on this file since 2347 was 2334, checked in by Gubaer, 15 years ago

Improved bookmark tab in download dialog

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