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

Last change on this file since 2224 was 2224, checked in by stoecker, 15 years ago

see #3550 - patch by bastiK - allow resizing right handside dialogs, updated i18n

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