Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java	(revision 2789)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java	(revision 2790)
@@ -3,4 +3,7 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.io.*;
 import java.net.URL;
@@ -8,6 +11,5 @@
 import java.util.Map.Entry;
 
-import javax.swing.JCheckBox;
-import javax.swing.JPanel;
+import javax.swing.*;
 
 import org.openstreetmap.josm.Main;
@@ -19,4 +21,5 @@
 import org.openstreetmap.josm.plugins.validator.*;
 import org.openstreetmap.josm.plugins.validator.util.Bag;
+import org.openstreetmap.josm.plugins.validator.util.Util;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.XmlObjectParser;
@@ -30,5 +33,8 @@
 public class SpellCheck extends Test 
 {
-	/** The spell check key substitutions: the key should be substituted by the value */
+	/** The default spellcheck data file */
+    public static final String SPELLCHECK_DATA_FILE = "http://svn.openstreetmap.org/applications/utils/planet.osm/java/speller/words.cfg";
+
+    /** The spell check key substitutions: the key should be substituted by the value */
 	protected static Map<String, String> spellCheckKeyData;
 
@@ -36,12 +42,36 @@
 	protected static Bag<String, String> spellCheckValueData;
 	
-	/** Preference name for checking values */
-	public static final String PREF_CHECK_VALUES = "tests." + SpellCheck.class.getSimpleName() + ".checkValues";
-	
-	/** Whether to check values too */
-	protected boolean checkValues = false;
-
-	/** Preferences checkbox */
-	protected JCheckBox prefCheckValues;
+    /** Preference name for checking values */
+    public static final String PREF_CHECK_VALUES = "tests." + SpellCheck.class.getSimpleName() + ".checkValues";
+    /** Preference name for checking values */
+    public static final String PREF_CHECK_KEYS = "tests." + SpellCheck.class.getSimpleName() + ".checkKeys";
+    /** Preference name for sources */
+    public static final String PREF_SOURCES = "tests." + SpellCheck.class.getSimpleName() + ".sources";
+    /** Preference name for global upload check */
+    public static final String PREF_CHECK_BEFORE_UPLOAD = "tests." + SpellCheck.class.getSimpleName() + ".checkBeforeUpload";
+    /** Preference name for keys upload check */
+    public static final String PREF_CHECK_KEYS_BEFORE_UPLOAD = "tests." + SpellCheck.class.getSimpleName() + ".checkKeysBeforeUpload";
+    /** Preference name for values upload check */
+    public static final String PREF_CHECK_VALUES_BEFORE_UPLOAD = "tests." + SpellCheck.class.getSimpleName() + ".checkValuesBeforeUpload";
+	
+    /** Whether to check keys */
+    protected boolean checkKeys = false;
+    /** Whether to check values */
+    protected boolean checkValues = false;
+
+    /** Preferences checkbox for keys */
+    protected JCheckBox prefCheckKeys;
+    /** Preferences checkbox for values */
+    protected JCheckBox prefCheckValues;
+    /** The preferences checkbox for validation of keys on upload */
+    protected JCheckBox prefCheckKeysBeforeUpload;
+    /** The preferences checkbox for validation of values on upload */
+    protected JCheckBox prefCheckValuesBeforeUpload;
+    /** The add button */
+    protected JButton addSrcButton;
+    /** The edit button */
+    protected JButton editSrcButton;
+    /** The delete button */
+    protected JButton deleteSrcButton;
 
 	/** Empty values error */
@@ -52,4 +82,12 @@
 	protected static int INVALID_VALUE 	= 2;
 	
+    /** List of sources for spellcheck data */
+    protected JList spellcheckSources;
+
+    /** Whether this test must check the keys before upload. Used by peferences */
+    protected boolean testKeysBeforeUpload;
+    /** Whether this test must check the values before upload. Used by peferences */
+    protected boolean testValuesBeforeUpload;
+    
 	/**
 	 * Constructor
@@ -57,5 +95,5 @@
 	public SpellCheck() 
 	{
-		super(tr("Spell checker."),
+		super(tr("Properties spell checker."),
 			  tr("This plugin checks misspelled property keys and values."));
 	}
@@ -80,29 +118,40 @@
 	private static void initializeSpellCheck(OSMValidatorPlugin plugin) throws FileNotFoundException, IOException 
 	{
-		plugin.copy("/resources/spellCheck.data", "spellCheck.data");
-		BufferedReader reader = new BufferedReader( new FileReader(plugin.getPluginDir() + "/spellCheck.data") );
-		
 		spellCheckKeyData = new HashMap<String, String>();
-		String okValue = null;
-		do
-		{
-			String line = reader.readLine();
-			if( line == null || line.length() == 0 )
-				break;
-
-			if( line.charAt(0) == '+' )
-			{
-				okValue = line.substring(1);
-			}
-			else if( line.charAt(0) == '-' && okValue != null )
-			{
-				spellCheckKeyData.put(line.substring(1), okValue);
-			}
-			else
-			{
-				System.err.println("Invalid spellcheck line:" + line);
-			}
-		}
-		while( true );
+        String sources = Main.pref.get( PREF_SOURCES );
+        if( sources == null || sources.length() == 0)
+            sources = SPELLCHECK_DATA_FILE;
+        
+        StringTokenizer st = new StringTokenizer(sources, ";");
+        while (st.hasMoreTokens())
+        {
+            String source = st.nextToken();
+            File sourceFile = Util.mirror(new URL(source), plugin.getPluginDir());
+            BufferedReader reader = new BufferedReader(new FileReader(sourceFile));
+
+    		String okValue = null;
+    		do
+    		{
+    			String line = reader.readLine();
+                if( line == null || line.length() == 0 )
+                    break;
+                if( line.startsWith("#") )
+                    continue;
+    
+    			if( line.charAt(0) == '+' )
+    			{
+    				okValue = line.substring(1);
+    			}
+    			else if( line.charAt(0) == '-' && okValue != null )
+    			{
+    				spellCheckKeyData.put(line.substring(1), okValue);
+    			}
+    			else
+    			{
+    				System.err.println("Invalid spellcheck line:" + line);
+    			}
+    		}
+    		while( true );
+        }
 	}
 	
@@ -127,6 +176,4 @@
 		spellCheckValueData = new Bag<String, String>();
 		readPresetFromPreferences();
-		
-		// TODO: allow per user word definitions
 	}
 	
@@ -166,10 +213,10 @@
 			String key = prop.getKey();
 			String value = prop.getValue();
-			if( (value==null || value.trim().length() == 0) && !withErrors.contains(p, "EV"))
+			if( checkValues && (value==null || value.trim().length() == 0) && !withErrors.contains(p, "EV"))
 			{
 				errors.add( new TestError(this, Severity.WARNING, tr("Tags with empty values"), p, EMPTY_VALUES) );
 				withErrors.add(p, "EV");
 			}
-			if( spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK"))
+			if( checkKeys && spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK"))
 			{
 				errors.add( new TestError(this, Severity.WARNING, tr("Invalid property keys"), p, INVALID_KEY) );
@@ -263,22 +310,146 @@
 	public void startTest() 
 	{
-		checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
-	}
-
+        checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS);
+        if( isBeforeUpload )
+            checkKeys = checkKeys && Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD);
+
+        checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
+        if( isBeforeUpload )
+            checkValues = checkValues && Main.pref.getBoolean(PREF_CHECK_VALUES_BEFORE_UPLOAD);
+	}
+
+    @Override
+    public void visit(Collection<OsmPrimitive> selection) 
+    {
+        if( checkKeys || checkValues)
+            super.visit(selection);
+    }
+    
 	@Override
 	public void addGui(JPanel testPanel)
 	{
-		boolean checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
-		
-		String text = tr("Check also property values from presets");
-		prefCheckValues = new JCheckBox(text, checkValues);
-		prefCheckValues.setToolTipText(text);
-		testPanel.add(prefCheckValues, GBC.eop().insets(40,0,0,0));
-	}
-
+        testPanel.add( new JLabel(), GBC.eol());
+        
+        boolean checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS);
+        prefCheckKeys = new JCheckBox(tr("Check property keys."), checkKeys);
+        prefCheckKeys .setToolTipText(tr("Validate that property keys are valid checking against list of words."));
+        testPanel.add(prefCheckKeys, GBC.std().insets(40,0,0,0));
+
+        prefCheckKeysBeforeUpload = new JCheckBox();
+        prefCheckKeysBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD));
+        testPanel.add(prefCheckKeysBeforeUpload, GBC.eop().insets(20,0,0,0));
+        
+        spellcheckSources = new JList(new DefaultListModel());
+        if( !Main.pref.hasKey(PREF_SOURCES))
+            Main.pref.put(PREF_SOURCES, SPELLCHECK_DATA_FILE);
+        
+        String sources = Main.pref.get( PREF_SOURCES );
+        StringTokenizer st = new StringTokenizer(sources, ";");
+        while (st.hasMoreTokens())
+            ((DefaultListModel)spellcheckSources.getModel()).addElement(st.nextToken());
+        
+        addSrcButton = new JButton(tr("Add"));
+        addSrcButton.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                String source = JOptionPane.showInputDialog(Main.parent, tr("Spellcheck source"));
+                if (source == null)
+                    return;
+                ((DefaultListModel)spellcheckSources.getModel()).addElement(source);
+            }
+        });
+
+        editSrcButton = new JButton(tr("Edit"));
+        editSrcButton.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                if (spellcheckSources.getSelectedIndex() == -1)
+                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
+                else {
+                    String source = JOptionPane.showInputDialog(Main.parent, tr("Spellcheck source"), spellcheckSources.getSelectedValue());
+                    if (source == null)
+                        return;
+                    ((DefaultListModel)spellcheckSources.getModel()).setElementAt(source, spellcheckSources.getSelectedIndex());
+                }
+            }
+        });
+
+        deleteSrcButton = new JButton(tr("Delete"));
+        deleteSrcButton.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                if (spellcheckSources.getSelectedIndex() == -1)
+                    JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
+                else {
+                    ((DefaultListModel)spellcheckSources.getModel()).remove(spellcheckSources.getSelectedIndex());
+                }
+            }
+        });
+        spellcheckSources.setVisibleRowCount(3);
+
+        spellcheckSources.setToolTipText(tr("The sources (url or filename) of spell check data files. See http://wiki.openstreetmap.org/index.php/User:JLS/speller for help."));
+        addSrcButton.setToolTipText(tr("Add a new spellcheck source to the list."));
+        editSrcButton.setToolTipText(tr("Edit the selected source."));
+        deleteSrcButton.setToolTipText(tr("Delete the selected source from the list."));
+
+        testPanel.add(new JLabel(tr("Spellcheck data sources")), GBC.eol().insets(40,0,0,0));
+        testPanel.add(new JScrollPane(spellcheckSources), GBC.eol().insets(40,0,0,0));
+        final JPanel buttonPanel = new JPanel(new GridBagLayout());
+        testPanel.add(buttonPanel, GBC.eol().fill(GBC.HORIZONTAL));
+        buttonPanel.add(addSrcButton, GBC.std().insets(0,5,0,0));
+        buttonPanel.add(editSrcButton, GBC.std().insets(5,5,5,0));
+        buttonPanel.add(deleteSrcButton, GBC.std().insets(0,5,0,0));
+        
+        prefCheckKeys.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                boolean selected = prefCheckKeys.isSelected();
+                spellcheckSources.setEnabled( selected );
+                addSrcButton.setEnabled(selected);
+                editSrcButton.setEnabled(selected);
+                deleteSrcButton.setEnabled(selected);
+            }
+        });
+        
+        spellcheckSources.setEnabled( checkKeys );
+        buttonPanel.setEnabled( checkKeys );
+        
+        boolean checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
+        prefCheckValues = new JCheckBox(tr("Check property values."), checkValues);
+        prefCheckValues .setToolTipText(tr("Validate that property values are valid checking against presets."));
+		testPanel.add(prefCheckValues, GBC.std().insets(40,0,0,0));
+
+        prefCheckValuesBeforeUpload = new JCheckBox();
+        prefCheckValuesBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_VALUES_BEFORE_UPLOAD));
+        testPanel.add(prefCheckValuesBeforeUpload, GBC.eop().insets(20,0,0,0));
+        
+	}
+
+    public void setGuiEnabled(boolean enabled)
+    {
+        prefCheckKeys.setEnabled(enabled);
+        prefCheckKeysBeforeUpload.setEnabled(enabled);
+        spellcheckSources.setEnabled( enabled );
+        addSrcButton.setEnabled(enabled);
+        editSrcButton.setEnabled(enabled);
+        deleteSrcButton.setEnabled(enabled);
+        prefCheckValues.setEnabled(enabled);
+        prefCheckValuesBeforeUpload.setEnabled(enabled);
+    } 
+    
 	@Override
 	public void ok() 
 	{
-		Main.pref.put(PREF_CHECK_VALUES, prefCheckValues.isSelected());
+        Main.pref.put(PREF_CHECK_VALUES, prefCheckValues.isSelected());
+        Main.pref.put(PREF_CHECK_KEYS, prefCheckKeys.isSelected());
+        Main.pref.put(PREF_CHECK_VALUES_BEFORE_UPLOAD, prefCheckValuesBeforeUpload.isSelected());
+        Main.pref.put(PREF_CHECK_KEYS_BEFORE_UPLOAD, prefCheckKeysBeforeUpload.isSelected());
+        Main.pref.put(PREF_CHECK_BEFORE_UPLOAD, prefCheckKeysBeforeUpload.isSelected() || prefCheckValuesBeforeUpload.isSelected());            
+        String sources = "";
+        if( spellcheckSources.getModel().getSize() > 0 )
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < spellcheckSources.getModel().getSize(); ++i)
+                sb.append(";"+spellcheckSources.getModel().getElementAt(i));
+            sources = sb.substring(1);
+        }
+        Main.pref.put(PREF_SOURCES, sources );
+
 	}
 	
