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

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

Cleanup in download logic (less global, more encapsulation)

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