Ticket #4421: CustomConfigurator.patch

File CustomConfigurator.patch, 63.3 KB (added by akks, 12 years ago)

minor improvements

  • src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java

     
    22package org.openstreetmap.josm.gui.preferences.advanced;
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
     5import static org.openstreetmap.josm.tools.I18n.marktr;
    56
     7import java.awt.Color;
    68import java.awt.Component;
    79import java.awt.Dimension;
    810import java.awt.Font;
     
    1113import java.awt.event.ActionListener;
    1214import java.awt.event.MouseAdapter;
    1315import java.awt.event.MouseEvent;
     16
     17import java.io.File;
    1418import java.util.ArrayList;
    1519import java.util.Collection;
    1620import java.util.Collections;
     21import java.util.Comparator;
    1722import java.util.List;
    1823import java.util.Map;
    1924import java.util.Map.Entry;
     
    2227import javax.swing.ButtonGroup;
    2328import javax.swing.DefaultCellEditor;
    2429import javax.swing.JButton;
     30import javax.swing.JFileChooser;
    2531import javax.swing.JLabel;
    2632import javax.swing.JOptionPane;
    2733import javax.swing.JPanel;
     
    3137import javax.swing.JTextField;
    3238import javax.swing.event.DocumentEvent;
    3339import javax.swing.event.DocumentListener;
     40import javax.swing.filechooser.FileFilter;
    3441import javax.swing.table.DefaultTableCellRenderer;
    3542import javax.swing.table.DefaultTableModel;
    3643
    3744import org.openstreetmap.josm.Main;
     45import org.openstreetmap.josm.data.CustomConfigurator;
    3846import org.openstreetmap.josm.data.Preferences;
    3947import org.openstreetmap.josm.data.Preferences.ListListSetting;
    4048import org.openstreetmap.josm.data.Preferences.ListSetting;
     
    4250import org.openstreetmap.josm.data.Preferences.Setting;
    4351import org.openstreetmap.josm.data.Preferences.StringSetting;
    4452import org.openstreetmap.josm.gui.ExtendedDialog;
     53import org.openstreetmap.josm.gui.actionsupport.LogShowDialog;
    4554import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
    4655import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
    4756import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
     
    5766            return new AdvancedPreference();
    5867        }
    5968    }
    60    
     69
    6170    private AdvancedPreference() {
    6271        super("advanced", tr("Advanced Preferences"), tr("Setting Preference entries directly. Use with caution!"));
    6372    }
     
    109118        public boolean isChanged() {
    110119            return changed;
    111120        }
    112 
     121   
     122        private void markAsChanged() {
     123            changed = true;
     124        }
     125   
    113126        public void reset() {
    114127            value = defaultValue;
    115128            changed = true;
     
    154167                applyFilter();
    155168            }
    156169        });
    157 
    158         Map<String, Setting> orig = Main.pref.getAllSettings();
    159         Map<String, Setting> defaults = Main.pref.getAllDefaults();
    160         orig.remove("osm-server.password");
    161         defaults.remove("osm-server.password");
    162         prepareData(orig, defaults);
     170        readPreferences(Main.pref);
    163171        model = new AllSettingsTableModel();
    164172        applyFilter();
    165173
     
    197205            }
    198206        });
    199207
     208        JButton read = new JButton(tr("Read from file"));
     209        p.add(read, GBC.std().insets(5,5,0,0));
     210        read.addActionListener(new ActionListener(){
     211            public void actionPerformed(ActionEvent e) {
     212                File[] files = askUserForCustomSettingsFiles(false, tr("Open JOSM customization file"));
     213                if (files.length==0) return;
     214               
     215                Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
     216               
     217                StringBuilder log = new StringBuilder();
     218                log.append("<html>");
     219                for (File f: files) {
     220                    CustomConfigurator.readXML(f, tmpPrefs);
     221                    log.append(CustomConfigurator.getLog());
     222                }
     223                //try { Main.pref.save();  } catch (IOException ex) { }
     224                log.append("</html>");
     225                String msg = log.toString().replace("\n", "<br/>");
     226               
     227                new LogShowDialog(tr("Import log"), tr("<html>Here is file import summary. <br/>"
     228                        + "You can reject preferences changes by pressing \"Cancel\" in preferences dialog <br/>"
     229                        + "To activate some changes JOSM restart may be needed.</html>"), msg).showDialog();
     230               
     231               
     232                //JOptionPane.showMessageDialog(Main.parent,
     233                //   tr("Installed plugins and some changes in preferences will start to work after JOSM restart"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
     234                readPreferences(tmpPrefs);
     235                // sorting - first modified, then non-default, then default entries
     236                Collections.sort(data, new Comparator<PrefEntry>() {
     237                    @Override
     238                    public int compare(PrefEntry o1, PrefEntry o2) {
     239                        if (o1.changed && !o2.changed) return -1;
     240                        if (o2.changed && !o1.changed) return 1;
     241                        if (!(o1.isDefault) && o2.isDefault) return -1;
     242                        if (!(o2.isDefault) && o1.isDefault) return 1;
     243                        return o1.key.compareTo(o2.key);
     244                    }
     245                  });
     246
     247                applyFilter();
     248                ((AllSettingsTableModel) list.getModel()).fireTableDataChanged();
     249            }
     250
     251        });
     252       
     253        JButton export = new JButton(tr("Export selected items"));
     254        p.add(export, GBC.std().insets(5,5,0,0));
     255        export.addActionListener(new ActionListener(){
     256            public void actionPerformed(ActionEvent e) {
     257                ArrayList<String> keys = new ArrayList<String>();
     258                boolean hasLists = false;
     259                for (int row : list.getSelectedRows()) {
     260                    PrefEntry p = (PrefEntry) model.getValueAt(row, -1);
     261                    if (!p.isDefault()) {
     262                        // preferences with default values are not saved
     263                        if (!(p.getValue() instanceof StringSetting)) hasLists=true; // => append and replace differs
     264                        keys.add(p.getKey());
     265                    }
     266                }
     267                if (keys.size()==0) {
     268                     JOptionPane.showMessageDialog(Main.parent,
     269                        tr("Please select some preference keys not marked as default"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
     270                     return;
     271                }
     272
     273                File[] files = askUserForCustomSettingsFiles(true, tr("Export preferences keys to JOSM customization file"));
     274                if (files.length==0) return;
     275               
     276                int answer = 0;
     277                if (hasLists) {
     278                    answer = JOptionPane.showOptionDialog(Main.parent, tr("What to with preference lists when this file is to be imported?"), tr("Question"),
     279                       JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
     280                        null, new String[]{"Append preferences from file to existing values","Replace existing values"},0);
     281                }
     282                CustomConfigurator.exportPreferencesKeysToFile(files[0].getAbsolutePath(), answer==0, keys);
     283            }
     284        });
     285
     286
    200287        list.addMouseListener(new MouseAdapter(){
    201288            @Override public void mouseClicked(MouseEvent e) {
    202289                if (e.getClickCount() == 2) {
     
    206293        });
    207294    }
    208295
    209     private void prepareData(Map<String, Setting> orig, Map<String, Setting> defaults) {
     296    private void readPreferences(Preferences tmpPrefs) {
     297        Map<String, Setting> loaded;
     298        Map<String, Setting> orig = Main.pref.getAllSettings();
     299        Map<String, Setting> defaults = tmpPrefs.getAllDefaults();
     300        orig.remove("osm-server.password");
     301        defaults.remove("osm-server.password");
     302        if (tmpPrefs != Main.pref) {
     303            loaded = tmpPrefs.getAllSettings();
     304        } else {
     305            loaded = orig;
     306        }
     307        prepareData(loaded, orig, defaults);
     308    }
     309   
     310    private File[] askUserForCustomSettingsFiles(boolean saveFileFlag, String title) {
     311        String dir = Main.pref.get("customsettings.lastDirectory");
     312        if (dir.length()==0) dir =".";
     313       
     314        JFileChooser fc = new JFileChooser(dir);
     315        fc.setDialogTitle(title);
     316        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
     317        fc.setAcceptAllFileFilterUsed(false);
     318        fc.setFileFilter(new FileFilter() {
     319            @Override
     320            public boolean accept(File f) {
     321                return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml");
     322            }
     323            @Override
     324            public String getDescription() {
     325                return tr("JOSM custom settings files (*.xml)");
     326            }
     327            });
     328       
     329            fc.setMultiSelectionEnabled(!saveFileFlag);
     330            int result = saveFileFlag? fc.showSaveDialog(Main.parent) : fc.showOpenDialog(Main.parent);
     331            if (result == JFileChooser.APPROVE_OPTION) {
     332                if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) {
     333                    Main.pref.put("customsettings.lastDirectory", fc.getCurrentDirectory().getAbsolutePath());
     334                }
     335                File sel[] = fc.isMultiSelectionEnabled() ? fc.getSelectedFiles() : (new File[]{fc.getSelectedFile()});
     336                if (sel.length==1 && !sel[0].getName().contains(".")) sel[0]=new File(sel[0].getAbsolutePath()+".xml");
     337                return sel;
     338            }
     339            return new File[0];
     340    }
     341           
     342    private void prepareData(Map<String, Setting> loaded, Map<String, Setting> orig, Map<String, Setting> defaults) {
    210343        data = new ArrayList<PrefEntry>();
    211         for (Entry<String, Setting> e : orig.entrySet()) {
     344        for (Entry<String, Setting> e : loaded.entrySet()) {
    212345            Setting value = e.getValue();
     346            Setting old = orig.get(e.getKey());
    213347            Setting def = defaults.get(e.getKey());
    214348            if (def == null) {
    215349                def = value.getNullInstance();
    216350            }
    217351            PrefEntry en = new PrefEntry(e.getKey(), value, def, false);
     352            // after changes we have nondefault value. Value is changed if is not equal to old value
     353            if ( !Preferences.isEqual(old, value) ) {
     354                en.markAsChanged();
     355            }
    218356            data.add(en);
    219357        }
    220358        for (Entry<String, Setting> e : defaults.entrySet()) {
    221             if (!orig.containsKey(e.getKey())) {
     359            if (!loaded.containsKey(e.getKey())) {
    222360                PrefEntry en = new PrefEntry(e.getKey(), e.getValue(), e.getValue(), true);
     361                // after changes we have default value. So, value is changed if old value is not default
     362                Setting old = orig.get(e.getKey());
     363                if ( old!=null ) {
     364                    en.markAsChanged();
     365                }
    223366                data.add(en);
    224367            }
    225368        }
     
    271414            Setting setting = pe.getValue();
    272415            Object val = setting.getValue();
    273416            String display = val != null ? val.toString() : "<html><i>&lt;"+tr("unset")+"&gt;</i></html>";
    274 
     417           
    275418            JLabel label = (JLabel)super.getTableCellRendererComponent(table,
    276419                    display, isSelected, hasFocus, row, column);
     420
     421            label.setBackground(Main.pref.getUIColor("Table.background"));
     422            if (isSelected) {
     423                label.setForeground(Main.pref.getUIColor("Table.foreground"));
     424            }
     425            if(pe.isChanged()) {
     426                label.setBackground(Main.pref.getColor(
     427                        marktr("Advanced Background: Changed"),
     428                        new Color(200,255,200)));
     429            } else if(!pe.isDefault()) {
     430                label.setBackground(Main.pref.getColor(
     431                        marktr("Advanced Background: NonDefalut"),
     432                        new Color(255,255,200)));
     433            }
     434
    277435            if (!pe.isDefault()) {
    278436                label.setFont(label.getFont().deriveFont(Font.BOLD));
    279437            }
  • src/org/openstreetmap/josm/gui/actionsupport/LogShowDialog.java

     
     1package org.openstreetmap.josm.gui.actionsupport;
     2
     3import java.awt.Dimension;
     4import java.awt.GridBagLayout;
     5import javax.swing.*;
     6import org.openstreetmap.josm.Main;
     7import org.openstreetmap.josm.data.Preferences;
     8import org.openstreetmap.josm.gui.ExtendedDialog;
     9import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
     10import org.openstreetmap.josm.gui.preferences.advanced.AdvancedPreference;
     11import org.openstreetmap.josm.gui.widgets.HtmlPanel;
     12import org.openstreetmap.josm.tools.GBC;
     13
     14import static org.openstreetmap.josm.tools.I18n.tr;
     15
     16/**
     17 * Generic dialog with message and scrolling area
     18 * @author Alexei
     19 */
     20public class LogShowDialog extends ExtendedDialog {
     21
     22
     23    public LogShowDialog (String title, String msg, String log) {
     24        super(Main.parent, title, new String[] {tr("OK")});
     25        setButtonIcons(new String[] {"ok.png"});
     26        setContent(build(msg, log));
     27    }
     28
     29    protected JPanel build(String msg, String log) {
     30        JPanel p = new JPanel(new GridBagLayout());
     31        JLabel lbl = new JLabel(msg);
     32       
     33        lbl.setFont(lbl.getFont().deriveFont(0, 14));
     34       
     35        p.add(lbl, GBC.eol().insets(5,0,5,0));
     36        JEditorPane txt = new JEditorPane();
     37        txt.setContentType("text/html");
     38        txt.setText(log);
     39        txt.setEditable(false);
     40        txt.setOpaque(false);
     41       
     42        JScrollPane sp = new JScrollPane(txt);
     43        sp.setOpaque(false);
     44        sp.setPreferredSize(new Dimension(600,300));
     45       
     46       
     47        p.add(sp, GBC.eop().insets(5,15,0,0).fill(GBC.HORIZONTAL));
     48
     49        return p;
     50    }
     51}
     52 
     53
  • src/org/openstreetmap/josm/data/Preferences.java

     
    17481748                System.out.println(tr("Preference setting {0} has been removed since it is no longer used.", key));
    17491749        }
    17501750    }
     1751
     1752    public static boolean isEqual(Setting a, Setting b) {
     1753        if (a==null && b==null) return true;
     1754        if (a==null) return false;
     1755        if (b==null) return false;
     1756        if (a==b) return true;
     1757       
     1758        if (a instanceof StringSetting)
     1759            return (a.getValue().equals(b.getValue()));
     1760        if (a instanceof ListSetting)
     1761            return equalCollection((Collection<String>) a.getValue(), (Collection<String>) b.getValue());
     1762        if (a instanceof ListListSetting)
     1763            return equalArray((Collection<Collection<String>>) a.getValue(), (Collection<List<String>>) b.getValue());
     1764        if (a instanceof MapListSetting)
     1765            return equalListOfStructs((Collection<Map<String, String>>) a.getValue(), (Collection<Map<String, String>>) b.getValue());
     1766        return a.equals(b);
     1767    }
     1768
    17511769}
  • src/org/openstreetmap/josm/data/CustomConfigurator.java

     
     1package org.openstreetmap.josm.data;
     2
     3import java.lang.reflect.InvocationTargetException;
     4import java.util.concurrent.ExecutionException;
     5import java.util.concurrent.TimeoutException;
     6import java.util.logging.Level;
     7import java.util.logging.Logger;
     8import javax.script.ScriptException;
     9import org.openstreetmap.josm.Main;
     10import org.openstreetmap.josm.data.Preferences.Setting;
     11import static org.openstreetmap.josm.tools.I18n.tr;
     12
     13import java.io.BufferedInputStream;
     14import java.io.ByteArrayInputStream;
     15import java.io.CharArrayReader;
     16import java.io.CharArrayWriter;
     17import java.io.File;
     18import java.io.FileInputStream;
     19import java.io.IOException;
     20import java.io.InputStream;
     21
     22import java.util.ArrayList;
     23import java.util.Arrays;
     24import java.util.Collection;
     25import java.util.Collections;
     26import java.util.HashMap;
     27import java.util.HashSet;
     28import java.util.Iterator;
     29import java.util.List;
     30import java.util.Map;
     31import java.util.Map.Entry;
     32import java.util.SortedMap;
     33import java.util.TreeMap;
     34import java.util.concurrent.Future;
     35import java.util.concurrent.TimeUnit;
     36import java.util.regex.Matcher;
     37import java.util.regex.Pattern;
     38import javax.script.ScriptEngine;
     39import javax.script.ScriptEngineManager;
     40import javax.swing.JOptionPane;
     41import javax.swing.SwingUtilities;
     42import javax.xml.parsers.DocumentBuilder;
     43import javax.xml.parsers.DocumentBuilderFactory;
     44import javax.xml.transform.OutputKeys;
     45import javax.xml.transform.Transformer;
     46import javax.xml.transform.TransformerFactory;
     47import javax.xml.transform.dom.DOMSource;
     48import javax.xml.transform.stream.StreamResult;
     49
     50import org.openstreetmap.josm.gui.io.DownloadFileTask;
     51import org.openstreetmap.josm.plugins.PluginDownloadTask;
     52import org.openstreetmap.josm.plugins.PluginInformation;
     53import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
     54import org.openstreetmap.josm.tools.LanguageInfo;
     55import org.w3c.dom.Document;
     56import org.w3c.dom.Element;
     57import org.w3c.dom.Node;
     58import org.w3c.dom.NodeList;
     59
     60/**
     61 * Class to process configuration changes stored in XML
     62 * can be used to modify preferences, store/delete files in .josm folders etc
     63 */
     64public class CustomConfigurator {
     65    private static StringBuilder summary = new StringBuilder();
     66       
     67    public static void log(String fmt, Object... vars) {
     68        summary.append(String.format(fmt, vars));
     69    }
     70   
     71    public static void log(String s) {
     72        summary.append(s);
     73        summary.append("\n");
     74    }
     75   
     76    public static String getLog() {
     77        return summary.toString();
     78    }
     79   
     80    public static void readXML(String dir, String fileName) {
     81        readXML(new File(dir, fileName));
     82    }
     83
     84    public static void readXML(final File file, final Preferences prefs) {
     85        synchronized(CustomConfigurator.class) {
     86            busy=true;
     87        }
     88        new XMLCommandProcessor(prefs).openAndReadXML(file);
     89        synchronized(CustomConfigurator.class) {
     90            CustomConfigurator.class.notifyAll();
     91            busy=false;
     92        }
     93    }
     94   
     95    public static void readXML(File file) {
     96        readXML(file, Main.pref);
     97    }
     98   
     99    public static void downloadFile(String address, String path, String base) {
     100        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, false);
     101    }
     102    public static void downloadAndUnpackFile(String address, String path, String base) {
     103        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, true);
     104    }
     105
     106    public static void processDownloadOperation(String address, String path, String parentDir, boolean mkdir, boolean unzip) {
     107        String dir = parentDir;
     108        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
     109            return; // some basic protection
     110        }
     111        File fOut = new File(dir, path);
     112        DownloadFileTask downloadFileTask = new DownloadFileTask(Main.parent, address, fOut, mkdir, unzip);
     113
     114        Future f = Main.worker.submit(downloadFileTask);
     115        log("Info: downloading file from %s to %s in background ", parentDir, fOut.getAbsolutePath());
     116        if (unzip) log("and unpacking it"); else log("");
     117       
     118    }
     119
     120    public static void messageBox(String type, String text) {
     121        if (type==null || type.length()==0) type="plain";
     122
     123        switch (type.charAt(0)) {
     124            case 'i': JOptionPane.showMessageDialog(Main.parent, text, tr("Information"), JOptionPane.INFORMATION_MESSAGE); break;
     125            case 'w': JOptionPane.showMessageDialog(Main.parent, text, tr("Warning"), JOptionPane.WARNING_MESSAGE); break;
     126            case 'e': JOptionPane.showMessageDialog(Main.parent, text, tr("Error"), JOptionPane.ERROR_MESSAGE); break;
     127            case 'q': JOptionPane.showMessageDialog(Main.parent, text, tr("Question"), JOptionPane.QUESTION_MESSAGE); break;
     128            case 'p': JOptionPane.showMessageDialog(Main.parent, text, tr("Message"), JOptionPane.PLAIN_MESSAGE); break;
     129        }
     130    }
     131   
     132    public static int askForOption(String text, String opts) {
     133        Integer answer;
     134        if (opts.length()>0) {
     135            String[] options = opts.split(";");
     136            answer = JOptionPane.showOptionDialog(Main.parent, text, "Question", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
     137        } else {
     138            answer = JOptionPane.showOptionDialog(Main.parent, text, "Question", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, 2);
     139        }
     140        if (answer==null) return -1; else return answer;
     141    }
     142
     143    public static String askForText(String text) {
     144        String s = JOptionPane.showInputDialog(Main.parent, text, tr("Enter text"), JOptionPane.QUESTION_MESSAGE);
     145        if (s!=null && (s=s.trim()).length()>0) {
     146            return s;
     147        } else {
     148            return "";
     149        }
     150    }
     151
     152    /**
     153     * This function exports part of user preferences to specified file.
     154     * Default values are not saved.
     155     * @param filename - where to export
     156     * @param append - if true, resulting file cause appending to exuisting preferences
     157     * @param keys - which preferences keys you need to export ("imagery.entries", for example)
     158     */
     159    public static void exportPreferencesKeysToFile(String filename, boolean append, String... keys) {
     160        HashSet<String> keySet = new HashSet<String>();
     161        Collections.addAll(keySet, keys);
     162        exportPreferencesKeysToFile(filename, append, keySet);
     163    }
     164
     165    /**
     166     * This function exports part of user preferences to specified file.
     167     * Default values are not saved.
     168     * Preference keys matching specified pattern are saved
     169     * @param filename - where to export
     170     * @param append - if true, resulting file cause appending to exuisting preferences
     171     * @param pattern - Regexp pattern forh preferences keys you need to export (".*imagery.*", for example)
     172     */
     173    public static void exportPreferencesKeysByPatternToFile(String fileName, boolean append, String pattern) {
     174        ArrayList<String> keySet = new ArrayList<String>();
     175        Map<String, Setting> allSettings = Main.pref.getAllSettings();
     176        for (String key: allSettings.keySet()) {
     177            if (key.matches(pattern)) keySet.add(key);
     178        }
     179        exportPreferencesKeysToFile(fileName, append, keySet);
     180    }
     181
     182    public static void exportPreferencesKeysToFile(String filename, boolean append, Collection<String> keys) {
     183        Element root = null;
     184        Document document = null;
     185        Document exportDocument = null;
     186
     187        try {
     188            String toXML = Main.pref.toXML(true);
     189            InputStream is = new ByteArrayInputStream(toXML.getBytes());
     190            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
     191            builderFactory.setValidating(false);
     192            builderFactory.setNamespaceAware(false);
     193            DocumentBuilder builder = builderFactory.newDocumentBuilder();
     194            document = builder.parse(is);
     195            exportDocument = builder.newDocument();
     196            root = document.getDocumentElement();
     197        } catch (Exception ex) {
     198            System.out.println("Error getting preferences to save:" +ex.getMessage());
     199        }
     200        if (root==null) return;
     201        try {
     202           
     203            Element newRoot = exportDocument.createElement("config");
     204            exportDocument.appendChild(newRoot);
     205           
     206            Element prefElem = exportDocument.createElement("preferences");
     207            prefElem.setAttribute("operation", append?"append":"replace");
     208            newRoot.appendChild(prefElem);
     209
     210            NodeList childNodes = root.getChildNodes();
     211            int n = childNodes.getLength();
     212            for (int i = 0; i < n ; i++) {
     213                Node item = childNodes.item(i);
     214                if (item.getNodeType() == Node.ELEMENT_NODE) {
     215                    String currentKey = ((Element) item).getAttribute("key");
     216                    if (keys.contains(currentKey)) {
     217                        Node imported = exportDocument.importNode(item, true);
     218                        prefElem.appendChild(imported);
     219                    }
     220                }
     221            }
     222            File f = new File(filename);
     223            Transformer ts = TransformerFactory.newInstance().newTransformer();
     224            ts.setOutputProperty(OutputKeys.INDENT, "yes");
     225            ts.transform(new DOMSource(exportDocument), new StreamResult(f.toURI().getPath()));
     226        } catch (Exception ex) {
     227            System.out.println("Error saving preferences part:" +ex.getMessage());
     228            ex.printStackTrace();
     229        }
     230    }
     231   
     232   
     233    public static void deleteFile(String path, String base) {
     234        String dir = getDirectoryByAbbr(base);
     235        if (dir==null) {
     236            log("Error: Can not find base, use base=cache, base=prefs or base=plugins attribute.");
     237            return;
     238        }
     239        log("Delete file: %s\n", path);
     240        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
     241            return; // some basic protection
     242        }
     243        File fOut = new File(dir, path);
     244        if (fOut.exists()) {
     245            deleteFileOrDirectory(fOut);
     246        }
     247        return;
     248    }
     249
     250    public static void deleteFileOrDirectory(String path) {
     251        deleteFileOrDirectory(new File(path));
     252    }
     253   
     254    public static void deleteFileOrDirectory(File f) {
     255        if (f.isDirectory()) {
     256            for (File f1: f.listFiles()) {
     257                deleteFileOrDirectory(f1);
     258            }
     259        }
     260        try {
     261            f.delete();
     262        } catch (Exception e) {
     263            log("Warning: Can not delete file "+f.getPath());
     264        }
     265    }
     266
     267    private static boolean busy=false;
     268
     269   
     270    public static void pluginOperation(String install, String uninstall, String delete)  {
     271        final List<String> installList = Arrays.asList(install.toLowerCase().split(";"));
     272        final List<String> removeList = Arrays.asList(uninstall.toLowerCase().split(";"));
     273        final List<String> deleteList = Arrays.asList(delete.toLowerCase().split(";"));
     274
     275        final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
     276        Runnable r = new Runnable() {
     277            public void run() {
     278                if (task.isCanceled()) return;
     279                synchronized (CustomConfigurator.class) {
     280                try { // proceed only after ll other tasks were finished
     281                    while (busy) CustomConfigurator.class.wait();
     282                } catch (InterruptedException ex) { }
     283                       
     284                SwingUtilities.invokeLater(new Runnable() {
     285                    public void run() {
     286                        List<PluginInformation> availablePlugins = task.getAvailablePlugins();
     287                        List<PluginInformation> toInstallPlugins = new ArrayList<PluginInformation>();
     288                        List<PluginInformation> toRemovePlugins = new ArrayList<PluginInformation>();
     289                        List<PluginInformation> toDeletePlugins = new ArrayList<PluginInformation>();
     290                        for (PluginInformation pi: availablePlugins) {
     291                            //System.out.print(pi.name+";");
     292                            String name = pi.name.toLowerCase();
     293                            if (installList.contains(name)) toInstallPlugins.add(pi);
     294                            if (removeList.contains(name)) toRemovePlugins.add(pi);
     295                            if (deleteList.contains(name)) toDeletePlugins.add(pi);
     296                        }
     297                        if (!installList.isEmpty()) {
     298                            PluginDownloadTask pluginDownloadTask = new PluginDownloadTask(Main.parent, toInstallPlugins, tr ("Installing plugins"));
     299                            Main.worker.submit(pluginDownloadTask);
     300                        }
     301                            Collection<String> pls = new ArrayList<String>(Main.pref.getCollection("plugins"));
     302                            for (PluginInformation pi: toInstallPlugins) {
     303                                if (!pls.contains(pi.name)) pls.add(pi.name);
     304                            }
     305                            for (PluginInformation pi: toRemovePlugins) {
     306                                pls.remove(pi.name);
     307                            }
     308                            for (PluginInformation pi: toDeletePlugins) {
     309                                pls.remove(pi.name);
     310                                new File(Main.pref.getPluginsDirectory(),pi.name+".jar").deleteOnExit();
     311                            }
     312                            Main.pref.putCollection("plugins",pls);
     313                        }
     314                });
     315            }
     316            }
     317
     318        };
     319        Main.worker.submit(task);
     320        Main.worker.submit(r);
     321    }
     322   
     323    private static String getDirectoryByAbbr(String base) {
     324            String dir;
     325            if ("prefs".equals(base) || base.length()==0) {
     326                dir = Main.pref.getPreferencesDir();
     327            } else if ("cache".equals(base)) {
     328                dir = Main.pref.getCacheDirectory().getAbsolutePath();
     329            } else if ("plugins".equals(base)) {
     330                dir = Main.pref.getPluginsDirectory().getAbsolutePath();
     331            } else {
     332                dir = null;
     333            }
     334            return dir;
     335    }
     336
     337    public static Preferences clonePreferences(Preferences pref) {
     338        Preferences tmp = new Preferences();
     339        tmp.defaults.putAll(   pref.defaults );
     340        tmp.properties.putAll( pref.properties );
     341        tmp.arrayDefaults.putAll(   pref.arrayDefaults );
     342        tmp.arrayProperties.putAll( pref.arrayProperties );
     343        tmp.collectionDefaults.putAll(   pref.collectionDefaults );
     344        tmp.collectionProperties.putAll( pref.collectionProperties );
     345        tmp.listOfStructsDefaults.putAll(   pref.listOfStructsDefaults );
     346        tmp.listOfStructsProperties.putAll( pref.listOfStructsProperties );
     347        tmp.colornames.putAll( pref.colornames );
     348       
     349        return tmp;
     350    }
     351
     352
     353    public static class XMLCommandProcessor {
     354       
     355        //ProgressMonitor monitor;
     356
     357        Preferences mainPrefs;
     358        Map<String,Element> tasksMap = new HashMap<String,Element>();
     359       
     360        private boolean lastV; // last If condition result
     361       
     362       
     363        ScriptEngine engine ;
     364       
     365        public void openAndReadXML(File file) {
     366            log("-- Reading custom preferences from " + file.getAbsolutePath() + " --");
     367            InputStream is = null;
     368            try {
     369                is = new BufferedInputStream(new FileInputStream(file));
     370                DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
     371                builderFactory.setValidating(false);
     372                builderFactory.setNamespaceAware(true);
     373                DocumentBuilder builder = builderFactory.newDocumentBuilder();
     374                Document document = builder.parse(is);
     375                synchronized (CustomConfigurator.class) {
     376                    processXML(document);
     377                }
     378            } catch (Exception ex) {
     379                log("Error reading custom preferences: "+ex.getMessage());
     380            } finally {
     381                try {
     382                    if (is != null) {
     383                        is.close();
     384                    }
     385                } catch (IOException ex) {         }
     386            }
     387            log("-- Reading complete --");
     388        }
     389
     390        public XMLCommandProcessor(Preferences mainPrefs) {
     391            try {
     392                this.mainPrefs = mainPrefs;
     393                CustomConfigurator.summary = new StringBuilder();
     394                engine = new ScriptEngineManager().getEngineByName("rhino");
     395                engine.eval("API={}; API.pref={}; API.fragments={};");
     396                String className =  CustomConfigurator.class.getName();
     397                engine.eval("API.messageBox="+className+".messageBox");
     398                engine.eval("API.askText=function(text) { return String("+className+".askForText(text));}");
     399                engine.eval("API.askOption="+className+".askForOption");
     400                engine.eval("API.downloadFile="+className+".downloadFile");
     401                engine.eval("API.downloadAndUnpackFile="+className+".downloadAndUnpackFile");
     402                engine.eval("API.deleteFile="+className+".deleteFile");
     403                engine.eval("API.plugin ="+className+".pluginOperation");
     404                engine.eval("API.pluginInstall = function(names) { "+className+".pluginOperation(names,'','');}");
     405                engine.eval("API.pluginUninstall = function(names) { "+className+".pluginOperation('',names,'');}");
     406                engine.eval("API.pluginDelete = function(names) { "+className+".pluginOperation('','',names);}");
     407            } catch (Exception ex) {
     408                log("Error: initializing script engine: "+ex.getMessage());
     409            }
     410        }
     411       
     412       
     413       
     414       
     415        /*public XMLCommandProcessor(Preferences mainPrefs, String baseDir, ProgressMonitor pm) {
     416            this.mainPrefs = mainPrefs;
     417            this.baseDir = baseDir;
     418            this.monitor = pm;
     419        }*/
     420
     421     
     422        private void processXML(Document document) {
     423            Element root = document.getDocumentElement();
     424            processXmlFragment(root);
     425        }
     426
     427        private void processXmlFragment(Element root) {
     428            NodeList childNodes = root.getChildNodes();
     429            int nops = childNodes.getLength();
     430            for (int i = 0; i < nops; i++) {
     431                Node item = childNodes.item(i);
     432                if (item.getNodeType() != Node.ELEMENT_NODE) continue;
     433                String elementName = item.getNodeName();
     434                //if (monitor!=null) monitor.indeterminateSubTask(elementName);
     435                Element elem = (Element) item;
     436
     437                if ("var".equals(elementName)) {
     438                    setVar(elem.getAttribute("name"), evalVars(elem.getAttribute("value")));
     439                } else if ("task".equals(elementName)) {
     440                    tasksMap.put(elem.getAttribute("name"), elem);
     441                } else if ("runtask".equals(elementName)) {
     442                    if (processRunTaskElement(elem)) return;
     443                } else if ("ask".equals(elementName)) {
     444                    processAskElement(elem);
     445                } else if ("if".equals(elementName)) {
     446                    processIfElement(elem);
     447                } else if ("else".equals(elementName)) {
     448                    processElseElement(elem);
     449                } else if ("break".equals(elementName)) {
     450                    return;
     451                } else if ("plugin".equals(elementName)) {
     452                    processPluginInstallElement(elem);
     453                } else if ("messagebox".equals(elementName)){
     454                    processMsgBoxElement(elem);
     455                } else if ("preferences".equals(elementName)) {
     456                    processPreferencesElement(elem);
     457                } else if ("download".equals(elementName)) {
     458                    processDownloadElement(elem);
     459                } else if ("delete".equals(elementName)) {
     460                    processDeleteElement(elem);
     461                } else if ("script".equals(elementName)) {
     462                    processScriptElement(elem);
     463                } else {
     464                    log("Error: Unknown element " + elementName);
     465                }
     466               
     467            }
     468        }
     469
     470
     471
     472        private void processPreferencesElement(Element item) {
     473            String oper = evalVars(item.getAttribute("operation"));
     474            String id = evalVars(item.getAttribute("id"));
     475           
     476           
     477            if ("delete-keys".equals(oper)) {
     478                String pattern = evalVars(item.getAttribute("pattern"));
     479                String key = evalVars(item.getAttribute("key"));
     480                if (key != null) {
     481                    PreferencesUtils.deletePreferenceKey(key, mainPrefs);
     482                }
     483                if (pattern != null) {
     484                    PreferencesUtils.deletePreferenceKeyByPattern(pattern, mainPrefs);
     485                }
     486                return;
     487            }
     488           
     489            Preferences tmpPref = readPreferencesFromDOMElement(item);
     490            PreferencesUtils.showPrefs(tmpPref);
     491           
     492            if (id.length()>0) {
     493                try {
     494                    String fragmentVar = "API.fragments['"+id+"']";
     495                    engine.eval(fragmentVar+"={};");
     496                    PreferencesUtils.loadPrefsToJS(engine, tmpPref, fragmentVar, false);
     497                    // we store this fragment as API.fragments['id']
     498                } catch (ScriptException ex) {
     499                    log("Error: can not load preferences fragment : "+ex.getMessage());
     500                }
     501            }
     502           
     503            if ("replace".equals(oper)) {
     504                log("Preferences replace: %d keys\n", tmpPref.getAllSettings().size());
     505                PreferencesUtils.replacePreferences(tmpPref, mainPrefs);
     506            } else if ("append".equals(oper)) {
     507                log("Preferences append: %d keys\n", tmpPref.getAllSettings().size());
     508                PreferencesUtils.appendPreferences(tmpPref, mainPrefs);
     509            }  else if ("delete-values".equals(oper)) {
     510                PreferencesUtils.deletePreferenceValues(tmpPref, mainPrefs);
     511            }
     512        }
     513       
     514         private void processDeleteElement(Element item) {
     515            String path = evalVars(item.getAttribute("path"));
     516            String base = evalVars(item.getAttribute("base"));
     517            deleteFile(base, path);
     518        }
     519
     520        private void processDownloadElement(Element item) {
     521            String address = evalVars(item.getAttribute("url"));
     522            String path = evalVars(item.getAttribute("path"));
     523            String unzip = evalVars(item.getAttribute("unzip"));
     524            String mkdir = evalVars(item.getAttribute("mkdir"));
     525
     526            String base = evalVars(item.getAttribute("base"));
     527            String dir = getDirectoryByAbbr(base);
     528            if (dir==null) {
     529                log("Error: Can not find directory to place file, use base=cache, base=prefs or base=plugins attribute.");
     530                return;
     531            }
     532
     533
     534           
     535            if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
     536                return; // some basic protection
     537            }
     538            if (address == null || path == null || address.length() == 0 || path.length() == 0) {
     539                log("Error: Please specify url=\"where to get file\" and path=\"where to place it\"");
     540                return;
     541            }
     542            processDownloadOperation(address, path, dir, "true".equals(mkdir), "true".equals(unzip));
     543        }
     544       
     545        private void processPluginInstallElement(Element elem) {
     546            String install = elem.getAttribute("install");
     547            String uninstall = elem.getAttribute("remove");
     548            String delete = elem.getAttribute("delete");
     549            log("PLUGIN: install %s, remove %s, delete %s", install, uninstall, delete);
     550            pluginOperation(install, uninstall, delete);
     551        }
     552       
     553        private void processMsgBoxElement(Element elem) {
     554            String text = evalVars(elem.getAttribute("text"));
     555            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
     556            if (locText!=null && locText.length()>0) text=locText;
     557
     558            String type = evalVars(elem.getAttribute("type"));
     559            messageBox(type, text);
     560        }
     561       
     562
     563        private void processAskElement(Element elem) {
     564            String text = evalVars(elem.getAttribute("text"));
     565            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
     566            if (locText.length()>0) text=locText;
     567            String var = elem.getAttribute("var");
     568            if (var.length()==0) var="result";
     569           
     570            String input = evalVars(elem.getAttribute("input"));
     571            if ("true".equals(input)) {
     572                setVar(var, askForText(text));
     573            } else {
     574                String opts = evalVars(elem.getAttribute("options"));
     575                String locOpts = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".options"));
     576                if (locOpts.length()>0) opts=locOpts;
     577                setVar(var, String.valueOf(askForOption(text, opts)));
     578            }
     579        }
     580
     581        public void setVar(String name, String value) {
     582            try {
     583                engine.eval(name+"='"+value+"';");
     584            } catch (ScriptException ex) {
     585                log("Error: Can not assign variable: %s=%s  : %s\n", name, value, ex.getMessage());
     586            }
     587        }
     588       
     589        private void processIfElement(Element elem) {
     590            String realValue = evalVars(elem.getAttribute("test"));
     591            boolean v=false;
     592            if ("true".equals(realValue)) v=true; else
     593            if ("fales".equals(realValue)) v=true; else
     594            {
     595                log("Error: Illegal test expression in if: %s=%s\n", elem.getAttribute("test"), realValue);
     596            }
     597               
     598            if (v) processXmlFragment(elem);
     599            lastV = v;
     600        }
     601
     602        private void processElseElement(Element elem) {
     603            if (!lastV) {
     604                processXmlFragment(elem);
     605            }
     606        }
     607
     608        private boolean processRunTaskElement(Element elem) {
     609            String taskName = elem.getAttribute("name");
     610            Element task = tasksMap.get(taskName);
     611            if (task!=null) {
     612                log("EXECUTING TASK "+taskName);
     613                processXmlFragment(task); // process task recursively
     614            } else {
     615                log("Error: Can not execute task "+taskName);
     616                return true;
     617            }
     618            return false;
     619        }
     620       
     621               
     622        private void processScriptElement(Element elem) {
     623            String js = elem.getChildNodes().item(0).getTextContent();
     624            log("Processing script...");
     625            try {
     626                PreferencesUtils.modifyPreferencesByScript(engine, mainPrefs, js);
     627            } catch (ScriptException ex) {
     628                messageBox("e", ex.getMessage());
     629                log("JS error: "+ex.getMessage());
     630            }
     631            log("Script finished");
     632        }
     633       
     634        /**
     635         * subsititute ${expression} = expression evaluated by JavaScript
     636         */
     637        private String evalVars(String s) {
     638            Pattern p = Pattern.compile("\\$\\{(.*)\\}");
     639            Matcher mr =  p.matcher(s);
     640            StringBuffer sb = new StringBuffer();
     641            while (mr.find()) {
     642            try {
     643                String result = engine.eval(mr.group(1)).toString();
     644                mr.appendReplacement(sb, result);
     645            } catch (ScriptException ex) {
     646                log("Error: Can not evaluate expression %s : %s",  mr.group(1), ex.getMessage());
     647                //mr.appendReplacement(sb, mr.group(0));
     648            }
     649            }
     650            mr.appendTail(sb);
     651            return sb.toString();
     652        }
     653
     654        private Preferences readPreferencesFromDOMElement(Element item) {
     655            Preferences tmpPref = new Preferences();
     656            try {
     657                Transformer xformer = TransformerFactory.newInstance().newTransformer();
     658                CharArrayWriter outputWriter = new CharArrayWriter(8192);
     659                StreamResult out = new StreamResult(outputWriter);
     660
     661                xformer.transform(new DOMSource(item), out);
     662               
     663                String fragmentWithReplacedVars= evalVars(outputWriter.toString());
     664
     665                CharArrayReader reader = new CharArrayReader(fragmentWithReplacedVars.toCharArray());
     666                tmpPref.fromXML(reader);
     667            } catch (Exception ex) {
     668                log("Error: can not read XML fragment :" + ex.getMessage());
     669            }
     670
     671            return tmpPref;
     672        }
     673
     674
     675    }
     676
     677    public static class PreferencesUtils {
     678   
     679        private static void replacePreferences(Preferences fragment, Preferences mainpref) {
     680            // normal prefs
     681            for (Entry<String, String> entry : fragment.properties.entrySet()) {
     682                mainpref.put(entry.getKey(), entry.getValue());
     683            }
     684            // "list"
     685            for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
     686                mainpref.putCollection(entry.getKey(), entry.getValue());
     687            }
     688            // "lists"
     689            for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
     690                ArrayList<Collection<String>> array = new ArrayList<Collection<String>>();
     691                array.addAll(entry.getValue());
     692                mainpref.putArray(entry.getKey(), array);
     693            }
     694            /// "maps"
     695            for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
     696                mainpref.putListOfStructs(entry.getKey(), entry.getValue());
     697            }
     698
     699        }
     700
     701        private static void appendPreferences(Preferences fragment, Preferences mainpref) {
     702            // normal prefs
     703            for (Entry<String, String> entry : fragment.properties.entrySet()) {
     704                mainpref.put(entry.getKey(), entry.getValue());
     705            }
     706
     707            // "list"
     708            for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
     709                String key = entry.getKey();
     710
     711                Collection<String> newItems = getCollection(mainpref, key, true);
     712                if (newItems == null) continue;
     713
     714                for (String item : entry.getValue()) {
     715                    // add nonexisting elements to then list
     716                    if (!newItems.contains(item)) {
     717                        newItems.add(item);
     718                    }
     719                }
     720                mainpref.putCollection(entry.getKey(), newItems);
     721            }
     722
     723            // "lists"
     724            for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
     725                String key = entry.getKey();
     726
     727                Collection<Collection<String>> newLists = getArray(mainpref, key, true);
     728                if (newLists == null) continue;
     729
     730                for (Collection<String> list : entry.getValue()) {
     731                    // add nonexisting list (equals comparison for lists is used implicitly)
     732                    if (!newLists.contains(list)) {
     733                        newLists.add(list);
     734                    }
     735                }
     736                mainpref.putArray(entry.getKey(), newLists);
     737            }
     738
     739            /// "maps"
     740            for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
     741                String key = entry.getKey();
     742
     743                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
     744                if (newMaps == null) continue;
     745
     746                // get existing properties as list of maps
     747
     748                for (Map<String, String> map : entry.getValue()) {
     749                    // add nonexisting map (equals comparison for maps is used implicitly)
     750                    if (!newMaps.contains(map)) {
     751                        newMaps.add(map);
     752                    }
     753                }
     754                mainpref.putListOfStructs(entry.getKey(), newMaps);
     755            }
     756        }
     757       
     758        /**
     759     * Delete items from @param mainpref collections that match items from @param fragment collections
     760     */
     761    private static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
     762
     763
     764        // normal prefs
     765        for (Entry<String, String> entry : fragment.properties.entrySet()) {
     766            // if mentioned value found, delete it
     767            if (entry.getValue().equals(mainpref.properties.get(entry.getKey()))) {
     768                mainpref.put(entry.getKey(), null);
     769            }
     770        }
     771
     772        // "list"
     773        for (Entry<String, List<String>> entry : fragment.collectionProperties.entrySet()) {
     774            String key = entry.getKey();
     775
     776            Collection<String> newItems = getCollection(mainpref, key, true);
     777            if (newItems == null) continue;
     778
     779            // remove mentioned items from collection
     780            for (String item : entry.getValue()) {
     781                log("Deleting preferences: from list %s: %s\n", key, item);
     782                newItems.remove(item);
     783            }
     784            mainpref.putCollection(entry.getKey(), newItems);
     785        }
     786
     787        // "lists"
     788        for (Entry<String, List<List<String>>> entry : fragment.arrayProperties.entrySet()) {
     789            String key = entry.getKey();
     790
     791           
     792            Collection<Collection<String>> newLists = getArray(mainpref, key, true);
     793            if (newLists == null) continue;
     794           
     795            // if items are found in one of lists, remove that list!
     796            Iterator<Collection<String>> listIterator = newLists.iterator();
     797            while (listIterator.hasNext()) {
     798                Collection<String> list = listIterator.next();
     799                for (Collection<String> removeList : entry.getValue()) {
     800                    if (list.containsAll(removeList)) {
     801                        // remove current list, because it matches search criteria
     802                        log("Deleting preferences: list from lists %s: %s\n", key, list);
     803                        listIterator.remove();
     804                    }
     805                }
     806            }
     807
     808            mainpref.putArray(entry.getKey(), newLists);
     809        }
     810
     811        /// "maps"
     812        for (Entry<String, List<Map<String, String>>> entry : fragment.listOfStructsProperties.entrySet()) {
     813            String key = entry.getKey();
     814
     815            List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
     816            if (newMaps == null) continue;
     817               
     818            Iterator<Map<String, String>> mapIterator = newMaps.iterator();
     819            while (mapIterator.hasNext()) {
     820                Map<String, String> map = mapIterator.next();
     821                for (Map<String, String> removeMap : entry.getValue()) {
     822                    if (map.entrySet().containsAll(removeMap.entrySet())) {
     823                        // the map contain all mentioned key-value pair, so it should be deleted from "maps"
     824                        log("Deleting preferences:  deleting map from maps %s: %s\n", key, map);
     825                        mapIterator.remove();
     826                    }
     827                }
     828            }
     829            mainpref.putListOfStructs(entry.getKey(), newMaps);
     830        }
     831    }
     832       
     833    private static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
     834        Map<String, Setting> allSettings = pref.getAllSettings();
     835        for (String key : allSettings.keySet()) {
     836            if (key.matches(pattern)) {
     837                log("Deleting preferences:  deleting key from preferences: " + key);
     838                pref.putSetting(key, allSettings.get(key).getNullInstance());
     839            }
     840        }
     841    }
     842
     843    private static void deletePreferenceKey(String key, Preferences pref) {
     844        Map<String, Setting> allSettings = pref.getAllSettings();
     845        if (allSettings.containsKey(key)) {
     846            log("Deleting preferences:  deleting key from preferences: " + key);
     847            pref.putSetting(key, allSettings.get(key).getNullInstance());
     848        }
     849    }
     850   
     851    private static Collection<String> getCollection(Preferences mainpref, String key, boolean warnUnknownDefault)  {
     852            Collection<String> existing = mainpref.collectionProperties.get(key);
     853            Collection<String> defaults = mainpref.collectionDefaults.get(key);
     854
     855            if (existing == null && defaults == null) {
     856                if (warnUnknownDefault) defaultUnknownWarning(key);
     857                return null;
     858            }
     859            return  (existing != null)
     860                    ? new ArrayList<String>(existing) : new ArrayList<String>(defaults);
     861    }
     862   
     863    private static Collection<Collection<String>> getArray(Preferences mainpref, String key, boolean warnUnknownDefault)  {
     864            Collection<List<String>> existing = mainpref.arrayProperties.get(key);
     865            Collection<List<String>> defaults = mainpref.arrayDefaults.get(key);
     866
     867            if (existing == null && defaults == null) {
     868                if (warnUnknownDefault) defaultUnknownWarning(key);
     869                return null;
     870            }
     871
     872            return  (existing != null)
     873                    ? new ArrayList<Collection<String>>(existing) : new ArrayList<Collection<String>>(defaults);
     874    }
     875
     876    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault)  {
     877            Collection<Map<String, String>> existing = mainpref.listOfStructsProperties.get(key);
     878            Collection<Map<String, String>> defaults = mainpref.listOfStructsDefaults.get(key);
     879
     880            if (existing == null && defaults == null) {
     881                if (warnUnknownDefault) defaultUnknownWarning(key);
     882                return null;
     883            }
     884
     885            return (existing != null)
     886                    ? new ArrayList<Map<String, String>>(existing) : new ArrayList<Map<String, String>>(defaults);
     887    }
     888   
     889   
     890
     891    private static void defaultUnknownWarning(String key) {
     892        log("Warning: Unknown default value of %s , skipped\n", key);
     893        JOptionPane.showMessageDialog(
     894                Main.parent,
     895                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),
     896                tr("Warning"),
     897                JOptionPane.WARNING_MESSAGE);
     898    }
     899
     900    private static void showPrefs(Preferences tmpPref) {
     901        System.out.println("properties: " + tmpPref.properties);
     902        System.out.println("collections: " + tmpPref.collectionProperties);
     903        System.out.println("arrays: " + tmpPref.arrayProperties);
     904        System.out.println("maps: " + tmpPref.listOfStructsProperties);
     905    }
     906   
     907    private static void modifyPreferencesByScript(ScriptEngine engine, Preferences tmpPref, String js) throws ScriptException {
     908        loadPrefsToJS(engine, tmpPref, "API.pref", true);
     909        engine.eval(js);
     910        readPrefsFromJS(engine, tmpPref, "API.pref");
     911    }
     912
     913    public static void readPrefsFromJS(ScriptEngine engine, Preferences tmpPref, String varInJS) throws ScriptException {
     914            String finish =
     915            "stringMap = new java.util.TreeMap ;"+
     916            "listMap =  new java.util.TreeMap ;"+
     917            "listlistMap = new java.util.TreeMap ;"+
     918            "listmapMap =  new java.util.TreeMap ;"+
     919            "for (key in "+varInJS+") {"+
     920            "  val = "+varInJS+"[key];"+
     921            "  type = typeof val == 'string' ? 'string' : val.type;"+
     922            "  if (type == 'string') {"+
     923            "    stringMap.put(key, val);"+
     924            "  } else if (type == 'list') {"+
     925            "    l = new java.util.ArrayList;"+
     926            "    for (i=0; i<val.length; i++) {"+
     927            "      l.add(java.lang.String.valueOf(val[i]));"+
     928            "    }"+
     929            "    listMap.put(key, l);"+
     930            "  } else if (type == 'listlist') {"+
     931            "    l = new java.util.ArrayList;"+
     932            "    for (i=0; i<val.length; i++) {"+
     933            "      list=val[i];"+
     934            "      jlist=new java.util.ArrayList;"+
     935            "      for (j=0; j<list.length; j++) {"+
     936            "         jlist.add(java.lang.String.valueOf(list[j]));"+
     937            "      }"+
     938            "      l.add(jlist);"+
     939            "    }"+
     940            "    listlistMap.put(key, l);"+
     941            "  } else if (type == 'listmap') {"+
     942            "    l = new java.util.ArrayList;"+
     943            "    for (i=0; i<val.length; i++) {"+
     944            "      map=val[i];"+
     945            "      jmap=new java.util.TreeMap;"+
     946            "      for (var key2 in map) {"+
     947            "         jmap.put(key2,java.lang.String.valueOf(map[key2]));"+
     948            "      }"+
     949            "      l.add(jmap);"+
     950            "    }"+
     951            "    listmapMap.put(key, l);"+
     952            "  }  else {" +
     953            "   org.openstreetmap.josm.data.CustomConfigurator.log('Unknown type:'+val.type+ '- use list, listlist or listmap'); }"+
     954            "  }";
     955            engine.eval(finish);
     956           
     957            Map<String, String> stringMap =  (Map<String, String>) engine.get("stringMap");
     958            Map<String, List<String>> listMap = (SortedMap<String, List<String>> ) engine.get("listMap");
     959            Map<String, List<Collection<String>>> listlistMap = (SortedMap<String, List<Collection<String>>>) engine.get("listlistMap");
     960            Map<String, List<Map<String, String>>> listmapMap = (SortedMap<String, List<Map<String,String>>>) engine.get("listmapMap");
     961           
     962            tmpPref.properties.clear();
     963            tmpPref.collectionProperties.clear();
     964            tmpPref.arrayProperties.clear();
     965            tmpPref.listOfStructsProperties.clear();
     966           
     967            for (Entry<String, String> e : stringMap.entrySet()) {
     968                if (e.getValue().equals( tmpPref.defaults.get(e.getKey())) ) continue;
     969                tmpPref.properties.put(e.getKey(), e.getValue());
     970            }
     971
     972            for (Entry<String, List<String>> e : listMap.entrySet()) {
     973                if (Preferences.equalCollection(e.getValue(), tmpPref.collectionDefaults.get(e.getKey()))) continue;
     974                tmpPref.collectionProperties.put(e.getKey(), e.getValue());
     975            }
     976           
     977            for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
     978                if (Preferences.equalArray(e.getValue(), tmpPref.arrayDefaults.get(e.getKey()))) continue;
     979                tmpPref.arrayProperties.put(e.getKey(), (List<List<String>>)(List)e.getValue());
     980            }
     981           
     982            for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
     983                if (Preferences.equalListOfStructs(e.getValue(), tmpPref.listOfStructsDefaults.get(e.getKey()))) continue;
     984                tmpPref.listOfStructsProperties.put(e.getKey(), e.getValue());
     985            }
     986           
     987    }
     988   
     989    public static void loadPrefsToJS(ScriptEngine engine, Preferences tmpPref, String whereToPutInJS, boolean includeDefaults) throws ScriptException {
     990            Map<String, String> stringMap =  new TreeMap<String, String>();
     991            Map<String, List<String>> listMap = new TreeMap<String, List<String>>();
     992            Map<String, List<List<String>>> listlistMap = new TreeMap<String, List<List<String>>>();
     993            Map<String, List<Map<String, String>>> listmapMap = new TreeMap<String, List<Map<String, String>>>();
     994           
     995            if (includeDefaults) {
     996                stringMap.putAll(tmpPref.defaults);
     997                listMap.putAll(tmpPref.collectionDefaults);
     998                listlistMap.putAll(tmpPref.arrayDefaults);
     999                listmapMap.putAll(tmpPref.listOfStructsDefaults);
     1000            }
     1001           
     1002            while (stringMap.values().remove(null)) { };
     1003            while (listMap.values().remove(null)) { };
     1004            while (listlistMap.values().remove(null)) { };
     1005            while (listmapMap.values().remove(null)) { };
     1006           
     1007            stringMap.putAll(tmpPref.properties);
     1008            listMap.putAll(tmpPref.collectionProperties);
     1009            listlistMap.putAll(tmpPref.arrayProperties);
     1010            listmapMap.putAll(tmpPref.listOfStructsProperties);
     1011           
     1012            engine.put("stringMap", stringMap);
     1013            engine.put("listMap", listMap);
     1014            engine.put("listlistMap", listlistMap);
     1015            engine.put("listmapMap", listmapMap);
     1016           
     1017            String init =
     1018                "function getJSList( javaList ) {"+
     1019                " var jsList; var i; "+
     1020                " if (javaList == null) return null;"+
     1021                "jsList = [];"+
     1022                "  for (i = 0; i < javaList.size(); i++) {"+
     1023                "    jsList.push(String(list.get(i)));"+
     1024                "  }"+
     1025                "return jsList;"+
     1026                "}"+
     1027                "function getJSMap( javaMap ) {"+
     1028                " var jsMap; var it; var e; "+
     1029                " if (javaMap == null) return null;"+
     1030                " jsMap = {};"+
     1031                " for (it = javaMap.entrySet().iterator(); it.hasNext();) {"+
     1032                "    e = it.next();"+
     1033                "    jsMap[ String(e.getKey()) ] = String(e.getValue()); "+
     1034                "  }"+
     1035                "  return jsMap;"+
     1036                "}"+
     1037                "for (it = stringMap.entrySet().iterator(); it.hasNext();) {"+
     1038                "  e = it.next();"+
     1039                whereToPutInJS+"[String(e.getKey())] = String(e.getValue());"+
     1040                "}\n"+
     1041                "for (it = listMap.entrySet().iterator(); it.hasNext();) {"+
     1042                "  e = it.next();"+
     1043                "  list = e.getValue();"+
     1044                "  jslist = getJSList(list);"+
     1045                "  jslist.type = 'list';"+
     1046                whereToPutInJS+"[String(e.getKey())] = jslist;"+
     1047                "}\n"+
     1048                "for (it = listlistMap.entrySet().iterator(); it.hasNext(); ) {"+
     1049                "  e = it.next();"+
     1050                "  listlist = e.getValue();"+
     1051                "  jslistlist = [];"+
     1052                "  for (it2 = listlist.iterator(); it2.hasNext(); ) {"+
     1053                "    list = it2.next(); "+
     1054                "    jslistlist.push(getJSList(list));"+
     1055                "    }"+
     1056                "  jslistlist.type = 'listlist';"+
     1057                whereToPutInJS+"[String(e.getKey())] = jslistlist;"+
     1058                "}\n"+
     1059                "for (it = listmapMap.entrySet().iterator(); it.hasNext();) {"+
     1060                "  e = it.next();"+
     1061                "  listmap = e.getValue();"+
     1062                "  jslistmap = [];"+
     1063                "  for (it2 = listmap.iterator(); it2.hasNext();) {"+
     1064                "    map = it2.next();"+
     1065                "    jslistmap.push(getJSMap(map));"+
     1066                "    }"+
     1067                "  jslistmap.type = 'listmap';"+
     1068                whereToPutInJS+"[String(e.getKey())] = jslistmap;"+
     1069                "}\n";
     1070               
     1071                //System.out.println("map1: "+stringMap );
     1072                //System.out.println("lists1: "+listMap );
     1073                //System.out.println("listlist1: "+listlistMap );
     1074                //System.out.println("listmap1: "+listmapMap );
     1075                engine.eval(init);
     1076           
     1077    }
     1078    }
     1079}