// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.data;
import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* GWT
*
* TODO
* stub - for now, some get...(...) methods have dummy implementations
* and simply return the default value
*
* changelog
* 'put' does not save the prefs permanently
*/
/**
* This class holds all preferences for JOSM.
*
* Other classes can register their beloved properties here. All properties will be
* saved upon set-access.
*
* Each property is a simple key=value pair of Strings.
* In addition, each key has a unique default value that is set when the value is first
* accessed using one of the get...() methods. You can use the same preference
* key in different parts of the code, but the default value must be the same
* everywhere. null is a legitimate default value.
*
* At the moment, there is no such thing as an empty value.
* If you put "" or null as value, the property is removed.
*
* @author imi
*/
public class Preferences {
// //static private final Logger logger = Logger.getLogger(Preferences.class.getName());
//
// /**
// * Internal storage for the preference directory.
// * Do not access this variable directly!
// * @see #getPreferencesDirFile()
// */
// private File preferencesDirFile = null;
/**
* Map the property name to the property object. Does not contain null or "" values.
*/
protected final SortedMap<String, String> properties = new TreeMap<String, String>();
protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
public interface PreferenceChangeEvent{
String getKey();
String getOldValue();
String getNewValue();
}
public interface PreferenceChangedListener {
void preferenceChanged(PreferenceChangeEvent e);
}
private static class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
private final String key;
private final String oldValue;
private final String newValue;
public DefaultPreferenceChangeEvent(String key, String oldValue, String newValue) {
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
}
public String getKey() {
return key;
}
public String getOldValue() {
return oldValue;
}
public String getNewValue() {
return newValue;
}
}
// public interface ColorKey {
// String getColorName();
// String getSpecialName();
// Color getDefault();
// }
private final CopyOnWriteArrayList<PreferenceChangedListener> listeners = new CopyOnWriteArrayList<PreferenceChangedListener>();
public void addPreferenceChangeListener(PreferenceChangedListener listener) {
if (listener != null) {
listeners.addIfAbsent(listener);
}
}
public void removePreferenceChangeListener(PreferenceChangedListener listener) {
listeners.remove(listener);
}
protected void firePreferenceChanged(String key, String oldValue, String newValue) {
PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue);
for (PreferenceChangedListener l : listeners) {
l.preferenceChanged(evt);
}
}
// /**
// * Return the location of the user defined preferences file
// */
// public String getPreferencesDir() {
// final String path = getPreferencesDirFile().getPath();
// if (path.endsWith(File.separator))
// return path;
// return path + File.separator;
// }
//
// public File getPreferencesDirFile() {
// if (preferencesDirFile != null)
// return preferencesDirFile;
// String path;
// path = System.getProperty("josm.home");
// if (path != null) {
// preferencesDirFile = new File(path);
// } else {
// path = System.getenv("APPDATA");
// if (path != null) {
// preferencesDirFile = new File(path, "JOSM");
// } else {
// preferencesDirFile = new File(System.getProperty("user.home"), ".josm");
// }
// }
// return preferencesDirFile;
// }
//
// public File getPreferenceFile() {
// return new File(getPreferencesDirFile(), "preferences");
// }
//
// public File getPluginsDirectory() {
// return new File(getPreferencesDirFile(), "plugins");
// }
//
// /**
// * @return A list of all existing directories where resources could be stored.
// */
// public Collection<String> getAllPossiblePreferenceDirs() {
// LinkedList<String> locations = new LinkedList<String>();
// locations.add(Main.pref.getPreferencesDir());
// String s;
// if ((s = System.getenv("JOSM_RESOURCES")) != null) {
// if (!s.endsWith(File.separator)) {
// s = s + File.separator;
// }
// locations.add(s);
// }
// if ((s = System.getProperty("josm.resources")) != null) {
// if (!s.endsWith(File.separator)) {
// s = s + File.separator;
// }
// locations.add(s);
// }
// String appdata = System.getenv("APPDATA");
// if (System.getenv("ALLUSERSPROFILE") != null && appdata != null
// && appdata.lastIndexOf(File.separator) != -1) {
// appdata = appdata.substring(appdata.lastIndexOf(File.separator));
// locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
// appdata), "JOSM").getPath());
// }
// locations.add("/usr/local/share/josm/");
// locations.add("/usr/local/lib/josm/");
// locations.add("/usr/share/josm/");
// locations.add("/usr/lib/josm/");
// return locations;
// }
synchronized public boolean hasKey(final String key) {
return properties.containsKey(key);
}
/**
* Get settings value for a certain key.
* @param key the identifier for the setting
* @return "" if there is nothing set for the preference key,
* the corresponding value otherwise. The result is not null.
*/
synchronized public String get(final String key) {
putDefault(key, null);
if (!properties.containsKey(key))
return "";
return properties.get(key);
}
/**
* Get settings value for a certain key and provide default a value.
* @param key the identifier for the setting
* @param def the default value. For each call of get() with a given key, the
* default value must be the same.
* @return the corresponding value if the property has been set before,
* def otherwise
*/
synchronized public String get(final String key, final String def) {
putDefault(key, def);
final String prop = properties.get(key);
if (prop == null || prop.equals(""))
return def;
return prop;
}
//
// synchronized public Map<String, String> getAllPrefix(final String prefix) {
// final Map<String,String> all = new TreeMap<String,String>();
// for (final Entry<String,String> e : properties.entrySet()) {
// if (e.getKey().startsWith(prefix)) {
// all.put(e.getKey(), e.getValue());
// }
// }
// return all;
// }
//
// synchronized private Map<String, String> getAllPrefixDefault(final String prefix) {
// final Map<String,String> all = new TreeMap<String,String>();
// for (final Entry<String,String> e : defaults.entrySet()) {
// if (e.getKey().startsWith(prefix)) {
// all.put(e.getKey(), e.getValue());
// }
// }
// return all;
// }
//
// synchronized public TreeMap<String, String> getAllColors() {
// final TreeMap<String,String> all = new TreeMap<String,String>();
// for (final Entry<String,String> e : defaults.entrySet()) {
// if (e.getKey().startsWith("color.") && e.getValue() != null) {
// all.put(e.getKey().substring(6), e.getValue());
// }
// }
// for (final Entry<String,String> e : properties.entrySet()) {
// if (e.getKey().startsWith("color.")) {
// all.put(e.getKey().substring(6), e.getValue());
// }
// }
// return all;
// }
//
// synchronized public Map<String, String> getDefaults() {
// return defaults;
// }
synchronized public void putDefault(final String key, final String def) {
if(!defaults.containsKey(key) || defaults.get(key) == null) {
defaults.put(key, def);
} else if(def != null && !defaults.get(key).equals(def)) {
System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
}
}
synchronized public boolean getBoolean(final String key) {
return false; // GWT fix
// putDefault(key, null);
// return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
}
synchronized public boolean getBoolean(final String key, final boolean def) {
return def; // GWT fix
// putDefault(key, Boolean.toString(def));
// return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
}
/**
* Set a value for a certain setting. The changed setting is saved
* to the preference file immediately. Due to caching mechanisms on modern
* operating systems and hardware, this shouldn't be a performance problem.
* @param key the unique identifier for the setting
* @param value the value of the setting. Can be null or "" wich both removes
* the key-value entry.
* @return if true, something has changed (i.e. value is different than before)
*/
public boolean put(final String key, String value) {
boolean changed = false;
String oldValue = null;
synchronized (this) {
oldValue = properties.get(key);
if(value != null && value.length() == 0) {
value = null;
}
// value is the same as before - no need to save anything
boolean equalValue = oldValue != null && oldValue.equals(value);
// The setting was previously unset and we are supposed to put a
// value that equals the default value. This is not necessary because
// the default value is the same throughout josm. In addition we like
// to have the possibility to change the default value from version
// to version, which would not work if we wrote it to the preference file.
boolean unsetIsDefault = oldValue == null && (value == null || value.equals(defaults.get(key)));
if (!(equalValue || unsetIsDefault)) {
if (value == null) {
properties.remove(key);
} else {
properties.put(key, value);
}
// try {
// save();
// } catch(IOException e){
// System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
// }
changed = true;
}
}
if (changed) {
// Call outside of synchronized section in case some listener wait for other thread that wait for preference lock
firePreferenceChanged(key, oldValue, value);
}
return changed;
}
public boolean put(final String key, final boolean value) {
return put(key, Boolean.toString(value));
}
public boolean putInteger(final String key, final Integer value) {
return put(key, Integer.toString(value));
}
public boolean putDouble(final String key, final Double value) {
return put(key, Double.toString(value));
}
public boolean putLong(final String key, final Long value) {
return put(key, Long.toString(value));
}
// /**
// * Called after every put. In case of a problem, do nothing but output the error
// * in log.
// */
// public void save() throws IOException {
// /* currently unused, but may help to fix configuration issues in future */
// putInteger("josm.version", Version.getInstance().getVersion());
//
// updateSystemProperties();
// if(Main.applet)
// return;
// File prefFile = new File(getPreferencesDirFile(), "preferences");
//
// // Backup old preferences if there are old preferences
// if(prefFile.exists()) {
// copyFile(prefFile, new File(prefFile + "_backup"));
// }
//
// final PrintWriter out = new PrintWriter(new OutputStreamWriter(
// new FileOutputStream(prefFile + "_tmp"), "utf-8"), false);
// for (final Entry<String, String> e : properties.entrySet()) {
// String s = defaults.get(e.getKey());
// /* don't save default values */
// if(s == null || !s.equals(e.getValue())) {
// out.println(e.getKey() + "=" + e.getValue());
// }
// }
// out.close();
//
// File tmpFile = new File(prefFile + "_tmp");
// copyFile(tmpFile, prefFile);
// tmpFile.delete();
// }
//
// /**
// * Simple file copy function that will overwrite the target file
// * Taken from http://www.rgagnon.com/javadetails/java-0064.html (CC-NC-BY-SA)
// * @param in
// * @param out
// * @throws IOException
// */
// public static void copyFile(File in, File out) throws IOException {
// FileChannel inChannel = new FileInputStream(in).getChannel();
// FileChannel outChannel = new FileOutputStream(out).getChannel();
// try {
// inChannel.transferTo(0, inChannel.size(),
// outChannel);
// }
// catch (IOException e) {
// throw e;
// }
// finally {
// if (inChannel != null) {
// inChannel.close();
// }
// if (outChannel != null) {
// outChannel.close();
// }
// }
// }
//
// public void load() throws IOException {
// properties.clear();
// if(!Main.applet) {
// final BufferedReader in = new BufferedReader(new InputStreamReader(
// new FileInputStream(getPreferencesDir()+"preferences"), "utf-8"));
// int lineNumber = 0;
// ArrayList<Integer> errLines = new ArrayList<Integer>();
// for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) {
// final int i = line.indexOf('=');
// if (i == -1 || i == 0) {
// errLines.add(lineNumber);
// continue;
// }
// String key = line.substring(0,i);
// String value = line.substring(i+1);
// if (!value.isEmpty()) {
// properties.put(key, value);
// }
// }
// if (!errLines.isEmpty())
// throw new IOException(tr("Malformed config file at lines {0}", errLines));
// }
// updateSystemProperties();
// }
//
// public void init(boolean reset){
// if(Main.applet)
// return;
// // get the preferences.
// File prefDir = getPreferencesDirFile();
// if (prefDir.exists()) {
// if(!prefDir.isDirectory()) {
// System.err.println(tr("Warning: Failed to initialize preferences. Preference directory ''{0}'' is not a directory.", prefDir.getAbsoluteFile()));
// JOptionPane.showMessageDialog(
// Main.parent,
// tr("<html>Failed to initialize preferences.<br>Preference directory ''{0}'' is not a directory.</html>", prefDir.getAbsoluteFile()),
// tr("Error"),
// JOptionPane.ERROR_MESSAGE
// );
// return;
// }
// } else {
// if (! prefDir.mkdirs()) {
// System.err.println(tr("Warning: Failed to initialize preferences. Failed to create missing preference directory: {0}", prefDir.getAbsoluteFile()));
// JOptionPane.showMessageDialog(
// Main.parent,
// tr("<html>Failed to initialize preferences.<br>Failed to create missing preference directory: {0}</html>",prefDir.getAbsoluteFile()),
// tr("Error"),
// JOptionPane.ERROR_MESSAGE
// );
// return;
// }
// }
//
// File preferenceFile = getPreferenceFile();
// try {
// if (!preferenceFile.exists()) {
// System.out.println(tr("Warning: Missing preference file ''{0}''. Creating a default preference file.", preferenceFile.getAbsoluteFile()));
// resetToDefault();
// save();
// } else if (reset) {
// System.out.println(tr("Warning: Replacing existing preference file ''{0}'' with default preference file.", preferenceFile.getAbsoluteFile()));
// resetToDefault();
// save();
// }
// } catch(IOException e) {
// e.printStackTrace();
// JOptionPane.showMessageDialog(
// Main.parent,
// tr("<html>Failed to initialize preferences.<br>Failed to reset preference file to default: {0}</html>",getPreferenceFile().getAbsoluteFile()),
// tr("Error"),
// JOptionPane.ERROR_MESSAGE
// );
// return;
// }
// try {
// load();
// } catch (IOException e) {
// e.printStackTrace();
// File backupFile = new File(prefDir,"preferences.bak");
// JOptionPane.showMessageDialog(
// Main.parent,
// 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()),
// tr("Error"),
// JOptionPane.ERROR_MESSAGE
// );
// preferenceFile.renameTo(backupFile);
// try {
// resetToDefault();
// save();
// } catch(IOException e1) {
// e1.printStackTrace();
// System.err.println(tr("Warning: Failed to initialize preferences.Failed to reset preference file to default: {0}", getPreferenceFile()));
// }
// }
// }
//
// public final void resetToDefault(){
// properties.clear();
// }
//
// /**
// * Convenience method for accessing colour preferences.
// *
// * @param colName name of the colour
// * @param def default value
// * @return a Color object for the configured colour, or the default value if none configured.
// */
// synchronized public Color getColor(String colName, Color def) {
// return getColor(colName, null, def);
// }
//
// public Color getColor(ColorKey key) {
// return getColor(key.getColorName(), key.getSpecialName(), key.getDefault());
// }
//
// /**
// * Convenience method for accessing colour preferences.
// *
// * @param colName name of the colour
// * @param specName name of the special colour settings
// * @param def default value
// * @return a Color object for the configured colour, or the default value if none configured.
// */
// synchronized public Color getColor(String colName, String specName, Color def) {
// putDefault("color."+colName, ColorHelper.color2html(def));
// String colStr = specName != null ? get("color."+specName) : "";
// if(colStr.equals("")) {
// colStr = get("color."+colName);
// }
// return colStr.equals("") ? def : ColorHelper.html2color(colStr);
// }
//
// synchronized public Color getDefaultColor(String colName) {
// String colStr = defaults.get("color."+colName);
// return colStr == null || "".equals(colStr) ? null : ColorHelper.html2color(colStr);
// }
//
// synchronized public boolean putColor(String colName, Color val) {
// return put("color."+colName, val != null ? ColorHelper.color2html(val) : null);
// }
//
synchronized public int getInteger(String key, int def) {
return def;
// putDefault(key, Integer.toString(def));
// String v = get(key);
// if(null == v)
// return def;
//
// try {
// return Integer.parseInt(v);
// } catch(NumberFormatException e) {
// // fall out
// }
// return def;
}
synchronized public long getLong(String key, long def) {
return def;
// putDefault(key, Long.toString(def));
// String v = get(key);
// if(null == v)
// return def;
//
// try {
// return Long.parseLong(v);
// } catch(NumberFormatException e) {
// // fall out
// }
// return def;
}
synchronized public double getDouble(String key, double def) {
return def;
// putDefault(key, Double.toString(def));
// String v = get(key);
// if(null == v)
// return def;
//
// try {
// return Double.parseDouble(v);
// } catch(NumberFormatException e) {
// // fall out
// }
// return def;
}
synchronized public double getDouble(String key, String def) {
// putDefault(key, def);
// String v = get(key);
// if(v != null && v.length() != 0) {
// try { return Double.parseDouble(v); } catch(NumberFormatException e) {}
// }
try { return Double.parseDouble(def); } catch(NumberFormatException e) {}
return 0.0;
}
// synchronized public String getCollectionAsString(final String key) {
// String s = get(key);
// if(s != null && s.length() != 0) {
// s = s.replaceAll("\u001e",",");
// }
// return s;
// }
//
// public boolean isCollection(String key, boolean def) {
// String s = get(key);
// if (s != null && s.length() != 0)
// return s.indexOf("\u001e") >= 0;
// else
// return def;
// }
//
/**
* Get a list of values for a certain key
* @param key the identifier for the setting
* @param def the default value.
* @return the corresponding value if the property has been set before,
* def otherwise
*/
synchronized public Collection<String> getCollection(String key, Collection<String> def) {
return def; // GWT fix
// putCollectionDefault(key, def);
// String s = get(key);
// if(s != null && s.length() != 0)
// return Arrays.asList(s.split("\u001e", -1));
// return def;
}
/**
* Get a list of values for a certain key
* @param key the identifier for the setting
* @return the corresponding value if the property has been set before,
* an empty Collection otherwise.
*/
synchronized public Collection<String> getCollection(String key) {
return null; // GWT fix
// putCollectionDefault(key, null);
// String s = get(key);
// if (s != null && s.length() != 0)
// return Arrays.asList(s.split("\u001e", -1));
// return Collections.emptyList();
}
//
// /* old style conversion, replace by above call after some transition time */
// /* remove this function, when no more old-style preference collections in the code */
// @Deprecated
// synchronized public Collection<String> getCollectionOld(String key, String sep) {
// putCollectionDefault(key, null);
// String s = get(key);
// if (s != null && s.length() != 0) {
// if(!s.contains("\u001e") && s.contains(sep)) {
// s = s.replace(sep, "\u001e");
// put(key, s);
// }
// return Arrays.asList(s.split("\u001e", -1));
// }
// return Collections.emptyList();
// }
//
// synchronized public void removeFromCollection(String key, String value) {
// List<String> a = new ArrayList<String>(getCollection(key, Collections.<String>emptyList()));
// a.remove(value);
// putCollection(key, a);
// }
synchronized public boolean putCollection(String key, Collection<String> val) {
return true;
// return put(key, Utils.join("\u001e", val));
}
// synchronized private void putCollectionDefault(String key, Collection<String> val) {
// putDefault(key, Utils.join("\u001e", val));
// }
//
// /**
// * Used to read a 2-dimensional array of strings from the preference file.
// * If not a single entry could be found, def is returned.
// */
synchronized public Collection<Collection<String>> getArray(String key,
Collection<Collection<String>> def)
{
return def;
// if(def != null)
// putArrayDefault(key, def);
// key += ".";
// int num = 0;
// Collection<Collection<String>> col = new LinkedList<Collection<String>>();
// while(properties.containsKey(key+num)) {
// col.add(getCollection(key+num++, null));
// }
// return num == 0 ? def : col;
}
//
// synchronized public boolean putArray(String key, Collection<Collection<String>> val) {
// boolean changed = false;
// key += ".";
// Collection<String> keys = getAllPrefix(key).keySet();
// if(val != null) {
// int num = 0;
// for(Collection<String> c : val) {
// keys.remove(key+num);
// changed |= putCollection(key+num++, c);
// }
// }
// int l = key.length();
// for(String k : keys) {
// try {
// Integer.valueOf(k.substring(l));
// changed |= put(k, null);
// } catch(NumberFormatException e) {
// /* everything which does not end with a number should not be deleted */
// }
// }
// return changed;
// }
//
// synchronized private void putArrayDefault(String key, Collection<Collection<String>> val) {
// key += ".";
// Collection<String> keys = getAllPrefixDefault(key).keySet();
// int num = 0;
// for(Collection<String> c : val) {
// keys.remove(key+num);
// putCollectionDefault(key+num++, c);
// }
// int l = key.length();
// for(String k : keys) {
// try {
// Integer.valueOf(k.substring(l));
// defaults.remove(k);
// } catch(Exception e) {
// /* everything which does not end with a number should not be deleted */
// }
// }
// }
//
// @Retention(RetentionPolicy.RUNTIME) public @interface pref { }
// @Retention(RetentionPolicy.RUNTIME) public @interface writeExplicitly { }
//
// /**
// * Get a list of hashes which are represented by a struct-like class.
// * It reads lines of the form
// * > key.0=prop:val \u001e prop:val \u001e ... \u001e prop:val
// * > ...
// * > key.N=prop:val \u001e prop:val \u001e ... \u001e prop:val
// * Possible properties are given by fields of the class klass that have
// * the @pref annotation.
// * Default constructor is used to initialize the struct objects, properties
// * then override some of these default values.
// * @param key main preference key
// * @param klass The struct class
// * @return a list of objects of type T or an empty list if nothing was found
// */
// public <T> List<T> getListOfStructs(String key, Class<T> klass) {
// List<T> r = getListOfStructs(key, null, klass);
// if (r == null)
// return Collections.emptyList();
// else
// return r;
// }
//
// /**
// * same as above, but returns def if nothing was found
// */
// public <T> List<T> getListOfStructs(String key, Collection<T> def, Class<T> klass) {
// Collection<Collection<String>> array =
// getArray(key, def == null ? null : serializeListOfStructs(def, klass));
// if (array == null)
// return def == null ? null : new ArrayList<T>(def);
// List<T> lst = new ArrayList<T>();
// for (Collection<String> entries : array) {
// T struct = deserializeStruct(entries, klass);
// lst.add(struct);
// }
// return lst;
// }
//
// /**
// * Save a list of hashes represented by a struct-like class.
// * Considers only fields that have the @pref annotation.
// * In addition it does not write fields with null values. (Thus they are cleared)
// * Default values are given by the field values after default constructor has
// * been called.
// * Fields equal to the default value are not written unless the field has
// * the @writeExplicitly annotation.
// * @param key main preference key
// * @param val the list that is supposed to be saved
// * @param klass The struct class
// * @return true if something has changed
// */
// public <T> boolean putListOfStructs(String key, Collection<T> val, Class<T> klass) {
// return putArray(key, serializeListOfStructs(val, klass));
// }
//
// private <T> Collection<Collection<String>> serializeListOfStructs(Collection<T> l, Class<T> klass) {
// if (l == null)
// return null;
// Collection<Collection<String>> vals = new ArrayList<Collection<String>>();
// for (T struct : l) {
// if (struct == null)
// continue;
// vals.add(serializeStruct(struct, klass));
// }
// return vals;
// }
//
// private <T> Collection<String> serializeStruct(T struct, Class<T> klass) {
// T structPrototype;
// try {
// structPrototype = klass.newInstance();
// } catch (InstantiationException ex) {
// throw new RuntimeException();
// } catch (IllegalAccessException ex) {
// throw new RuntimeException();
// }
//
// Collection<String> hash = new ArrayList<String>();
// for (Field f : klass.getDeclaredFields()) {
// if (f.getAnnotation(pref.class) == null) {
// continue;
// }
// f.setAccessible(true);
// try {
// Object fieldValue = f.get(struct);
// Object defaultFieldValue = f.get(structPrototype);
// if (fieldValue != null) {
// if (f.getAnnotation(writeExplicitly.class) != null || !Utils.equal(fieldValue, defaultFieldValue)) {
// hash.add(String.format("%s:%s", f.getName().replace("_", "-"), fieldValue.toString()));
// }
// }
// } catch (IllegalArgumentException ex) {
// throw new RuntimeException();
// } catch (IllegalAccessException ex) {
// throw new RuntimeException();
// }
// }
// return hash;
// }
//
// private <T> T deserializeStruct(Collection<String> hash, Class<T> klass) {
// T struct = null;
// try {
// struct = klass.newInstance();
// } catch (InstantiationException ex) {
// throw new RuntimeException();
// } catch (IllegalAccessException ex) {
// throw new RuntimeException();
// }
// for (String key_value : hash) {
// final int i = key_value.indexOf(':');
// if (i == -1 || i == 0) {
// continue;
// }
// String key = key_value.substring(0,i);
// String valueString = key_value.substring(i+1);
//
// Object value = null;
// Field f;
// try {
// f = klass.getDeclaredField(key.replace("-", "_"));
// } catch (NoSuchFieldException ex) {
// continue;
// } catch (SecurityException ex) {
// throw new RuntimeException();
// }
// if (f.getAnnotation(pref.class) == null) {
// continue;
// }
// f.setAccessible(true);
// if (f.getType() == Boolean.class || f.getType() == boolean.class) {
// value = Boolean.parseBoolean(valueString);
// } else if (f.getType() == Integer.class || f.getType() == int.class) {
// try {
// value = Integer.parseInt(valueString);
// } catch (NumberFormatException nfe) {
// continue;
// }
// } else if (f.getType() == String.class) {
// value = valueString;
// } else
// throw new RuntimeException("unsupported preference primitive type");
//
// try {
// f.set(struct, value);
// } catch (IllegalArgumentException ex) {
// throw new AssertionError();
// } catch (IllegalAccessException ex) {
// throw new RuntimeException();
// }
// }
// return struct;
// }
//
// /**
// * Updates system properties with the current values in the preferences.
// *
// */
// public void updateSystemProperties() {
// Properties sysProp = System.getProperties();
// sysProp.put("http.agent", Version.getInstance().getAgentString());
// System.setProperties(sysProp);
// }
//
// /**
// * The default plugin site
// */
// private final static String[] DEFAULT_PLUGIN_SITE = {
// "http://josm.openstreetmap.de/plugin%<?plugins=>"};
//
// /**
// * Replies the collection of plugin site URLs from where plugin lists can be downloaded
// *
// * @return
// */
// public Collection<String> getPluginSites() {
// return getCollection("pluginmanager.sites", Arrays.asList(DEFAULT_PLUGIN_SITE));
// }
//
// /**
// * Sets the collection of plugin site URLs.
// *
// * @param sites the site URLs
// */
// public void setPluginSites(Collection<String> sites) {
// putCollection("pluginmanager.sites", sites);
// }
//
// public static class XMLTag {
// public String key;
// public String value;
// }
// public static class XMLCollection {
// public String key;
// }
// public static class XMLEntry {
// public String value;
// }
// public void fromXML(Reader in) throws SAXException {
// XmlObjectParser parser = new XmlObjectParser();
// parser.map("tag", XMLTag.class);
// parser.map("entry", XMLEntry.class);
// parser.map("collection", XMLCollection.class);
// parser.startWithValidation(in,
// "http://josm.openstreetmap.de/preferences-1.0", "resource://data/preferences.xsd");
// LinkedList<String> vals = new LinkedList<String>();
// while(parser.hasNext()) {
// Object o = parser.next();
// if(o instanceof XMLTag) {
// properties.put(((XMLTag)o).key, ((XMLTag)o).value);
// } else if (o instanceof XMLEntry) {
// vals.add(((XMLEntry)o).value);
// } else if (o instanceof XMLCollection) {
// properties.put(((XMLCollection)o).key, Utils.join("\u001e", vals));
// vals = new LinkedList<String>();
// }
// }
// }
//
// public String toXML(boolean nopass) {
// StringBuilder b = new StringBuilder(
// "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
// "<preferences xmlns=\"http://josm.openstreetmap.de/preferences-1.0\">\n");
// for (Entry<String, String> p : properties.entrySet()) {
// if (nopass && p.getKey().equals("osm-server.password")) {
// continue; // do not store plain password.
// }
// String r = p.getValue();
// if(r.contains("\u001e"))
// {
// b.append(" <collection key='");
// b.append(XmlWriter.encode(p.getKey()));
// b.append("'>\n");
// for (String val : r.split("\u001e", -1))
// {
// b.append(" <entry value='");
// b.append(XmlWriter.encode(val));
// b.append("' />\n");
// }
// b.append(" </collection>\n");
// }
// else
// {
// b.append(" <tag key='");
// b.append(XmlWriter.encode(p.getKey()));
// b.append("' value='");
// b.append(XmlWriter.encode(p.getValue()));
// b.append("' />\n");
// }
// }
// b.append("</preferences>");
// return b.toString();
// }
}