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

Last change on this file since 2641 was 2641, checked in by Gubaer, 14 years ago

new: supports system defined proxies if JOSM is started with -Djava.net.useSystemProxies=true
fixed #1641: JOSM doesn't allow for setting HTTP proxy user/password distrinct from OSM server user/password
fixed #2865: SOCKS Proxy Support
fixed #4182: Proxy Authentication

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