Index: src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java
===================================================================
--- src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(revision 5074)
+++ src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(working copy)
@@ -2,7 +2,9 @@
 package org.openstreetmap.josm.gui.preferences.advanced;
 
 import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.marktr;
 
+import java.awt.Color;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
@@ -11,9 +13,12 @@
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -22,6 +27,7 @@
 import javax.swing.ButtonGroup;
 import javax.swing.DefaultCellEditor;
 import javax.swing.JButton;
+import javax.swing.JFileChooser;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
@@ -31,10 +37,12 @@
 import javax.swing.JTextField;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
+import javax.swing.filechooser.FileFilter;
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.DefaultTableModel;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.CustomConfigurator;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.Preferences.ListListSetting;
 import org.openstreetmap.josm.data.Preferences.ListSetting;
@@ -42,6 +50,7 @@
 import org.openstreetmap.josm.data.Preferences.Setting;
 import org.openstreetmap.josm.data.Preferences.StringSetting;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.actionsupport.LogShowDialog;
 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
@@ -57,7 +66,7 @@
             return new AdvancedPreference();
         }
     }
-    
+
     private AdvancedPreference() {
         super("advanced", tr("Advanced Preferences"), tr("Setting Preference entries directly. Use with caution!"));
     }
@@ -109,7 +118,11 @@
         public boolean isChanged() {
             return changed;
         }
-
+    
+        private void markAsChanged() {
+            changed = true;
+        }
+    
         public void reset() {
             value = defaultValue;
             changed = true;
@@ -154,12 +167,7 @@
                 applyFilter();
             }
         });
-
-        Map<String, Setting> orig = Main.pref.getAllSettings();
-        Map<String, Setting> defaults = Main.pref.getAllDefaults();
-        orig.remove("osm-server.password");
-        defaults.remove("osm-server.password");
-        prepareData(orig, defaults);
+        readPreferences(Main.pref);
         model = new AllSettingsTableModel();
         applyFilter();
 
@@ -197,6 +205,85 @@
             }
         });
 
+        JButton read = new JButton(tr("Read from file"));
+        p.add(read, GBC.std().insets(5,5,0,0));
+        read.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                File[] files = askUserForCustomSettingsFiles(false, tr("Open JOSM customization file"));
+                if (files.length==0) return;
+                
+                Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
+                
+                StringBuilder log = new StringBuilder();
+                log.append("<html>");
+                for (File f: files) {
+                    CustomConfigurator.readXML(f, tmpPrefs);
+                    log.append(CustomConfigurator.getLog());
+                }
+                //try { Main.pref.save();  } catch (IOException ex) { }
+                log.append("</html>");
+                String msg = log.toString().replace("\n", "<br/>");
+                
+                new LogShowDialog(tr("Import log"), tr("<html>Here is file import summary. <br/>"
+                        + "You can reject preferences changes by pressing \"Cancel\" in preferences dialog <br/>"
+                        + "To activate some changes JOSM restart may be needed.</html>"), msg).showDialog();
+                
+                
+                //JOptionPane.showMessageDialog(Main.parent,
+                //   tr("Installed plugins and some changes in preferences will start to work after JOSM restart"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
+                readPreferences(tmpPrefs);
+                // sorting - first modified, then non-default, then default entries
+                Collections.sort(data, new Comparator<PrefEntry>() {
+                    @Override
+                    public int compare(PrefEntry o1, PrefEntry o2) {
+                        if (o1.changed && !o2.changed) return -1;
+                        if (o2.changed && !o1.changed) return 1;
+                        if (!(o1.isDefault) && o2.isDefault) return -1;
+                        if (!(o2.isDefault) && o1.isDefault) return 1;
+                        return o1.key.compareTo(o2.key);
+                    }
+                  });
+
+                applyFilter();
+                ((AllSettingsTableModel) list.getModel()).fireTableDataChanged();
+            }
+
+        });
+        
+        JButton export = new JButton(tr("Export selected items"));
+        p.add(export, GBC.std().insets(5,5,0,0));
+        export.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                ArrayList<String> keys = new ArrayList<String>();
+                boolean hasLists = false;
+                for (int row : list.getSelectedRows()) {
+                    PrefEntry p = (PrefEntry) model.getValueAt(row, -1);
+                    if (!p.isDefault()) {
+                        // preferences with default values are not saved
+                        if (!(p.getValue() instanceof StringSetting)) hasLists=true; // => append and replace differs
+                        keys.add(p.getKey());
+                    }
+                }
+                if (keys.size()==0) {
+                     JOptionPane.showMessageDialog(Main.parent,
+                        tr("Please select some preference keys not marked as default"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
+                     return;
+                }
+
+                File[] files = askUserForCustomSettingsFiles(true, tr("Export preferences keys to JOSM customization file"));
+                if (files.length==0) return;
+                
+                int answer = 0;
+                if (hasLists) { 
+                    answer = JOptionPane.showOptionDialog(Main.parent, tr("What to with preference lists when this file is to be imported?"), tr("Question"),
+                       JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
+                        null, new String[]{"Append preferences from file to existing values","Replace existing values"},0);
+                }
+                CustomConfigurator.exportPreferencesKeysToFile(files[0].getAbsolutePath(), answer==0, keys);
+            }
+        });
+
+
         list.addMouseListener(new MouseAdapter(){
             @Override public void mouseClicked(MouseEvent e) {
                 if (e.getClickCount() == 2) {
@@ -206,20 +293,76 @@
         });
     }
 
-    private void prepareData(Map<String, Setting> orig, Map<String, Setting> defaults) {
+    private void readPreferences(Preferences tmpPrefs) {
+        Map<String, Setting> loaded;
+        Map<String, Setting> orig = Main.pref.getAllSettings();
+        Map<String, Setting> defaults = tmpPrefs.getAllDefaults();
+        orig.remove("osm-server.password");
+        defaults.remove("osm-server.password");
+        if (tmpPrefs != Main.pref) {
+            loaded = tmpPrefs.getAllSettings();
+        } else {
+            loaded = orig;
+        }
+        prepareData(loaded, orig, defaults);
+    }
+    
+    private File[] askUserForCustomSettingsFiles(boolean saveFileFlag, String title) {
+        String dir = Main.pref.get("customsettings.lastDirectory");
+        if (dir.length()==0) dir =".";
+        
+        JFileChooser fc = new JFileChooser(dir);
+        fc.setDialogTitle(title);
+        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
+        fc.setAcceptAllFileFilterUsed(false);
+        fc.setFileFilter(new FileFilter() {
+            @Override
+            public boolean accept(File f) {
+                return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml");
+            }
+            @Override
+            public String getDescription() {
+                return tr("JOSM custom settings files (*.xml)");
+            }
+            });
+        
+            fc.setMultiSelectionEnabled(!saveFileFlag);
+            int result = saveFileFlag? fc.showSaveDialog(Main.parent) : fc.showOpenDialog(Main.parent);
+            if (result == JFileChooser.APPROVE_OPTION) {
+                if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) {
+                    Main.pref.put("customsettings.lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
+                }
+                File sel[] = fc.isMultiSelectionEnabled() ? fc.getSelectedFiles() : (new File[]{fc.getSelectedFile()});
+                if (sel.length==1 && !sel[0].getName().contains(".")) sel[0]=new File(sel[0].getAbsolutePath()+".xml");
+                return sel;
+            } 
+            return new File[0];
+    }
+            
+    private void prepareData(Map<String, Setting> loaded, Map<String, Setting> orig, Map<String, Setting> defaults) {
         data = new ArrayList<PrefEntry>();
-        for (Entry<String, Setting> e : orig.entrySet()) {
+        for (Entry<String, Setting> e : loaded.entrySet()) {
             Setting value = e.getValue();
+            Setting old = orig.get(e.getKey());
             Setting def = defaults.get(e.getKey());
             if (def == null) {
                 def = value.getNullInstance();
             }
             PrefEntry en = new PrefEntry(e.getKey(), value, def, false);
+            // after changes we have nondefault value. Value is changed if is not equal to old value
+            if ( !Preferences.isEqual(old, value) ) {
+                en.markAsChanged();
+            }
             data.add(en);
         }
         for (Entry<String, Setting> e : defaults.entrySet()) {
-            if (!orig.containsKey(e.getKey())) {
+            if (!loaded.containsKey(e.getKey())) {
                 PrefEntry en = new PrefEntry(e.getKey(), e.getValue(), e.getValue(), true);
+                // after changes we have default value. So, value is changed if old value is not default
+                Setting old = orig.get(e.getKey());
+                if ( old!=null ) {
+                    en.markAsChanged();
+                }
                 data.add(en);
             }
         }
@@ -271,9 +414,24 @@
             Setting setting = pe.getValue();
             Object val = setting.getValue();
             String display = val != null ? val.toString() : "<html><i>&lt;"+tr("unset")+"&gt;</i></html>";
-
+            
             JLabel label = (JLabel)super.getTableCellRendererComponent(table,
                     display, isSelected, hasFocus, row, column);
+
+            label.setBackground(Main.pref.getUIColor("Table.background"));
+            if (isSelected) {
+                label.setForeground(Main.pref.getUIColor("Table.foreground"));
+            }
+            if(pe.isChanged()) {
+                label.setBackground(Main.pref.getColor(
+                        marktr("Advanced Background: Changed"),
+                        new Color(200,255,200)));
+            } else if(!pe.isDefault()) {
+                label.setBackground(Main.pref.getColor(
+                        marktr("Advanced Background: NonDefalut"),
+                        new Color(255,255,200)));
+            }
+
             if (!pe.isDefault()) {
                 label.setFont(label.getFont().deriveFont(Font.BOLD));
             }
Index: src/org/openstreetmap/josm/gui/actionsupport/LogShowDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/actionsupport/LogShowDialog.java	(revision 0)
+++ src/org/openstreetmap/josm/gui/actionsupport/LogShowDialog.java	(working copy)
@@ -0,0 +1,53 @@
+package org.openstreetmap.josm.gui.actionsupport;
+
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import javax.swing.*;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
+import org.openstreetmap.josm.gui.preferences.advanced.AdvancedPreference;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
+import org.openstreetmap.josm.tools.GBC;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Generic dialog with message and scrolling area
+ * @author Alexei
+ */
+public class LogShowDialog extends ExtendedDialog {
+
+
+    public LogShowDialog (String title, String msg, String log) {
+        super(Main.parent, title, new String[] {tr("OK")});
+        setButtonIcons(new String[] {"ok.png"});
+        setContent(build(msg, log));
+    }
+
+    protected JPanel build(String msg, String log) {
+        JPanel p = new JPanel(new GridBagLayout());
+        JLabel lbl = new JLabel(msg);
+        
+        lbl.setFont(lbl.getFont().deriveFont(0, 14));
+        
+        p.add(lbl, GBC.eol().insets(5,0,5,0));
+        JEditorPane txt = new JEditorPane();
+        txt.setContentType("text/html");
+        txt.setText(log);
+        txt.setEditable(false);
+        txt.setOpaque(false);
+        
+        JScrollPane sp = new JScrollPane(txt);
+        sp.setOpaque(false);
+        sp.setPreferredSize(new Dimension(600,300));
+        
+        
+        p.add(sp, GBC.eop().insets(5,15,0,0).fill(GBC.HORIZONTAL));
+
+        return p;
+    }
+}
+ 
+
Index: src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- src/org/openstreetmap/josm/data/Preferences.java	(revision 5074)
+++ src/org/openstreetmap/josm/data/Preferences.java	(working copy)
@@ -1748,4 +1748,22 @@
                 System.out.println(tr("Preference setting {0} has been removed since it is no longer used.", key));
         }
     }
+
+    public static boolean isEqual(Setting a, Setting b) {
+        if (a==null && b==null) return true;
+        if (a==null) return false;
+        if (b==null) return false;
+        if (a==b) return true;
+        
+        if (a instanceof StringSetting) 
+            return (a.getValue().equals(b.getValue()));
+        if (a instanceof ListSetting) 
+            return equalCollection((Collection<String>) a.getValue(), (Collection<String>) b.getValue());
+        if (a instanceof ListListSetting) 
+            return equalArray((Collection<Collection<String>>) a.getValue(), (Collection<List<String>>) b.getValue());
+        if (a instanceof MapListSetting) 
+            return equalListOfStructs((Collection<Map<String, String>>) a.getValue(), (Collection<Map<String, String>>) b.getValue());
+        return a.equals(b);
+    }
+
 }
Index: src/org/openstreetmap/josm/data/CustomConfigurator.java
===================================================================
--- src/org/openstreetmap/josm/data/CustomConfigurator.java	(revision 0)
+++ src/org/openstreetmap/josm/data/CustomConfigurator.java	(working copy)
@@ -0,0 +1,1079 @@
+package org.openstreetmap.josm.data;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.script.ScriptException;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences.Setting;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.openstreetmap.josm.gui.io.DownloadFileTask;
+import org.openstreetmap.josm.plugins.PluginDownloadTask;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
+import org.openstreetmap.josm.tools.LanguageInfo;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Class to process configuration changes stored in XML
+ * can be used to modify preferences, store/delete files in .josm folders etc
+ */
+public class CustomConfigurator {
+    private static StringBuilder summary = new StringBuilder();
+        
+    public static void log(String fmt, Object... vars) {
+        summary.append(String.format(fmt, vars));
+    }
+    
+    public static void log(String s) {
+        summary.append(s);
+        summary.append("\n");
+    }
+    
+    public static String getLog() {
+        return summary.toString();
+    }
+    
+    public static void readXML(String dir, String fileName) {
+        readXML(new File(dir, fileName));
+    }
+
+    public static void readXML(final File file, final Preferences prefs) {
+        synchronized(CustomConfigurator.class) {
+            busy=true;
+        }
+        new XMLCommandProcessor(prefs).openAndReadXML(file);
+        synchronized(CustomConfigurator.class) {
+            CustomConfigurator.class.notifyAll(); 
+            busy=false;
+        }
+    }
+    
+    public static void readXML(File file) {
+        readXML(file, Main.pref);
+    }
+    
+    public static void downloadFile(String address, String path, String base) {
+        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, false);
+    }
+    public static void downloadAndUnpackFile(String address, String path, String base) {
+        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, true);
+    }
+
+    public static void processDownloadOperation(String address, String path, String parentDir, boolean mkdir, boolean unzip) {
+        String dir = parentDir;
+        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+            return; // some basic protection
+        }
+        File fOut = new File(dir, path);
+        DownloadFileTask downloadFileTask = new DownloadFileTask(Main.parent, address, fOut, mkdir, unzip);
+
+        Future f = Main.worker.submit(downloadFileTask);
+        log("Info: downloading file from %s to %s in background ", parentDir, fOut.getAbsolutePath());
+        if (unzip) log("and unpacking it"); else log("");
+        
+    }
+
+    public static void messageBox(String type, String text) {
+        if (type==null || type.length()==0) type="plain";
+
+        switch (type.charAt(0)) {
+            case 'i': JOptionPane.showMessageDialog(Main.parent, text, tr("Information"), JOptionPane.INFORMATION_MESSAGE); break;
+            case 'w': JOptionPane.showMessageDialog(Main.parent, text, tr("Warning"), JOptionPane.WARNING_MESSAGE); break;
+            case 'e': JOptionPane.showMessageDialog(Main.parent, text, tr("Error"), JOptionPane.ERROR_MESSAGE); break;
+            case 'q': JOptionPane.showMessageDialog(Main.parent, text, tr("Question"), JOptionPane.QUESTION_MESSAGE); break;
+            case 'p': JOptionPane.showMessageDialog(Main.parent, text, tr("Message"), JOptionPane.PLAIN_MESSAGE); break;
+        }
+    }
+    
+    public static int askForOption(String text, String opts) {
+        Integer answer;
+        if (opts.length()>0) {
+            String[] options = opts.split(";");
+            answer = JOptionPane.showOptionDialog(Main.parent, text, "Question", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
+        } else {
+            answer = JOptionPane.showOptionDialog(Main.parent, text, "Question", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, 2);
+        }
+        if (answer==null) return -1; else return answer;
+    }
+
+    public static String askForText(String text) {
+        String s = JOptionPane.showInputDialog(Main.parent, text, tr("Enter text"), JOptionPane.QUESTION_MESSAGE);
+        if (s!=null && (s=s.trim()).length()>0) {
+            return s;
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * This function exports part of user preferences to specified file.
+     * Default values are not saved.
+     * @param filename - where to export
+     * @param append - if true, resulting file cause appending to exuisting preferences
+     * @param keys - which preferences keys you need to export ("imagery.entries", for example)
+     */
+    public static void exportPreferencesKeysToFile(String filename, boolean append, String... keys) {
+        HashSet<String> keySet = new HashSet<String>();
+        Collections.addAll(keySet, keys);
+        exportPreferencesKeysToFile(filename, append, keySet);
+    }
+
+    /**
+     * This function exports part of user preferences to specified file.
+     * Default values are not saved.
+     * Preference keys matching specified pattern are saved
+     * @param filename - where to export
+     * @param append - if true, resulting file cause appending to exuisting preferences
+     * @param pattern - Regexp pattern forh preferences keys you need to export (".*imagery.*", for example)
+     */
+    public static void exportPreferencesKeysByPatternToFile(String fileName, boolean append, String pattern) {
+        ArrayList<String> keySet = new ArrayList<String>();
+        Map<String, Setting> allSettings = Main.pref.getAllSettings();
+        for (String key: allSettings.keySet()) {
+            if (key.matches(pattern)) keySet.add(key);
+        }
+        exportPreferencesKeysToFile(fileName, append, keySet);
+    }
+
+    public static void exportPreferencesKeysToFile(String filename, boolean append, Collection<String> keys) {
+        Element root = null;
+        Document document = null;
+        Document exportDocument = null;
+
+        try {
+            String toXML = Main.pref.toXML(true);
+            InputStream is = new ByteArrayInputStream(toXML.getBytes());
+            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+            builderFactory.setValidating(false);
+            builderFactory.setNamespaceAware(false);
+            DocumentBuilder builder = builderFactory.newDocumentBuilder();
+            document = builder.parse(is);
+            exportDocument = builder.newDocument();
+            root = document.getDocumentElement();
+        } catch (Exception ex) {
+            System.out.println("Error getting preferences to save:" +ex.getMessage());
+        }
+        if (root==null) return;
+        try {
+            
+            Element newRoot = exportDocument.createElement("config");
+            exportDocument.appendChild(newRoot);
+            
+            Element prefElem = exportDocument.createElement("preferences");
+            prefElem.setAttribute("operation", append?"append":"replace");
+            newRoot.appendChild(prefElem);
+
+            NodeList childNodes = root.getChildNodes();
+            int n = childNodes.getLength();
+            for (int i = 0; i < n ; i++) {
+                Node item = childNodes.item(i);
+                if (item.getNodeType() == Node.ELEMENT_NODE) {
+                    String currentKey = ((Element) item).getAttribute("key");
+                    if (keys.contains(currentKey)) {
+                        Node imported = exportDocument.importNode(item, true);
+                        prefElem.appendChild(imported);
+                    }
+                }
+            }
+            File f = new File(filename);
+            Transformer ts = TransformerFactory.newInstance().newTransformer();
+            ts.setOutputProperty(OutputKeys.INDENT, "yes");
+            ts.transform(new DOMSource(exportDocument), new StreamResult(f.toURI().getPath()));
+        } catch (Exception ex) {
+            System.out.println("Error saving preferences part:" +ex.getMessage());
+            ex.printStackTrace();
+        }
+    }
+    
+    
+    public static void deleteFile(String path, String base) {
+        String dir = getDirectoryByAbbr(base);
+        if (dir==null) {
+            log("Error: Can not find base, use base=cache, base=prefs or base=plugins attribute.");
+            return;
+        }
+        log("Delete file: %s\n", path);
+        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+            return; // some basic protection
+        }
+        File fOut = new File(dir, path);
+        if (fOut.exists()) {
+            deleteFileOrDirectory(fOut);
+        }
+        return;
+    }
+
+    public static void deleteFileOrDirectory(String path) {
+        deleteFileOrDirectory(new File(path));
+    }
+    
+    public static void deleteFileOrDirectory(File f) {
+        if (f.isDirectory()) {
+            for (File f1: f.listFiles()) {
+                deleteFileOrDirectory(f1);
+            } 
+        }
+        try {
+            f.delete();
+        } catch (Exception e) {
+            log("Warning: Can not delete file "+f.getPath());
+        }
+    }
+
+    private static boolean busy=false;
+
+    
+    public static void pluginOperation(String install, String uninstall, String delete)  {
+        final List<String> installList = Arrays.asList(install.toLowerCase().split(";"));
+        final List<String> removeList = Arrays.asList(uninstall.toLowerCase().split(";"));
+        final List<String> deleteList = Arrays.asList(delete.toLowerCase().split(";"));
+
+        final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
+        Runnable r = new Runnable() {
+            public void run() {
+                if (task.isCanceled()) return;
+                synchronized (CustomConfigurator.class) { 
+                try { // proceed only after ll other tasks were finished
+                    while (busy) CustomConfigurator.class.wait();
+                } catch (InterruptedException ex) { }
+                        
+                SwingUtilities.invokeLater(new Runnable() {
+                    public void run() {
+                        List<PluginInformation> availablePlugins = task.getAvailablePlugins();
+                        List<PluginInformation> toInstallPlugins = new ArrayList<PluginInformation>();
+                        List<PluginInformation> toRemovePlugins = new ArrayList<PluginInformation>();
+                        List<PluginInformation> toDeletePlugins = new ArrayList<PluginInformation>();
+                        for (PluginInformation pi: availablePlugins) {
+                            //System.out.print(pi.name+";");
+                            String name = pi.name.toLowerCase();
+                            if (installList.contains(name)) toInstallPlugins.add(pi);
+                            if (removeList.contains(name)) toRemovePlugins.add(pi);
+                            if (deleteList.contains(name)) toDeletePlugins.add(pi);
+                        }
+                        if (!installList.isEmpty()) {
+                            PluginDownloadTask pluginDownloadTask = new PluginDownloadTask(Main.parent, toInstallPlugins, tr ("Installing plugins"));
+                            Main.worker.submit(pluginDownloadTask);
+                        }
+                            Collection<String> pls = new ArrayList<String>(Main.pref.getCollection("plugins"));
+                            for (PluginInformation pi: toInstallPlugins) {
+                                if (!pls.contains(pi.name)) pls.add(pi.name);
+                            }
+                            for (PluginInformation pi: toRemovePlugins) {
+                                pls.remove(pi.name);
+                            }
+                            for (PluginInformation pi: toDeletePlugins) {
+                                pls.remove(pi.name);
+                                new File(Main.pref.getPluginsDirectory(),pi.name+".jar").deleteOnExit();
+                            }
+                            Main.pref.putCollection("plugins",pls);
+                        }
+                });
+            }
+            }
+
+        };
+        Main.worker.submit(task);
+        Main.worker.submit(r);
+    }
+    
+    private static String getDirectoryByAbbr(String base) {
+            String dir;
+            if ("prefs".equals(base) || base.length()==0) {
+                dir = Main.pref.getPreferencesDir();
+            } else if ("cache".equals(base)) {
+                dir = Main.pref.getCacheDirectory().getAbsolutePath();
+            } else if ("plugins".equals(base)) {
+                dir = Main.pref.getPluginsDirectory().getAbsolutePath();
+            } else {
+                dir = null;
+            }
+            return dir;
+    }
+
+    public static Preferences clonePreferences(Preferences pref) {
+        Preferences tmp = new Preferences();
+        tmp.defaults.putAll(   pref.defaults );
+        tmp.properties.putAll( pref.properties );
+        tmp.arrayDefaults.putAll(   pref.arrayDefaults );
+        tmp.arrayProperties.putAll( pref.arrayProperties );
+        tmp.collectionDefaults.putAll(   pref.collectionDefaults );
+        tmp.collectionProperties.putAll( pref.collectionProperties );
+        tmp.listOfStructsDefaults.putAll(   pref.listOfStructsDefaults );
+        tmp.listOfStructsProperties.putAll( pref.listOfStructsProperties );
+        tmp.colornames.putAll( pref.colornames );
+        
+        return tmp;
+    }
+
+
+    public static class XMLCommandProcessor {
+        
+        //ProgressMonitor monitor;
+
+        Preferences mainPrefs;
+        Map<String,Element> tasksMap = new HashMap<String,Element>();
+        
+        private boolean lastV; // last If condition result
+        
+        
+        ScriptEngine engine ;
+        
+        public void openAndReadXML(File file) {
+            log("-- Reading custom preferences from " + file.getAbsolutePath() + " --");
+            InputStream is = null;
+            try {
+                is = new BufferedInputStream(new FileInputStream(file));
+                DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+                builderFactory.setValidating(false);
+                builderFactory.setNamespaceAware(true);
+                DocumentBuilder builder = builderFactory.newDocumentBuilder();
+                Document document = builder.parse(is);
+                synchronized (CustomConfigurator.class) {
+                    processXML(document);
+                }
+            } catch (Exception ex) {
+                log("Error reading custom preferences: "+ex.getMessage());
+            } finally {
+                try {
+                    if (is != null) {
+                        is.close();
+                    }
+                } catch (IOException ex) {         }
+            }
+            log("-- Reading complete --");
+        }
+
+        public XMLCommandProcessor(Preferences mainPrefs) {
+            try {
+                this.mainPrefs = mainPrefs;
+                CustomConfigurator.summary = new StringBuilder();
+                engine = new ScriptEngineManager().getEngineByName("rhino");
+                engine.eval("API={}; API.pref={}; API.fragments={};");
+                String className =  CustomConfigurator.class.getName();
+                engine.eval("API.messageBox="+className+".messageBox");
+                engine.eval("API.askText=function(text) { return String("+className+".askForText(text));}");
+                engine.eval("API.askOption="+className+".askForOption");
+                engine.eval("API.downloadFile="+className+".downloadFile");
+                engine.eval("API.downloadAndUnpackFile="+className+".downloadAndUnpackFile");
+                engine.eval("API.deleteFile="+className+".deleteFile");
+                engine.eval("API.plugin ="+className+".pluginOperation");
+                engine.eval("API.pluginInstall = function(names) { "+className+".pluginOperation(names,'','');}");
+                engine.eval("API.pluginUninstall = function(names) { "+className+".pluginOperation('',names,'');}");
+                engine.eval("API.pluginDelete = function(names) { "+className+".pluginOperation('','',names);}");
+            } catch (Exception ex) {
+                log("Error: initializing script engine: "+ex.getMessage());
+            }
+        }
+        
+        
+        
+        
+        /*public XMLCommandProcessor(Preferences mainPrefs, String baseDir, ProgressMonitor pm) {
+            this.mainPrefs = mainPrefs;
+            this.baseDir = baseDir;
+            this.monitor = pm;
+        }*/
+
+     
+        private void processXML(Document document) {
+            Element root = document.getDocumentElement();
+            processXmlFragment(root);
+        }
+
+        private void processXmlFragment(Element root) {
+            NodeList childNodes = root.getChildNodes();
+            int nops = childNodes.getLength();
+            for (int i = 0; i < nops; i++) {
+                Node item = childNodes.item(i);
+                if (item.getNodeType() != Node.ELEMENT_NODE) continue;
+                String elementName = item.getNodeName();
+                //if (monitor!=null) monitor.indeterminateSubTask(elementName);
+                Element elem = (Element) item;
+
+                if ("var".equals(elementName)) {
+                    setVar(elem.getAttribute("name"), evalVars(elem.getAttribute("value")));
+                } else if ("task".equals(elementName)) {
+                    tasksMap.put(elem.getAttribute("name"), elem);
+                } else if ("runtask".equals(elementName)) {
+                    if (processRunTaskElement(elem)) return;
+                } else if ("ask".equals(elementName)) {
+                    processAskElement(elem); 
+                } else if ("if".equals(elementName)) {
+                    processIfElement(elem); 
+                } else if ("else".equals(elementName)) {
+                    processElseElement(elem); 
+                } else if ("break".equals(elementName)) {
+                    return;
+                } else if ("plugin".equals(elementName)) {
+                    processPluginInstallElement(elem);
+                } else if ("messagebox".equals(elementName)){
+                    processMsgBoxElement(elem);
+                } else if ("preferences".equals(elementName)) {
+                    processPreferencesElement(elem);
+                } else if ("download".equals(elementName)) {
+                    processDownloadElement(elem);
+                } else if ("delete".equals(elementName)) {
+                    processDeleteElement(elem);
+                } else if ("script".equals(elementName)) {
+                    processScriptElement(elem);
+                } else {
+                    log("Error: Unknown element " + elementName);
+                }
+                
+            }
+        }
+
+
+
+        private void processPreferencesElement(Element item) {
+            String oper = evalVars(item.getAttribute("operation"));
+            String id = evalVars(item.getAttribute("id"));
+            
+            
+            if ("delete-keys".equals(oper)) {
+                String pattern = evalVars(item.getAttribute("pattern"));
+                String key = evalVars(item.getAttribute("key"));
+                if (key != null) {
+                    PreferencesUtils.deletePreferenceKey(key, mainPrefs);
+                }
+                if (pattern != null) {
+                    PreferencesUtils.deletePreferenceKeyByPattern(pattern, mainPrefs);
+                }
+                return;
+            }
+            
+            Preferences tmpPref = readPreferencesFromDOMElement(item);
+            PreferencesUtils.showPrefs(tmpPref);
+            
+            if (id.length()>0) {
+                try {
+                    String fragmentVar = "API.fragments['"+id+"']";
+                    engine.eval(fragmentVar+"={};");
+                    PreferencesUtils.loadPrefsToJS(engine, tmpPref, fragmentVar, false);
+                    // we store this fragment as API.fragments['id']
+                } catch (ScriptException ex) {
+                    log("Error: can not load preferences fragment : "+ex.getMessage());
+                }
+            }
+            
+            if ("replace".equals(oper)) {
+                log("Preferences replace: %d keys\n", tmpPref.getAllSettings().size());
+                PreferencesUtils.replacePreferences(tmpPref, mainPrefs);
+            } else if ("append".equals(oper)) {
+                log("Preferences append: %d keys\n", tmpPref.getAllSettings().size());
+                PreferencesUtils.appendPreferences(tmpPref, mainPrefs);
+            }  else if ("delete-values".equals(oper)) {
+                PreferencesUtils.deletePreferenceValues(tmpPref, mainPrefs);
+            }
+        }
+        
+         private void processDeleteElement(Element item) {
+            String path = evalVars(item.getAttribute("path"));
+            String base = evalVars(item.getAttribute("base"));
+            deleteFile(base, path);
+        }
+
+        private void processDownloadElement(Element item) {
+            String address = evalVars(item.getAttribute("url"));
+            String path = evalVars(item.getAttribute("path"));
+            String unzip = evalVars(item.getAttribute("unzip"));
+            String mkdir = evalVars(item.getAttribute("mkdir"));
+
+            String base = evalVars(item.getAttribute("base"));
+            String dir = getDirectoryByAbbr(base);
+            if (dir==null) {
+                log("Error: Can not find directory to place file, use base=cache, base=prefs or base=plugins attribute.");
+                return;
+            }
+
+
+            
+            if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+                return; // some basic protection
+            }
+            if (address == null || path == null || address.length() == 0 || path.length() == 0) {
+                log("Error: Please specify url=\"where to get file\" and path=\"where to place it\"");
+                return;
+            }
+            processDownloadOperation(address, path, dir, "true".equals(mkdir), "true".equals(unzip));
+        }
+        
+        private void processPluginInstallElement(Element elem) {
+            String install = elem.getAttribute("install");
+            String uninstall = elem.getAttribute("remove");
+            String delete = elem.getAttribute("delete");
+            log("PLUGIN: install %s, remove %s, delete %s", install, uninstall, delete);
+            pluginOperation(install, uninstall, delete);
+        }
+        
+        private void processMsgBoxElement(Element elem) {
+            String text = evalVars(elem.getAttribute("text"));
+            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
+            if (locText!=null && locText.length()>0) text=locText;
+
+            String type = evalVars(elem.getAttribute("type"));
+            messageBox(type, text);
+        }
+        
+
+        private void processAskElement(Element elem) {
+            String text = evalVars(elem.getAttribute("text"));
+            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
+            if (locText.length()>0) text=locText;
+            String var = elem.getAttribute("var");
+            if (var.length()==0) var="result";
+            
+            String input = evalVars(elem.getAttribute("input"));
+            if ("true".equals(input)) {
+                setVar(var, askForText(text));
+            } else {
+                String opts = evalVars(elem.getAttribute("options"));
+                String locOpts = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".options"));
+                if (locOpts.length()>0) opts=locOpts;
+                setVar(var, String.valueOf(askForOption(text, opts)));
+            }
+        }
+
+        public void setVar(String name, String value) {
+            try {
+                engine.eval(name+"='"+value+"';");
+            } catch (ScriptException ex) {
+                log("Error: Can not assign variable: %s=%s  : %s\n", name, value, ex.getMessage());
+            }
+        }
+        
+        private void processIfElement(Element elem) {
+            String realValue = evalVars(elem.getAttribute("test"));
+            boolean v=false;
+            if ("true".equals(realValue)) v=true; else
+            if ("fales".equals(realValue)) v=true; else
+            {
+                log("Error: Illegal test expression in if: %s=%s\n", elem.getAttribute("test"), realValue);
+            }
+                
+            if (v) processXmlFragment(elem); 
+            lastV = v;
+        }
+
+        private void processElseElement(Element elem) {
+            if (!lastV) {
+                processXmlFragment(elem); 
+            }
+        }
+
+        private boolean processRunTaskElement(Element elem) {
+            String taskName = elem.getAttribute("name");
+            Element task = tasksMap.get(taskName);
+            if (task!=null) {
+                log("EXECUTING TASK "+taskName);
+                processXmlFragment(task); // process task recursively
+            } else {
+                log("Error: Can not execute task "+taskName);
+                return true;
+            }
+            return false;
+        }
+        
+                
+        private void processScriptElement(Element elem) {
+            String js = elem.getChildNodes().item(0).getTextContent();
+            log("Processing script...");
+            try {
+                PreferencesUtils.modifyPreferencesByScript(engine, mainPrefs, js);
+            } catch (ScriptException ex) {
+                messageBox("e", ex.getMessage());
+                log("JS error: "+ex.getMessage());
+            }
+            log("Script finished");
+        }
+        
+        /**
+         * subsititute ${expression} = expression evaluated by JavaScript
+         */
+        private String evalVars(String s) {
+            Pattern p = Pattern.compile("\\$\\{(.*)\\}");
+            Matcher mr =  p.matcher(s);
+            StringBuffer sb = new StringBuffer();
+            while (mr.find()) {
+            try {
+                String result = engine.eval(mr.group(1)).toString();
+                mr.appendReplacement(sb, result);
+            } catch (ScriptException ex) {
+                log("Error: Can not evaluate expression %s : %s",  mr.group(1), ex.getMessage());
+                //mr.appendReplacement(sb, mr.group(0));
+            }
+            }
+            mr.appendTail(sb);
+            return sb.toString();
+        }
+
+        private Preferences readPreferencesFromDOMElement(Element item) {
+            Preferences tmpPref = new Preferences();
+            try {
+                Transformer xformer = TransformerFactory.newInstance().newTransformer();
+                CharArrayWriter outputWriter = new CharArrayWriter(8192);
+                StreamResult out = new StreamResult(outputWriter);
+
+                xformer.transform(new DOMSource(item), out);
+                
+                String fragmentWithReplacedVars= evalVars(outputWriter.toString());
+
+                CharArrayReader reader = new CharArrayReader(fragmentWithReplacedVars.toCharArray());
+                tmpPref.fromXML(reader);
+            } catch (Exception ex) {
+                log("Error: can not read XML fragment :" + ex.getMessage());
+            } 
+
+            return tmpPref;
+        }
+
+
+    }
+
+    public static class PreferencesUtils {
+    
+        private static void replacePreferences(Preferences fragment, Preferences mainpref) {
+            // normal prefs
+            for (Entry<String, String> entry : fragment.properties.entrySet()) {
+                mainpref.put(entry.getKey(), entry.getValue());
+            }
+            // "list"
+            for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
+                mainpref.putCollection(entry.getKey(), entry.getValue());
+            }
+            // "lists"
+            for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
+                ArrayList<Collection<String>> array = new ArrayList<Collection<String>>();
+                array.addAll(entry.getValue());
+                mainpref.putArray(entry.getKey(), array);
+            }
+            /// "maps"
+            for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
+                mainpref.putListOfStructs(entry.getKey(), entry.getValue());
+            }
+
+        }
+
+        private static void appendPreferences(Preferences fragment, Preferences mainpref) {
+            // normal prefs
+            for (Entry<String, String> entry : fragment.properties.entrySet()) {
+                mainpref.put(entry.getKey(), entry.getValue());
+            }
+
+            // "list"
+            for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
+                String key = entry.getKey();
+
+                Collection<String> newItems = getCollection(mainpref, key, true);
+                if (newItems == null) continue;
+
+                for (String item : entry.getValue()) {
+                    // add nonexisting elements to then list
+                    if (!newItems.contains(item)) {
+                        newItems.add(item);
+                    }
+                }
+                mainpref.putCollection(entry.getKey(), newItems);
+            }
+
+            // "lists"
+            for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
+                String key = entry.getKey();
+
+                Collection<Collection<String>> newLists = getArray(mainpref, key, true);
+                if (newLists == null) continue;
+
+                for (Collection<String> list : entry.getValue()) {
+                    // add nonexisting list (equals comparison for lists is used implicitly)
+                    if (!newLists.contains(list)) {
+                        newLists.add(list);
+                    }
+                }
+                mainpref.putArray(entry.getKey(), newLists);
+            }
+
+            /// "maps" 
+            for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
+                String key = entry.getKey();
+
+                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
+                if (newMaps == null) continue;
+
+                // get existing properties as list of maps 
+
+                for (Map<String, String> map : entry.getValue()) {
+                    // add nonexisting map (equals comparison for maps is used implicitly)
+                    if (!newMaps.contains(map)) {
+                        newMaps.add(map);
+                    }
+                }
+                mainpref.putListOfStructs(entry.getKey(), newMaps);
+            }
+        }
+        
+        /**
+     * Delete items from @param mainpref collections that match items from @param fragment collections
+     */
+    private static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
+
+
+        // normal prefs
+        for (Entry<String, String> entry : fragment.properties.entrySet()) {
+            // if mentioned value found, delete it
+            if (entry.getValue().equals(mainpref.properties.get(entry.getKey()))) {
+                mainpref.put(entry.getKey(), null);
+            }
+        }
+
+        // "list"
+        for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
+            String key = entry.getKey();
+
+            Collection<String> newItems = getCollection(mainpref, key, true);
+            if (newItems == null) continue;
+
+            // remove mentioned items from collection
+            for (String item : entry.getValue()) {
+                log("Deleting preferences: from list %s: %s\n", key, item);
+                newItems.remove(item);
+            }
+            mainpref.putCollection(entry.getKey(), newItems);
+        }
+
+        // "lists"
+        for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
+            String key = entry.getKey();
+
+            
+            Collection<Collection<String>> newLists = getArray(mainpref, key, true);
+            if (newLists == null) continue;
+            
+            // if items are found in one of lists, remove that list!
+            Iterator<Collection<String>> listIterator = newLists.iterator();
+            while (listIterator.hasNext()) {
+                Collection<String> list = listIterator.next();
+                for (Collection<String> removeList : entry.getValue()) {
+                    if (list.containsAll(removeList)) {
+                        // remove current list, because it matches search criteria
+                        log("Deleting preferences: list from lists %s: %s\n", key, list);
+                        listIterator.remove();
+                    }
+                }
+            }
+
+            mainpref.putArray(entry.getKey(), newLists);
+        }
+
+        /// "maps" 
+        for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
+            String key = entry.getKey();
+
+            List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
+            if (newMaps == null) continue;
+                
+            Iterator<Map<String, String>> mapIterator = newMaps.iterator();
+            while (mapIterator.hasNext()) {
+                Map<String, String> map = mapIterator.next();
+                for (Map<String, String> removeMap : entry.getValue()) {
+                    if (map.entrySet().containsAll(removeMap.entrySet())) {
+                        // the map contain all mentioned key-value pair, so it should be deleted from "maps"
+                        log("Deleting preferences:  deleting map from maps %s: %s\n", key, map);
+                        mapIterator.remove();
+                    }
+                }
+            }
+            mainpref.putListOfStructs(entry.getKey(), newMaps);
+        }
+    }
+        
+    private static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
+        Map<String, Setting> allSettings = pref.getAllSettings();
+        for (String key : allSettings.keySet()) {
+            if (key.matches(pattern)) {
+                log("Deleting preferences:  deleting key from preferences: " + key);
+                pref.putSetting(key, allSettings.get(key).getNullInstance());
+            }
+        }
+    }
+
+    private static void deletePreferenceKey(String key, Preferences pref) {
+        Map<String, Setting> allSettings = pref.getAllSettings();
+        if (allSettings.containsKey(key)) {
+            log("Deleting preferences:  deleting key from preferences: " + key);
+            pref.putSetting(key, allSettings.get(key).getNullInstance());
+        }
+    }
+    
+    private static Collection<String> getCollection(Preferences mainpref, String key, boolean warnUnknownDefault)  {
+            Collection<String> existing = mainpref.collectionProperties.get(key);
+            Collection<String> defaults = mainpref.collectionDefaults.get(key);
+
+            if (existing == null && defaults == null) {
+                if (warnUnknownDefault) defaultUnknownWarning(key);
+                return null;
+            }
+            return  (existing != null)
+                    ? new ArrayList<String>(existing) : new ArrayList<String>(defaults);
+    }
+    
+    private static Collection<Collection<String>> getArray(Preferences mainpref, String key, boolean warnUnknownDefault)  {
+            Collection<List<String>> existing = mainpref.arrayProperties.get(key);
+            Collection<List<String>> defaults = mainpref.arrayDefaults.get(key);
+
+            if (existing == null && defaults == null) {
+                if (warnUnknownDefault) defaultUnknownWarning(key);
+                return null;
+            }
+
+            return  (existing != null)
+                    ? new ArrayList<Collection<String>>(existing) : new ArrayList<Collection<String>>(defaults);
+    }
+
+    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault)  {
+            Collection<Map<String, String>> existing = mainpref.listOfStructsProperties.get(key);
+            Collection<Map<String, String>> defaults = mainpref.listOfStructsDefaults.get(key);
+
+            if (existing == null && defaults == null) {
+                if (warnUnknownDefault) defaultUnknownWarning(key);
+                return null;
+            }
+
+            return (existing != null)
+                    ? new ArrayList<Map<String, String>>(existing) : new ArrayList<Map<String, String>>(defaults);
+    }
+    
+    
+
+    private static void defaultUnknownWarning(String key) {
+        log("Warning: Unknown default value of %s , skipped\n", key);
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                tr("<html>Settings file asks to append preferences to <b>{0}</b>,<br/> but it's default value is unknown at this moment.<br/> Please activate corresponding function manually and retry importing.", key),
+                tr("Warning"),
+                JOptionPane.WARNING_MESSAGE);
+    }
+
+    private static void showPrefs(Preferences tmpPref) {
+        System.out.println("properties: " + tmpPref.properties);
+        System.out.println("collections: " + tmpPref.collectionProperties);
+        System.out.println("arrays: " + tmpPref.arrayProperties);
+        System.out.println("maps: " + tmpPref.listOfStructsProperties);
+    }
+    
+    private static void modifyPreferencesByScript(ScriptEngine engine, Preferences tmpPref, String js) throws ScriptException {
+        loadPrefsToJS(engine, tmpPref, "API.pref", true);
+        engine.eval(js);
+        readPrefsFromJS(engine, tmpPref, "API.pref");
+    }
+
+    public static void readPrefsFromJS(ScriptEngine engine, Preferences tmpPref, String varInJS) throws ScriptException {
+            String finish =
+            "stringMap = new java.util.TreeMap ;"+
+            "listMap =  new java.util.TreeMap ;"+
+            "listlistMap = new java.util.TreeMap ;"+
+            "listmapMap =  new java.util.TreeMap ;"+
+            "for (key in "+varInJS+") {"+
+            "  val = "+varInJS+"[key];"+
+            "  type = typeof val == 'string' ? 'string' : val.type;"+
+            "  if (type == 'string') {"+
+            "    stringMap.put(key, val);"+
+            "  } else if (type == 'list') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      l.add(java.lang.String.valueOf(val[i]));"+
+            "    }"+
+            "    listMap.put(key, l);"+
+            "  } else if (type == 'listlist') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      list=val[i];"+
+            "      jlist=new java.util.ArrayList;"+
+            "      for (j=0; j<list.length; j++) {"+
+            "         jlist.add(java.lang.String.valueOf(list[j]));"+
+            "      }"+
+            "      l.add(jlist);"+
+            "    }"+
+            "    listlistMap.put(key, l);"+
+            "  } else if (type == 'listmap') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      map=val[i];"+
+            "      jmap=new java.util.TreeMap;"+
+            "      for (var key2 in map) {"+
+            "         jmap.put(key2,java.lang.String.valueOf(map[key2]));"+
+            "      }"+
+            "      l.add(jmap);"+
+            "    }"+
+            "    listmapMap.put(key, l);"+
+            "  }  else {" +
+            "   org.openstreetmap.josm.data.CustomConfigurator.log('Unknown type:'+val.type+ '- use list, listlist or listmap'); }"+
+            "  }";
+            engine.eval(finish);
+            
+            Map<String, String> stringMap =  (Map<String, String>) engine.get("stringMap");
+            Map<String, List<String>> listMap = (SortedMap<String, List<String>> ) engine.get("listMap");
+            Map<String, List<Collection<String>>> listlistMap = (SortedMap<String, List<Collection<String>>>) engine.get("listlistMap");
+            Map<String, List<Map<String, String>>> listmapMap = (SortedMap<String, List<Map<String,String>>>) engine.get("listmapMap");
+            
+            tmpPref.properties.clear();
+            tmpPref.collectionProperties.clear();
+            tmpPref.arrayProperties.clear();
+            tmpPref.listOfStructsProperties.clear();
+            
+            for (Entry<String, String> e : stringMap.entrySet()) {
+                if (e.getValue().equals( tmpPref.defaults.get(e.getKey())) ) continue;
+                tmpPref.properties.put(e.getKey(), e.getValue());
+            }
+
+            for (Entry<String, List<String>> e : listMap.entrySet()) {
+                if (Preferences.equalCollection(e.getValue(), tmpPref.collectionDefaults.get(e.getKey()))) continue;
+                tmpPref.collectionProperties.put(e.getKey(), e.getValue());
+            }
+            
+            for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
+                if (Preferences.equalArray(e.getValue(), tmpPref.arrayDefaults.get(e.getKey()))) continue;
+                tmpPref.arrayProperties.put(e.getKey(), (List<List<String>>)(List)e.getValue());
+            }
+            
+            for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
+                if (Preferences.equalListOfStructs(e.getValue(), tmpPref.listOfStructsDefaults.get(e.getKey()))) continue;
+                tmpPref.listOfStructsProperties.put(e.getKey(), e.getValue());
+            }
+            
+    }
+    
+    public static void loadPrefsToJS(ScriptEngine engine, Preferences tmpPref, String whereToPutInJS, boolean includeDefaults) throws ScriptException {
+            Map<String, String> stringMap =  new TreeMap<String, String>();
+            Map<String, List<String>> listMap = new TreeMap<String, List<String>>();
+            Map<String, List<List<String>>> listlistMap = new TreeMap<String, List<List<String>>>();
+            Map<String, List<Map<String, String>>> listmapMap = new TreeMap<String, List<Map<String, String>>>();
+            
+            if (includeDefaults) {
+                stringMap.putAll(tmpPref.defaults);
+                listMap.putAll(tmpPref.collectionDefaults);
+                listlistMap.putAll(tmpPref.arrayDefaults);
+                listmapMap.putAll(tmpPref.listOfStructsDefaults);
+            }
+            
+            while (stringMap.values().remove(null)) { };
+            while (listMap.values().remove(null)) { };
+            while (listlistMap.values().remove(null)) { };
+            while (listmapMap.values().remove(null)) { };
+            
+            stringMap.putAll(tmpPref.properties);
+            listMap.putAll(tmpPref.collectionProperties);
+            listlistMap.putAll(tmpPref.arrayProperties);
+            listmapMap.putAll(tmpPref.listOfStructsProperties);
+            
+            engine.put("stringMap", stringMap);
+            engine.put("listMap", listMap);
+            engine.put("listlistMap", listlistMap);
+            engine.put("listmapMap", listmapMap);
+            
+            String init =
+                "function getJSList( javaList ) {"+
+                " var jsList; var i; "+
+                " if (javaList == null) return null;"+
+                "jsList = [];"+
+                "  for (i = 0; i < javaList.size(); i++) {"+
+                "    jsList.push(String(list.get(i)));"+
+                "  }"+
+                "return jsList;"+
+                "}"+
+                "function getJSMap( javaMap ) {"+
+                " var jsMap; var it; var e; "+
+                " if (javaMap == null) return null;"+
+                " jsMap = {};"+
+                " for (it = javaMap.entrySet().iterator(); it.hasNext();) {"+
+                "    e = it.next();"+
+                "    jsMap[ String(e.getKey()) ] = String(e.getValue()); "+
+                "  }"+
+                "  return jsMap;"+
+                "}"+
+                "for (it = stringMap.entrySet().iterator(); it.hasNext();) {"+
+                "  e = it.next();"+
+                whereToPutInJS+"[String(e.getKey())] = String(e.getValue());"+
+                "}\n"+
+                "for (it = listMap.entrySet().iterator(); it.hasNext();) {"+
+                "  e = it.next();"+
+                "  list = e.getValue();"+
+                "  jslist = getJSList(list);"+
+                "  jslist.type = 'list';"+
+                whereToPutInJS+"[String(e.getKey())] = jslist;"+
+                "}\n"+
+                "for (it = listlistMap.entrySet().iterator(); it.hasNext(); ) {"+
+                "  e = it.next();"+
+                "  listlist = e.getValue();"+
+                "  jslistlist = [];"+
+                "  for (it2 = listlist.iterator(); it2.hasNext(); ) {"+
+                "    list = it2.next(); "+
+                "    jslistlist.push(getJSList(list));"+
+                "    }"+
+                "  jslistlist.type = 'listlist';"+
+                whereToPutInJS+"[String(e.getKey())] = jslistlist;"+
+                "}\n"+
+                "for (it = listmapMap.entrySet().iterator(); it.hasNext();) {"+
+                "  e = it.next();"+
+                "  listmap = e.getValue();"+
+                "  jslistmap = [];"+
+                "  for (it2 = listmap.iterator(); it2.hasNext();) {"+
+                "    map = it2.next();"+
+                "    jslistmap.push(getJSMap(map));"+
+                "    }"+
+                "  jslistmap.type = 'listmap';"+
+                whereToPutInJS+"[String(e.getKey())] = jslistmap;"+
+                "}\n";
+                
+                //System.out.println("map1: "+stringMap );
+                //System.out.println("lists1: "+listMap );
+                //System.out.println("listlist1: "+listlistMap );
+                //System.out.println("listmap1: "+listmapMap );
+                engine.eval(init);
+            
+    }
+    }
+}
