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

Last change on this file since 2645 was 2645, checked in by jttt, 14 years ago

SearchCompiler refactoring, use search pattern for OsmPrimitive.hasDirectionKeys(), added toString() and type to dataset events

  • Property svn:eol-style set to native
File size: 25.7 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 public boolean isCollection(String key, boolean def) {
673 String s = get(key);
674 if (s != null && s.length() != 0)
675 return s.indexOf("\u001e") >= 0 || s.indexOf("§§§") >= 0;
676 else
677 return def;
678 }
679
680 synchronized public Collection<String> getCollection(String key, Collection<String> def) {
681 String s = get(key);
682 if(def != null)
683 {
684 String d = null;
685 for(String a : def)
686 {
687 if(d != null) {
688 d += "\u001e" + a;
689 } else {
690 d = a;
691 }
692 }
693 putDefault(key, d);
694 }
695 if(s != null && s.length() != 0)
696 {
697 if(s.indexOf("\u001e") < 0) /* FIXME: legacy code, remove later */
698 {
699 String r =s;
700 if(r.indexOf("§§§") > 0) {
701 r = r.replaceAll("§§§","\u001e");
702 } else {
703 r = r.replace(';','\u001e');
704 }
705 if(!r.equals(s)) /* save the converted string */
706 {
707 put(key,r);
708 s = r;
709 }
710 }
711 return Arrays.asList(s.split("\u001e"));
712 }
713 return def;
714 }
715 synchronized public void removeFromCollection(String key, String value) {
716 List<String> a = new ArrayList<String>(getCollection(key, null));
717 a.remove(value);
718 putCollection(key, a);
719 }
720 synchronized public boolean putCollection(String key, Collection<String> val) {
721 String s = null;
722 if(val != null)
723 {
724 for(String a : val)
725 {
726 if(s != null) {
727 s += "\u001e" + a;
728 } else {
729 s = a;
730 }
731 }
732 }
733 return put(key, s);
734 }
735
736 /**
737 * Updates system properties with the current values in the preferences.
738 *
739 */
740 public void updateSystemProperties() {
741 Properties sysProp = System.getProperties();
742 sysProp.put("http.agent", Version.getInstance().getAgentString());
743 System.setProperties(sysProp);
744 }
745}
Note: See TracBrowser for help on using the repository browser.