Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreePanel.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreePanel.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreePanel.java	(revision 9269)
@@ -160,24 +160,38 @@
     }
 
-    /**
-     * Clears the current error list and adds thiese errors to it
-     * @param errors The validation errors
-     */
-    public void setErrors(List<TestError> errors)
-    {
-    	this.errors.clear();
-    	this.errors.addAll(errors);
-        if( isVisible() )
-        	buildTree();
-    }
-
-    /**
-     * Returns the errors of the tree
-     * @return  the errors of the tree
-     */
-    public List<TestError> getErrors()
-    {
-        return errors != null ? errors : Collections.<TestError>emptyList();
-    }
+	/**
+	 * Clears the current error list and adds these errors to it
+	 * @param errors The validation errors
+	 */
+	public void setErrors(List<TestError> newerrors)
+	{
+		errors.clear();
+		for(TestError error : newerrors)
+		{
+			if(!error.getIgnored())
+				errors.add(error);
+		}
+		if( isVisible() )
+			buildTree();
+	}
+
+	/**
+	 * Returns the errors of the tree
+	 * @return  the errors of the tree
+	 */
+	public List<TestError> getErrors()
+	{
+		return errors != null ? errors : Collections.<TestError>emptyList();
+	}
+
+	/**
+	 * Updates the current errors list
+	 * @param errors The validation errors
+	 */
+	public void resetErrors()
+	{
+		List<TestError> e = new ArrayList<TestError>(errors);
+		setErrors(e);
+	}
 
     /**
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java	(revision 9269)
@@ -50,5 +50,4 @@
 		UntaggedWay.class,
 		SelfIntersectingWay.class,
-		SpellCheck.class,
 		DuplicatedWayNodes.class,
 		CrossingWays.class,
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java	(revision 9269)
@@ -35,6 +35,11 @@
 	public static final String PREF_TESTS = PREFIX + ".tests";
 
+	/** The preferences key for enabled tests */
+	public static final String PREF_USE_IGNORE = PREFIX + ".ignore";
+
 	/** The preferences key for enabled tests before upload*/
 	public static final String PREF_TESTS_BEFORE_UPLOAD = PREFIX + ".testsBeforeUpload";
+
+	private JCheckBox prefUseIgnore;
 
 	/** The list of all tests */
@@ -45,18 +50,24 @@
 	}
 
-    public void addGui(PreferenceDialog gui)
-    {
+	public void addGui(PreferenceDialog gui)
+	{
 		JPanel testPanel = new JPanel(new GridBagLayout());
 		testPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
-		
-        testPanel.add( new JLabel(), GBC.std() );
-        testPanel.add( new JLabel("On upload"), GBC.eop() );
+
+		prefUseIgnore = new JCheckBox(tr("Use ignore list."), Main.pref.getBoolean(PREF_USE_IGNORE, true));
+		prefUseIgnore.setToolTipText(tr("Use the use ignore list to suppress warnings."));
+		testPanel.add(prefUseIgnore, GBC.eol());
+
+		GBC a = GBC.eol().insets(-5,0,0,0);
+		a.anchor = GBC.EAST;
+		testPanel.add( new JLabel(tr("On demand")), GBC.std() );
+		testPanel.add( new JLabel(tr("On upload")), a );
 
 		allTests = OSMValidatorPlugin.getTests();
-		for(Test test: allTests) 
+		for(Test test: allTests)
 		{
-            test.addGui(testPanel);
+			test.addGui(testPanel);
 		}
-		
+
 		JScrollPane testPane = new JScrollPane(testPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 		testPane.setBorder(null);
@@ -66,14 +77,14 @@
 		if( ver != null )
 			description += "<br><br>" + tr("Version: {0}<br>Last change at {1}", ver.revision, ver.time);
-    	JPanel tab = gui.createPreferenceTab("validator", tr("Data validator"), description);
+		JPanel tab = gui.createPreferenceTab("validator", tr("Data validator"), description);
 		tab.add(testPane, GBC.eol().fill(GBC.BOTH));
-		tab.add(GBC.glue(0,10), GBC.eol());
-    }
+		tab.add(GBC.glue(0,10), a);
+	}
 
-	public void ok() 
+	public void ok()
 	{
 		StringBuilder tests = new StringBuilder();
 		StringBuilder testsBeforeUpload = new StringBuilder();
-		
+
 		for (Test test : allTests)
 		{
@@ -83,12 +94,13 @@
 			testsBeforeUpload.append( ',' ).append( name ).append( '=' ).append( test.testBeforeUpload );
 		}
-		
+
 		if (tests.length() > 0 ) tests = tests.deleteCharAt(0);
 		if (testsBeforeUpload.length() > 0 ) testsBeforeUpload = testsBeforeUpload.deleteCharAt(0);
-		
+
 		plugin.initializeTests( allTests );
-		
+
 		Main.pref.put( PREF_TESTS, tests.toString());
 		Main.pref.put( PREF_TESTS_BEFORE_UPLOAD, testsBeforeUpload.toString());
+		Main.pref.put( PREF_USE_IGNORE, prefUseIgnore.isSelected());
 	}
 	
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java	(revision 9269)
@@ -1,3 +1,5 @@
 package org.openstreetmap.josm.plugins.validator;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
@@ -8,9 +10,9 @@
 public enum Severity {
 	/** Error messages */
-	ERROR("Errors", "error.gif",       Preferences.getPreferencesColor("validation error", Color.RED)),
+	ERROR(tr("Errors"), "error.gif",       Preferences.getPreferencesColor("validation error", Color.RED)),
 	/** Warning messages */ 
-	WARNING("Warnings", "warning.gif", Preferences.getPreferencesColor("validation warning", Color.YELLOW)), 
+	WARNING(tr("Warnings"), "warning.gif", Preferences.getPreferencesColor("validation warning", Color.YELLOW)), 
 	/** Other messages */ 
-	OTHER("Other", "other.gif",        Preferences.getPreferencesColor("validation other", Color.CYAN)); 
+	OTHER(tr("Other"), "other.gif",        Preferences.getPreferencesColor("validation other", Color.CYAN)); 
 	
 	/** Description of the severity code */
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java	(revision 9269)
@@ -142,9 +142,11 @@
 		checkEnabled = new JCheckBox(name, enabled);
 		checkEnabled.setToolTipText(description);
-		testPanel.add(checkEnabled, GBC.std().insets(20,0,0,0));
-		
-        checkBeforeUpload = new JCheckBox();
-        checkBeforeUpload.setSelected(testBeforeUpload);
-        testPanel.add(checkBeforeUpload, GBC.eop().insets(20,0,0,0));
+		testPanel.add(checkEnabled, GBC.std());
+
+		GBC a = GBC.eol();
+		a.anchor = GBC.EAST;
+		checkBeforeUpload = new JCheckBox();
+		checkBeforeUpload.setSelected(testBeforeUpload);
+		testPanel.add(checkBeforeUpload, a);
 	}
 
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java	(revision 9269)
@@ -2,6 +2,8 @@
 
 import java.awt.*;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.TreeSet;
 
 import org.openstreetmap.josm.command.Command;
@@ -16,4 +18,6 @@
 public class TestError
 {
+	/** is this error on the ignore list */
+	private Boolean ignored = false;
 	/** Severity */
 	private Severity severity;
@@ -28,6 +32,6 @@
 	/** Internal code used by testers to classify errors */
 	private int internalCode = -1;
-    /** If this error is selected */
-    private boolean selected;
+	/** If this error is selected */
+	private boolean selected;
 	
 	public TestError(Test tester, Severity severity, String message,
@@ -136,18 +140,38 @@
 	 * Sets the ignore state for this error
 	 */
-	public void getIgnoreState()
-	{
-		System.out.println("Ignore " + message);
+	public String getIgnoreState()
+	{
+		Collection<String> strings = new TreeSet<String>();
+		String ignorestring = message;
 		for (OsmPrimitive o : primitives)
 		{
-			System.out.println(o.id + " - " + o.getClass());
-		}
-	}
-
-	/**
-	 * Gets the tester that raised this error 
+			String type = "u";
+			if (o instanceof Way) type = "w";
+			else if (o instanceof Relation) type = "r";
+			else if (o instanceof Node) type = "n";
+			strings.add(type + "_" + o.id);
+		}
+		for (String o : strings)
+		{
+			ignorestring += ":" + o;
+		}
+		return ignorestring;
+	}
+
+	public void setIgnored(boolean state)
+	{
+		ignored = state;
+	}
+
+	public Boolean getIgnored()
+	{
+		return ignored;
+	}
+
+	/**
+	 * Gets the tester that raised this error
 	 * @return the tester that raised this error
 	 */
-	public Test getTester() 
+	public Test getTester()
 	{
 		return tester;
@@ -158,10 +182,9 @@
 	 * @return the internal code
 	 */
-	public int getInternalCode() 
+	public int getInternalCode()
 	{
 		return internalCode;
 	}
 
-	
 	/**
 	 * Sets the internal code
@@ -175,5 +198,5 @@
 	/**
 	 * Returns true if the error can be fixed automatically
-	 * 
+	 *
 	 * @return true if the error can be fixed
 	 */
@@ -182,8 +205,8 @@
 		return tester != null && tester.isFixable(this);
 	}
-	
+
 	/**
 	 * Fixes the error with the appropiate command
-	 * 
+	 *
 	 * @return The command to fix the error
 	 */
@@ -192,26 +215,28 @@
 		if( tester == null )
 			return null;
-		
+
 		return tester.fixError(this);
 	}
 
-    /**
-     * Paints the error on affected primitives
-     * 
-     * @param g The graphics
-     * @param mv The MapView
-     */
-    public void paint(Graphics g, MapView mv)
-    {
-        PaintVisitor v = new PaintVisitor(g, mv);
-        for (Object o : highlighted) {
-			if (o instanceof OsmPrimitive) {
-				v.visit((OsmPrimitive) o);
-			} else if (o instanceof WaySegment) {
-				v.visit((WaySegment) o);
+	/**
+	 * Paints the error on affected primitives
+	 *
+	 * @param g The graphics
+	 * @param mv The MapView
+	 */
+	public void paint(Graphics g, MapView mv)
+	{
+		if(!ignored)
+		{
+			PaintVisitor v = new PaintVisitor(g, mv);
+			for (Object o : highlighted) {
+				if (o instanceof OsmPrimitive)
+					v.visit((OsmPrimitive) o);
+				else if (o instanceof WaySegment)
+					v.visit((WaySegment) o);
 			}
-        }
-    }	
-    
+		}
+	}
+
     /**
      * Visitor that highlights the primitives affected by this error
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java	(revision 9269)
@@ -1,3 +1,5 @@
 package org.openstreetmap.josm.plugins.validator;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.event.ActionEvent;
@@ -35,5 +37,5 @@
 	 */
 	public ValidateAction(OSMValidatorPlugin plugin) {
-		super("Validation", "validator", "Performs the data validation", KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK + KeyEvent.ALT_MASK, true);
+		super(tr("Validation"), "validator", tr("Performs the data validation"), KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK + KeyEvent.ALT_MASK, true);
 		this.plugin = plugin;
 	}
@@ -88,17 +90,27 @@
 
 		List<TestError> errors = new ArrayList<TestError>();
-		for(Test test : tests) 
-        {
+		for(Test test : tests)
+		{
 			test.setPartialSelection(lastSelection != null);
-		    test.startTest();
-		    test.visit(selection);
+			test.startTest();
+			test.visit(selection);
 			test.endTest();
 			errors.addAll( test.getErrors() );
 		}
 		tests = null;
-		
+		if(Main.pref.getBoolean(PreferenceEditor.PREF_USE_IGNORE, true))
+		{
+			for(TestError error : errors)
+			{
+				if(plugin.validationDialog.ignoredErrors.contains(error.getIgnoreState()))
+				{
+					error.setIgnored(true);
+				}
+			}
+		}
+
 		plugin.validationDialog.tree.setErrors(errors);
-        plugin.validationDialog.setVisible(true);
-        DataSet.fireSelectionChanged(Main.ds.getSelected());
+		plugin.validationDialog.setVisible(true);
+		DataSet.fireSelectionChanged(Main.ds.getSelected());
 	}
 }
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java	(revision 9269)
@@ -6,4 +6,10 @@
 import java.awt.GridLayout;
 import java.awt.event.*;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.*;
 import java.util.Map.Entry;
@@ -40,4 +46,6 @@
 	protected ErrorTreePanel tree;
 
+	public Collection<String> ignoredErrors = new TreeSet<String>();
+
 	/** The fix button */
 	private JButton fixButton;
@@ -76,15 +84,51 @@
 		fixButton.setEnabled(false);
 		buttonPanel.add(fixButton);
-		ignoreButton = Util.createButton(tr("Ignore"), "ignore", "dialogs/delete", tr("Ignore the selected errors next time."), this);
-		ignoreButton.setEnabled(false);
-		buttonPanel.add(ignoreButton);
+		if(Main.pref.getBoolean(PreferenceEditor.PREF_USE_IGNORE, true))
+		{
+			ignoreButton = Util.createButton(tr("Ignore"), "ignore", "dialogs/delete", tr("Ignore the selected errors next time."), this);
+			ignoreButton.setEnabled(false);
+			buttonPanel.add(ignoreButton);
+		}
+		else
+		{
+			ignoreButton = null;
+		}
 		add(buttonPanel, BorderLayout.SOUTH);
-	}
-
-    @Override 
-    public void setVisible(boolean v) 
-    {
-        if( tree != null )
-            tree.setVisible(v);
+		loadIgnoredErrors();
+	}
+
+	private void loadIgnoredErrors() {
+		ignoredErrors.clear();
+		if(Main.pref.getBoolean(PreferenceEditor.PREF_USE_IGNORE, true))
+		{
+			try {
+				final BufferedReader in = new BufferedReader(new FileReader(Util.getPluginDir() + "ignorederrors"));
+				for (String line = in.readLine(); line != null; line = in.readLine()) {
+					ignoredErrors.add(line);
+				}
+			}
+			catch (final FileNotFoundException e) {}
+			catch (final IOException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	private void saveIgnoredErrors() {
+		try {
+			final PrintWriter out = new PrintWriter(new FileWriter(Util.getPluginDir() + "ignorederrors"), false);
+			for (String e : ignoredErrors)
+				out.println(e);
+			out.close();
+		} catch (final IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	@Override
+	public void setVisible(boolean v)
+	{
+		if( tree != null )
+			tree.setVisible(v);
 		if( action != null && action.button != null )
 			action.button.setSelected(v);
@@ -92,57 +136,12 @@
 		Main.map.repaint();
 	}
-    
-    
+
+
 	/**
 	 * Fix selected errors
-	 * @param e 
+	 * @param e
 	 */
 	@SuppressWarnings("unchecked")
-	private void fixErrors(ActionEvent e) 
-	{
-        TreePath[] selectionPaths = tree.getSelectionPaths();
-        if( selectionPaths == null )
-            return;
-        
-        Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
-        for( TreePath path : selectionPaths )
-        {
-            DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
-        	if( node == null )
-        		continue;
-            
-    		Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
-    		while( children.hasMoreElements() )
-    		{
-        		DefaultMutableTreeNode childNode = children.nextElement();
-                if( processedNodes.contains(childNode) )
-                    continue;
-                
-                processedNodes.add(childNode);
-        		Object nodeInfo = childNode.getUserObject();
-        		if( nodeInfo instanceof TestError)
-        		{
-        			TestError error = (TestError)nodeInfo;
-        			Command fixCommand = error.getFix();
-        			if( fixCommand != null )
-        			{
-        				Main.main.undoRedo.add(fixCommand);
-        			}
-        		}
-    		}
-        }
-		
-		Main.map.repaint();
-		DataSet.fireSelectionChanged(Main.ds.getSelected());
-		       
-    	plugin.validateAction.doValidate(e, false);
-	}	
-
-	/**
-	 * Set selected errors to ignore state
-	 * @param e
-	 */
-	@SuppressWarnings("unchecked")
-	private void ignoreErrors(ActionEvent e)
+	private void fixErrors(ActionEvent e)
 	{
 		TreePath[] selectionPaths = tree.getSelectionPaths();
@@ -169,6 +168,10 @@
 				{
 					TestError error = (TestError)nodeInfo;
-					String error.getIgnoreState();
-/* ignore */
+					Command fixCommand = error.getFix();
+					if( fixCommand != null )
+					{
+						Main.main.undoRedo.add(fixCommand);
+						error.setIgnored(true);
+					}
 				}
 			}
@@ -176,7 +179,52 @@
 
 		Main.map.repaint();
+		tree.resetErrors();
 		DataSet.fireSelectionChanged(Main.ds.getSelected());
-
-		plugin.validateAction.doValidate(e, false);
+	}
+
+	/**
+	 * Set selected errors to ignore state
+	 * @param e
+	 */
+	@SuppressWarnings("unchecked")
+	private void ignoreErrors(ActionEvent e)
+	{
+		boolean changed = false;
+		TreePath[] selectionPaths = tree.getSelectionPaths();
+		if( selectionPaths == null )
+			return;
+
+		Set<DefaultMutableTreeNode> processedNodes = new HashSet<DefaultMutableTreeNode>();
+		for( TreePath path : selectionPaths )
+		{
+			DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
+			if( node == null )
+				continue;
+
+			Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
+			while( children.hasMoreElements() )
+			{
+				DefaultMutableTreeNode childNode = children.nextElement();
+				if( processedNodes.contains(childNode) )
+					continue;
+
+				processedNodes.add(childNode);
+				Object nodeInfo = childNode.getUserObject();
+				if( nodeInfo instanceof TestError)
+				{
+					TestError error = (TestError)nodeInfo;
+					String state = error.getIgnoreState();
+					ignoredErrors.add(state);
+					changed = true;
+					error.setIgnored(true);
+				}
+			}
+		}
+		if(changed)
+		{
+			tree.resetErrors();
+			saveIgnoredErrors();
+			Main.map.repaint();
+		}
 	}
 
@@ -277,5 +325,6 @@
 		}
 		selectButton.setEnabled(true);
-		ignoreButton.setEnabled(true);
+		if(ignoreButton != null)
+			ignoreButton.setEnabled(true);
 		
 		return hasFixes;
@@ -291,5 +340,6 @@
 		{
 			fixButton.setEnabled(false);
-			ignoreButton.setEnabled(false);
+			if(ignoreButton != null)
+				ignoreButton.setEnabled(false);
 			selectButton.setEnabled(false);
 
@@ -316,5 +366,6 @@
 		{
 			fixButton.setEnabled(false);
-			ignoreButton.setEnabled(false);
+			if(ignoreButton != null)
+				ignoreButton.setEnabled(false);
 			selectButton.setEnabled(false);
 
Index: plications/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 9268)
+++ 	(revision )
@@ -1,548 +1,0 @@
-package org.openstreetmap.josm.plugins.validator.tests;
-
-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;
-import java.util.*;
-import java.util.Map.Entry;
-
-import javax.swing.*;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.*;
-import org.openstreetmap.josm.data.osm.*;
-import org.openstreetmap.josm.gui.tagging.TaggingPreset;
-import org.openstreetmap.josm.gui.tagging.TaggingPreset.*;
-import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
-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;
-import org.xml.sax.SAXException;
-
-/**
- * Check for mispelled properties
- * 
- * @author frsantos
- */
-public class SpellCheck extends Test 
-{
-	/** 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;
-
-	/** The spell check preset values */
-	protected static Bag<String, String> spellCheckValueData;
-
-	/** The preferences prefix */
-	protected static final String PREFIX = PreferenceEditor.PREFIX + "." + SpellCheck.class.getSimpleName();
-    
-    /** Preference name for checking values */
-    public static final String PREF_CHECK_VALUES 				= PREFIX + ".checkValues";
-    /** Preference name for checking values */
-    public static final String PREF_CHECK_KEYS 					= PREFIX + ".checkKeys";
-    /** Preference name for checking FIXMES */
-    public static final String PREF_CHECK_FIXMES 				= PREFIX + ".checkFixmes";
-    /** Preference name for sources */
-    public static final String PREF_SOURCES 					= PREFIX + ".sources";
-    /** Preference name for keys upload check */
-    public static final String PREF_CHECK_KEYS_BEFORE_UPLOAD 	= PREFIX + ".checkKeysBeforeUpload";
-    /** Preference name for values upload check */
-    public static final String PREF_CHECK_VALUES_BEFORE_UPLOAD 	= PREFIX + ".checkValuesBeforeUpload";
-    /** Preference name for fixmes upload check */
-    public static final String PREF_CHECK_FIXMES_BEFORE_UPLOAD 	= PREFIX + ".checkFixmesBeforeUpload";
-	
-    /** Whether to check keys */
-    protected boolean checkKeys = false;
-    /** Whether to check values */
-    protected boolean checkValues = false;
-    /** Whether to check for fixmes in values */
-    protected boolean checkFixmes = false;
-
-    /** Preferences checkbox for keys */
-    protected JCheckBox prefCheckKeys;
-    /** Preferences checkbox for values */
-    protected JCheckBox prefCheckValues;
-    /** Preferences checkbox for FIXMES */
-    protected JCheckBox prefCheckFixmes;
-    /** 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 preferences checkbox for validation of fixmes on upload */
-    protected JCheckBox prefCheckFixmesBeforeUpload;
-    /** The add button */
-    protected JButton addSrcButton;
-    /** The edit button */
-    protected JButton editSrcButton;
-    /** The delete button */
-    protected JButton deleteSrcButton;
-
-	/** Empty values error */
-	protected static int EMPTY_VALUES 	= 0;
-	/** Invalid key error */
-	protected static int INVALID_KEY  	= 1;
-    /** Invalid value error */
-    protected static int INVALID_VALUE  = 2;
-    /** fixme error */
-    protected static int FIXME          = 3;
-	
-    /** 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;
-    /** Whether this test must check form fixmes in values before upload. Used by peferences */
-    protected boolean testFixmesBeforeUpload;
-    
-	/**
-	 * Constructor
-	 */
-	public SpellCheck() 
-	{
-		super(tr("Properties checker."),
-			  tr("This plugin checks for errors in property keys and values."));
-	}
-
-	public static void initialize(OSMValidatorPlugin plugin) throws Exception
-	{
-		initializeSpellCheck();
-		initializePresets();
-	}
-
-	/**
-	 * Reads the spellcheck file into a HashMap.
-	 * <p>
-	 * The data file is a list of words, beginning with +/-. If it starts with +,
-	 * the word is valid, but if it starts with -, the word should be replaced
-	 * by the nearest + word before this.
-	 * 
-	 * @throws FileNotFoundException 
-	 * @throws IOException 
-	 */
-	private static void initializeSpellCheck() throws FileNotFoundException, IOException 
-	{
-		spellCheckKeyData = new HashMap<String, String>();
-        String sources = Main.pref.get( PREF_SOURCES );
-        if( sources == null || sources.length() == 0)
-            sources = SPELLCHECK_DATA_FILE;
-        
-        StringTokenizer st = new StringTokenizer(sources, ";");
-        StringBuilder errorSources = new StringBuilder();
-        while (st.hasMoreTokens())
-        {
-            String source = st.nextToken();
-            File sourceFile = Util.mirror(new URL(source), Util.getPluginDir(), -1);
-            if( sourceFile == null || !sourceFile.exists() )
-            {
-                errorSources.append(source).append("\n");
-                continue;
-            }
-            
-            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 );
-        }
-
-        if( errorSources.length() > 0 )
-            throw new IOException( tr("Could not download spellcheck data file:\n {0}", errorSources) );
-
-	}
-	
-	/**
-	 * Reads the presets data.
-	 * 
-	 * @throws Exception
-	 */
-	public static void initializePresets() throws Exception
-	{
-		if( !Main.pref.getBoolean(PREF_CHECK_VALUES) )
-			return;
-		
-		Collection<TaggingPreset> presets = TaggingPresetPreference.taggingPresets;
-		if( presets == null || presets.isEmpty() )
-		{
-			// Skip re-reading presets if there are none available
-			return;
-		}
-		
-		spellCheckValueData = new Bag<String, String>();
-		readPresetFromPreferences();
-	}
-	
-	
-	@Override
-	public void visit(Node n)
-	{
-		checkPrimitive(n);
-	}
-
-
-	@Override
-	public void visit(Way w) 
-	{
-		checkPrimitive(w);
-	}
-
-	/**
-	 * Checks the spelling of the primitive properties
-	 * @param p The primitive to check
-	 */
-	private void checkPrimitive(OsmPrimitive p)
-	{
-	    // Just a collection to know if a primitive has been already marked with error
-		Bag<OsmPrimitive, String> withErrors = new Bag<OsmPrimitive, String>();
-
-		Map<String, String> props = (p.keys == null) ? Collections.<String, String>emptyMap() : p.keys;
-		for(Entry<String, String> prop: props.entrySet() )
-		{
-			String key = prop.getKey();
-			String value = prop.getValue();
-			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( checkKeys && spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK"))
-			{
-				errors.add( new TestError(this, Severity.WARNING, tr("Invalid property key ''{0}''", key), p, INVALID_KEY) );
-				withErrors.add(p, "IPK");
-			}
-			if( checkKeys && key.indexOf(" ") >= 0 && !withErrors.contains(p, "IPK"))
-			{
-				errors.add( new TestError(this, Severity.WARNING, tr("Invalid white space in property key ''{0}''", key), p, INVALID_KEY) );
-				withErrors.add(p, "IPK");
-			}
-			if( checkValues && value != null && (value.startsWith(" ") || value.endsWith(" ")) && !withErrors.contains(p, "SPACE"))
-			{
-				errors.add( new TestError(this, Severity.OTHER, tr("Property values start or end with white space"), p, INVALID_VALUE) );
-				withErrors.add(p, "SPACE");
-			}
-			if( checkValues && value != null && value.length() > 0 && spellCheckValueData != null)
-			{
-				List<String> values = spellCheckValueData.get(key);
-				if( values != null && !values.contains(prop.getValue()) && !withErrors.contains(p, "UPV"))
-				{
-					errors.add( new TestError(this, Severity.OTHER, tr("Unknown property values"), p, INVALID_VALUE) );
-					withErrors.add(p, "UPV");
-				}
-			}
-			if( checkFixmes && value != null && value.length() > 0 )
-			{
-				if( (value.contains("FIXME") || value.contains("check and delete") || key.contains("todo") || key.contains("fixme"))
-				&& !withErrors.contains(p, "FIXME"))
-				{
-					errors.add( new TestError(this, Severity.OTHER, tr("FIXMES"), p, FIXME) );
-					withErrors.add(p, "FIXME");
-				}
-			}
-		}
-	}
-
-	/**
-	 * Parse an anotation preset from a stream
-	 * 
-	 * @param inStream The stream of the anotstion preset
-	 * @throws SAXException
-	 */
-	public static void readPresets(InputStream inStream) throws SAXException 
-	{
-		BufferedReader in = null;
-		try 
-		{
-			in = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
-		} 
-		catch (UnsupportedEncodingException e) 
-		{
-			e.printStackTrace();
-			in = new BufferedReader(new InputStreamReader(inStream));
-		}
-		
-		XmlObjectParser parser = new XmlObjectParser();
-		parser.mapOnStart("item", TaggingPreset.class);
-		parser.map("text", Text.class);
-		parser.map("check", Check.class);
-		parser.map("combo", Combo.class);
-		parser.map("label", Label.class);
-		parser.map("key", Key.class);
-		parser.start(in);
-		
-		while(parser.hasNext()) 
-		{
-			Object obj = parser.next();
-			if (obj instanceof Combo) {
-				Combo combo = (Combo)obj;
-				for(String value :  combo.values.split(",") )
-					spellCheckValueData.add(combo.key, value);
-			}
-		}
-	}
-
-	/**
-	 * Reads the tagging presets
-	 */
-	public static void readPresetFromPreferences() 
-	{
-		String allAnnotations = Main.pref.get("taggingpreset.sources");
-		StringTokenizer st = new StringTokenizer(allAnnotations, ";");
-		while (st.hasMoreTokens()) 
-		{
-			InputStream in = null;
-			String source = st.nextToken();
-			try 
-			{
-				if (source.startsWith("http") || source.startsWith("ftp") || source.startsWith("file"))
-					in = new URL(source).openStream();
-				else if (source.startsWith("resource://"))
-					in = Main.class.getResourceAsStream(source.substring("resource:/".length()));
-				else
-					in = new FileInputStream(source);
-				readPresets(in);
-				in.close();
-			} 
-			catch (IOException e) 
-			{
-				// Error already reported by JOSM
-			} 
-			catch (SAXException e) 
-			{
-                // Error already reported by JOSM
-			}
-		}
-	}
-
-	@Override
-	public void startTest() 
-	{
-        checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS);
-        if( isBeforeUpload )
-            checkKeys = checkKeys && Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD, true);
-
-        checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
-        if( isBeforeUpload )
-            checkValues = checkValues && Main.pref.getBoolean(PREF_CHECK_VALUES_BEFORE_UPLOAD, true);
-
-        checkFixmes = Main.pref.getBoolean(PREF_CHECK_FIXMES);
-        if( isBeforeUpload )
-            checkFixmes = checkFixmes && Main.pref.getBoolean(PREF_CHECK_FIXMES_BEFORE_UPLOAD, true);
-	}
-
-    @Override
-    public void visit(Collection<OsmPrimitive> selection) 
-    {
-        if( checkKeys || checkValues)
-            super.visit(selection);
-    }
-    
-	@Override
-	public void addGui(JPanel testPanel)
-	{
-		testPanel.add( new JLabel(name), GBC.eol().insets(35,0,0,0) );
-        
-        boolean checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS, true);
-        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, true));
-        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));
-        
-        ActionListener disableCheckKeysActionListener = new ActionListener(){
-		            public void actionPerformed(ActionEvent e) {
-		                boolean selected = prefCheckKeys.isSelected() || prefCheckKeysBeforeUpload.isSelected();
-		                spellcheckSources.setEnabled( selected );
-		                addSrcButton.setEnabled(selected);
-		                editSrcButton.setEnabled(selected);
-		                deleteSrcButton.setEnabled(selected);
-		            }
-		        };
-		prefCheckKeys.addActionListener(disableCheckKeysActionListener);
-		prefCheckKeysBeforeUpload.addActionListener(disableCheckKeysActionListener);
-        
-        spellcheckSources.setEnabled( checkKeys );
-        buttonPanel.setEnabled( checkKeys );
-        
-        boolean checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES, true);
-        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, true));
-        testPanel.add(prefCheckValuesBeforeUpload, GBC.eop().insets(20,0,0,0));
-
-        boolean checkFixmes = Main.pref.getBoolean(PREF_CHECK_FIXMES, true);
-        prefCheckFixmes = new JCheckBox(tr("Check for FIXMES."), checkFixmes);
-        prefCheckFixmes.setToolTipText(tr("Looks for nodes or ways with FIXME in any property value."));
-        testPanel.add(prefCheckFixmes, GBC.std().insets(40,0,0,0));
-
-        prefCheckFixmesBeforeUpload = new JCheckBox();
-        prefCheckFixmesBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_FIXMES_BEFORE_UPLOAD, true));
-        testPanel.add(prefCheckFixmesBeforeUpload, GBC.eop().insets(20,0,0,0));
-	}
-
-	@Override
-	public void ok() 
-	{
-		enabled = prefCheckKeys.isSelected() || prefCheckValues.isSelected() || prefCheckFixmes.isSelected();
-        testBeforeUpload = prefCheckKeysBeforeUpload.isSelected() || prefCheckValuesBeforeUpload.isSelected() || prefCheckFixmesBeforeUpload.isSelected();
-		
-        Main.pref.put(PREF_CHECK_VALUES, prefCheckValues.isSelected());
-        Main.pref.put(PREF_CHECK_KEYS, prefCheckKeys.isSelected());
-        Main.pref.put(PREF_CHECK_FIXMES, prefCheckFixmes.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_FIXMES_BEFORE_UPLOAD, prefCheckFixmesBeforeUpload.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 );
-
-	}
-	
-	@Override
-	public Command fixError(TestError testError)
-	{
-		List<Command> commands = new ArrayList<Command>(50);
-		
-		int i = -1;
-		List<? extends OsmPrimitive> primitives = testError.getPrimitives();
-		for(OsmPrimitive p : primitives )
-		{
-			i++;
-			Map<String, String> tags = p.keys;
-			if( tags == null || tags.size() == 0 )
-				continue;
-		
-			for(Entry<String, String> prop: tags.entrySet() )
-			{
-				String key = prop.getKey();
-				String value = prop.getValue();
-				if( value == null || value.trim().length() == 0 )
-					commands.add( new ChangePropertyCommand(Collections.singleton(primitives.get(i)), key, null) );
-				else
-				{
-					String replacementKey = spellCheckKeyData.get(key);
-					if( replacementKey != null )
-						commands.add( new ChangePropertyKeyCommand(Collections.singleton(primitives.get(i)), key, replacementKey) );					
-				}
-			}
-		}
-		
-        if( commands.size() == 0 )
-            return null;
-        else if( commands.size() == 1 )
-            return commands.get(0);
-        else
-            return new SequenceCommand("Fix properties", commands);
-	}
-	
-	@Override
-	public boolean isFixable(TestError testError)
-	{
-		if( testError.getTester() instanceof SpellCheck)
-		{
-			int code = testError.getInternalCode();
-			return code == INVALID_KEY || code == EMPTY_VALUES;
-		}
-		
-		return false;
-	}	
-}
-	
-	
Index: /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java
===================================================================
--- /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java	(revision 9268)
+++ /applications/editors/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TagChecker.java	(revision 9269)
@@ -3,22 +3,109 @@
 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;
 import java.util.*;
-
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.plugins.validator.Severity;
-import org.openstreetmap.josm.plugins.validator.Test;
-import org.openstreetmap.josm.plugins.validator.TestError;
+import java.util.Map.Entry;
+
+import javax.swing.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.*;
+import org.openstreetmap.josm.data.osm.*;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset;
+import org.openstreetmap.josm.gui.tagging.TaggingPreset.*;
+import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
+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;
+import org.xml.sax.SAXException;
 
 /**
- * Check area type ways for errors
+ * Check for mispelled or wrong properties
  *
- * @author stoecker
+ * @author frsantos
  */
-public class TagChecker extends Test  {
-	/** The already detected errors */
-	Bag<Way, Way> _errorWays;
+public class TagChecker extends Test
+{
+	/** The default data files */
+	public static final String DATA_FILE = "http://svn.openstreetmap.org/applications/editors/josm/plugins/validator/tagchecker.cfg";
+	public static final String SPELL_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;
+	/** The spell check preset values */
+	protected static Bag<String, String> presetsValueData;
+
+	/** The preferences prefix */
+	protected static final String PREFIX = PreferenceEditor.PREFIX + "." + TagChecker.class.getSimpleName();
+
+	/** Preference name for checking values */
+	public static final String PREF_CHECK_VALUES = PREFIX + ".checkValues";
+	/** Preference name for checking values */
+	public static final String PREF_CHECK_KEYS = PREFIX + ".checkKeys";
+	/** Preference name for checking FIXMES */
+	public static final String PREF_CHECK_FIXMES = PREFIX + ".checkFixmes";
+	/** Preference name for sources */
+	public static final String PREF_SOURCES = PREFIX + ".sources";
+	/** Preference name for sources */
+	public static final String PREF_USE_DATA_FILE = PREFIX + ".usedatafile";
+	/** Preference name for sources */
+	public static final String PREF_USE_SPELL_FILE = PREFIX + ".usespellfile";
+	/** Preference name for keys upload check */
+	public static final String PREF_CHECK_KEYS_BEFORE_UPLOAD = PREFIX + ".checkKeysBeforeUpload";
+	/** Preference name for values upload check */
+	public static final String PREF_CHECK_VALUES_BEFORE_UPLOAD = PREFIX + ".checkValuesBeforeUpload";
+	/** Preference name for fixmes upload check */
+	public static final String PREF_CHECK_FIXMES_BEFORE_UPLOAD = PREFIX + ".checkFixmesBeforeUpload";
+
+	/** Whether to check keys */
+	protected boolean checkKeys = false;
+	/** Whether to check values */
+	protected boolean checkValues = false;
+	/** Whether to check for fixmes in values */
+	protected boolean checkFixmes = false;
+
+	/** Preferences checkbox for keys */
+	protected JCheckBox prefCheckKeys;
+	/** Preferences checkbox for values */
+	protected JCheckBox prefCheckValues;
+	/** Preferences checkbox for FIXMES */
+	protected JCheckBox prefCheckFixmes;
+	/** 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 preferences checkbox for validation of fixmes on upload */
+	protected JCheckBox prefCheckFixmesBeforeUpload;
+	/** The add button */
+	protected JButton addSrcButton;
+	/** The edit button */
+	protected JButton editSrcButton;
+	/** The delete button */
+	protected JButton deleteSrcButton;
+
+	/** Empty values error */
+	protected static int EMPTY_VALUES = 0;
+	/** Invalid key error */
+	protected static int INVALID_KEY = 1;
+	/** Invalid value error */
+	protected static int INVALID_VALUE = 2;
+	/** fixme error */
+	protected static int FIXME = 3;
+	
+	/** List of sources for spellcheck data */
+	protected JList Sources;
+
+	/** 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;
+	/** Whether this test must check form fixmes in values before upload. Used by peferences */
+	protected boolean testFixmesBeforeUpload;
 
 	/**
@@ -27,6 +114,246 @@
 	public TagChecker()
 	{
-		super(tr("Tag Checker."),
-			  tr("This tests if major tags are used as expected."));
+		super(tr("Properties checker :"),
+			  tr("This plugin checks for errors in property keys and values."));
+	}
+
+	public static void initialize(OSMValidatorPlugin plugin) throws Exception
+	{
+		initializeData();
+		initializePresets();
+	}
+
+	/**
+	 * Reads the spellcheck file into a HashMap.
+	 * <p>
+	 * The data file is a list of words, beginning with +/-. If it starts with +,
+	 * the word is valid, but if it starts with -, the word should be replaced
+	 * by the nearest + word before this.
+	 *
+	 * @throws FileNotFoundException
+	 * @throws IOException
+	 */
+	private static void initializeData() throws FileNotFoundException, IOException
+	{
+		spellCheckKeyData = new HashMap<String, String>();
+		String sources = Main.pref.get( PREF_SOURCES );
+//		if(Main.pref.getBoolean(PREF_USE_DATA_FILE))
+//		{
+//			if( sources == null || sources.length() == 0)
+//				sources = DATA_FILE;
+//			else
+//				sources = DATA_FILE + ";" + sources;
+//		}
+		if(Main.pref.getBoolean(PREF_USE_SPELL_FILE))
+		{
+			if( sources == null || sources.length() == 0)
+				sources = SPELL_FILE;
+			else
+				sources = SPELL_FILE + ";" + sources;
+		}
+		
+		StringTokenizer st = new StringTokenizer(sources, ";");
+		StringBuilder errorSources = new StringBuilder();
+		while (st.hasMoreTokens())
+		{
+			String source = st.nextToken();
+			File sourceFile = Util.mirror(new URL(source), Util.getPluginDir(), -1);
+			if( sourceFile == null || !sourceFile.exists() )
+			{
+				errorSources.append(source).append("\n");
+				continue;
+			}
+
+			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 );
+		}
+
+		if( errorSources.length() > 0 )
+			throw new IOException( tr("Could not download data file(s):\n{0}", errorSources) );
+	}
+	
+	/**
+	 * Reads the presets data.
+	 *
+	 * @throws Exception
+	 */
+	public static void initializePresets() throws Exception
+	{
+		if( !Main.pref.getBoolean(PREF_CHECK_VALUES) )
+			return;
+
+		Collection<TaggingPreset> presets = TaggingPresetPreference.taggingPresets;
+		if( presets == null || presets.isEmpty() )
+		{
+			// Skip re-reading presets if there are none available
+			return;
+		}
+
+		presetsValueData = new Bag<String, String>();
+		readPresetFromPreferences();
+	}
+	
+	
+	@Override
+	public void visit(Node n)
+	{
+		checkPrimitive(n);
+	}
+
+
+	@Override
+	public void visit(Way w)
+	{
+		checkPrimitive(w);
+	}
+
+	/**
+	 * Checks the primitive properties
+	 * @param p The primitive to check
+	 */
+	private void checkPrimitive(OsmPrimitive p)
+	{
+		// Just a collection to know if a primitive has been already marked with error
+		Bag<OsmPrimitive, String> withErrors = new Bag<OsmPrimitive, String>();
+
+		Map<String, String> props = (p.keys == null) ? Collections.<String, String>emptyMap() : p.keys;
+		for(Entry<String, String> prop: props.entrySet() )
+		{
+			String key = prop.getKey();
+			String value = prop.getValue();
+			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( checkKeys && spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK"))
+			{
+				errors.add( new TestError(this, Severity.WARNING, tr("Invalid property key ''{0}''", key), p, INVALID_KEY) );
+				withErrors.add(p, "IPK");
+			}
+			if( checkKeys && key.indexOf(" ") >= 0 && !withErrors.contains(p, "IPK"))
+			{
+				errors.add( new TestError(this, Severity.WARNING, tr("Invalid white space in property key ''{0}''", key), p, INVALID_KEY) );
+				withErrors.add(p, "IPK");
+			}
+			if( checkValues && value != null && (value.startsWith(" ") || value.endsWith(" ")) && !withErrors.contains(p, "SPACE"))
+			{
+				errors.add( new TestError(this, Severity.OTHER, tr("Property values start or end with white space"), p, INVALID_VALUE) );
+				withErrors.add(p, "SPACE");
+			}
+			if( checkValues && value != null && value.length() > 0 && presetsValueData != null)
+			{
+				List<String> values = presetsValueData.get(key);
+				if( values != null && !values.contains(prop.getValue()) && !withErrors.contains(p, "UPV"))
+				{
+					errors.add( new TestError(this, Severity.OTHER, tr("Unknown property values"), p, INVALID_VALUE) );
+					withErrors.add(p, "UPV");
+				}
+			}
+			if( checkFixmes && value != null && value.length() > 0 )
+			{
+				if( (value.contains("FIXME") || value.contains("check and delete") || key.contains("todo") || key.contains("fixme"))
+				&& !withErrors.contains(p, "FIXME"))
+				{
+					errors.add( new TestError(this, Severity.OTHER, tr("FIXMES"), p, FIXME) );
+					withErrors.add(p, "FIXME");
+				}
+			}
+		}
+	}
+
+	/**
+	 * Parse an anotation preset from a stream
+	 *
+	 * @param inStream The stream of the anotstion preset
+	 * @throws SAXException
+	 */
+	public static void readPresets(InputStream inStream) throws SAXException
+	{
+		BufferedReader in = null;
+		try
+		{
+			in = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
+		}
+		catch (UnsupportedEncodingException e)
+		{
+			e.printStackTrace();
+			in = new BufferedReader(new InputStreamReader(inStream));
+		}
+		
+		XmlObjectParser parser = new XmlObjectParser();
+		parser.mapOnStart("item", TaggingPreset.class);
+		parser.map("text", Text.class);
+		parser.map("check", Check.class);
+		parser.map("combo", Combo.class);
+		parser.map("label", Label.class);
+		parser.map("key", Key.class);
+		parser.start(in);
+		
+		while(parser.hasNext())
+		{
+			Object obj = parser.next();
+			if (obj instanceof Combo) {
+				Combo combo = (Combo)obj;
+				for(String value : combo.values.split(",") )
+					presetsValueData.add(combo.key, value);
+			}
+		}
+	}
+
+	/**
+	 * Reads the tagging presets
+	 */
+	public static void readPresetFromPreferences()
+	{
+		String allAnnotations = Main.pref.get("taggingpreset.sources");
+		StringTokenizer st = new StringTokenizer(allAnnotations, ";");
+		while (st.hasMoreTokens())
+		{
+			InputStream in = null;
+			String source = st.nextToken();
+			try 
+			{
+				if (source.startsWith("http") || source.startsWith("ftp") || source.startsWith("file"))
+					in = new URL(source).openStream();
+				else if (source.startsWith("resource://"))
+					in = Main.class.getResourceAsStream(source.substring("resource:/".length()));
+				else
+					in = new FileInputStream(source);
+				readPresets(in);
+				in.close();
+			} 
+			catch (IOException e)
+			{
+				// Error already reported by JOSM
+			} 
+			catch (SAXException e)
+			{
+				// Error already reported by JOSM
+			}
+		}
 	}
 
@@ -34,23 +361,228 @@
 	public void startTest()
 	{
-		_errorWays = new Bag<Way, Way>();
-	}
-
-	@Override
-	public void endTest()
-	{
-		_errorWays = null;
+		checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS);
+		if( isBeforeUpload )
+			checkKeys = checkKeys && Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD, true);
+
+		checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES);
+		if( isBeforeUpload )
+			checkValues = checkValues && Main.pref.getBoolean(PREF_CHECK_VALUES_BEFORE_UPLOAD, true);
+
+		checkFixmes = Main.pref.getBoolean(PREF_CHECK_FIXMES);
+		if( isBeforeUpload )
+			checkFixmes = checkFixmes && Main.pref.getBoolean(PREF_CHECK_FIXMES_BEFORE_UPLOAD, true);
+	}
+
+	@Override
+	public void visit(Collection<OsmPrimitive> selection)
+	{
+		if( checkKeys || checkValues)
+			super.visit(selection);
 	}
 	
 	@Override
-	public void visit(Node n)
-	{
-/* ... */
-	}
-
-	@Override
-	public void visit(Way w)
-	{
-/* ... */
+	public void addGui(JPanel testPanel)
+	{
+		GBC a = GBC.eol();
+		a.anchor = GBC.EAST;
+
+		testPanel.add( new JLabel(name), GBC.eol().insets(3,0,0,0) );
+		
+		boolean checkKeys = Main.pref.getBoolean(PREF_CHECK_KEYS, true);
+		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(20,0,0,0));
+
+		prefCheckKeysBeforeUpload = new JCheckBox();
+		prefCheckKeysBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_KEYS_BEFORE_UPLOAD, true));
+		testPanel.add(prefCheckKeysBeforeUpload, a);
+
+		Sources = new JList(new DefaultListModel());
+
+		String sources = Main.pref.get( PREF_SOURCES );
+		StringTokenizer st = new StringTokenizer(sources, ";");
+		while (st.hasMoreTokens())
+			((DefaultListModel)Sources.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("TagChecker source"));
+				if (source != null)
+					((DefaultListModel)Sources.getModel()).addElement(source);
+				Sources.clearSelection();
+			}
+		});
+
+		editSrcButton = new JButton(tr("Edit"));
+		editSrcButton.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				int row = Sources.getSelectedIndex();
+				if(row == -1 && Sources.getModel().getSize() == 1)
+				{
+					Sources.setSelectedIndex(0);
+					row = 0;
+				}
+				if (row == -1)
+				{
+					if(Sources.getModel().getSize() == 0)
+					{
+						String source = JOptionPane.showInputDialog(Main.parent, tr("TagChecker source"));
+						if (source != null)
+							((DefaultListModel)Sources.getModel()).addElement(source);
+					}
+					else
+					{
+						JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
+					}
+				}
+				else {
+					String source = JOptionPane.showInputDialog(Main.parent, tr("TagChecker source"), Sources.getSelectedValue());
+					if (source != null)
+						((DefaultListModel)Sources.getModel()).setElementAt(source, row);
+				}
+				Sources.clearSelection();
+			}
+		});
+
+		deleteSrcButton = new JButton(tr("Delete"));
+		deleteSrcButton.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				if (Sources.getSelectedIndex() == -1)
+					JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
+				else {
+					((DefaultListModel)Sources.getModel()).remove(Sources.getSelectedIndex());
+				}
+			}
+		});
+		Sources.setVisibleRowCount(3);
+
+		Sources.setToolTipText(tr("The sources (url or filename) of spell check (see http://wiki.openstreetmap.org/index.php/User:JLS/speller) or tag checking data files."));
+		addSrcButton.setToolTipText(tr("Add a new 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("Data sources")), GBC.eol().insets(23,0,0,0));
+		testPanel.add(new JScrollPane(Sources), GBC.eol().insets(23,0,0,0).fill(GBC.HORIZONTAL));
+		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));
+
+		ActionListener disableCheckKeysActionListener = new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				boolean selected = prefCheckKeys.isSelected() || prefCheckKeysBeforeUpload.isSelected();
+				Sources.setEnabled( selected );
+				addSrcButton.setEnabled(selected);
+				editSrcButton.setEnabled(selected);
+				deleteSrcButton.setEnabled(selected);
+			}
+		};
+		prefCheckKeys.addActionListener(disableCheckKeysActionListener);
+		prefCheckKeysBeforeUpload.addActionListener(disableCheckKeysActionListener);
+
+		Sources.setEnabled( checkKeys );
+		buttonPanel.setEnabled( checkKeys );
+
+		boolean checkValues = Main.pref.getBoolean(PREF_CHECK_VALUES, true);
+		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(20,0,0,0));
+
+		prefCheckValuesBeforeUpload = new JCheckBox();
+		prefCheckValuesBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_VALUES_BEFORE_UPLOAD, true));
+		testPanel.add(prefCheckValuesBeforeUpload, a);
+
+		boolean checkFixmes = Main.pref.getBoolean(PREF_CHECK_FIXMES, true);
+		prefCheckFixmes = new JCheckBox(tr("Check for FIXMES."), checkFixmes);
+		prefCheckFixmes.setToolTipText(tr("Looks for nodes or ways with FIXME in any property value."));
+		testPanel.add(prefCheckFixmes, GBC.std().insets(20,0,0,0));
+
+		prefCheckFixmesBeforeUpload = new JCheckBox();
+		prefCheckFixmesBeforeUpload.setSelected(Main.pref.getBoolean(PREF_CHECK_FIXMES_BEFORE_UPLOAD, true));
+		testPanel.add(prefCheckFixmesBeforeUpload, a);
+
+		boolean useDataFile = Main.pref.getBoolean(PREF_USE_DATA_FILE, true);
+		JCheckBox prefUseDataFile = new JCheckBox(tr("Use default data file."), checkValues);
+		prefUseDataFile.setToolTipText(tr("Use the default data file (recommended)."));
+		testPanel.add(prefUseDataFile, GBC.eol().insets(20,0,0,0));
+
+		boolean useSpellFile = Main.pref.getBoolean(PREF_USE_DATA_FILE, true);
+		JCheckBox prefUseSpellFile = new JCheckBox(tr("Use default spellcheck file."), checkValues);
+		prefUseSpellFile.setToolTipText(tr("Use the default spellcheck file (recommended)."));
+		testPanel.add(prefUseSpellFile, GBC.eol().insets(20,0,0,0));
+	}
+
+	@Override
+	public void ok()
+	{
+		enabled = prefCheckKeys.isSelected() || prefCheckValues.isSelected() || prefCheckFixmes.isSelected();
+		testBeforeUpload = prefCheckKeysBeforeUpload.isSelected() || prefCheckValuesBeforeUpload.isSelected() || prefCheckFixmesBeforeUpload.isSelected();
+
+		Main.pref.put(PREF_CHECK_VALUES, prefCheckValues.isSelected());
+		Main.pref.put(PREF_CHECK_KEYS, prefCheckKeys.isSelected());
+		Main.pref.put(PREF_CHECK_FIXMES, prefCheckFixmes.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_FIXMES_BEFORE_UPLOAD, prefCheckFixmesBeforeUpload.isSelected());
+		String sources = "";
+		if( Sources.getModel().getSize() > 0 )
+		{
+			StringBuilder sb = new StringBuilder();
+			for (int i = 0; i < Sources.getModel().getSize(); ++i)
+				sb.append(";"+Sources.getModel().getElementAt(i));
+			sources = sb.substring(1);
+		}
+		Main.pref.put(PREF_SOURCES, sources );
+	}
+
+	@Override
+	public Command fixError(TestError testError)
+	{
+		List<Command> commands = new ArrayList<Command>(50);
+
+		int i = -1;
+		List<? extends OsmPrimitive> primitives = testError.getPrimitives();
+		for(OsmPrimitive p : primitives )
+		{
+			i++;
+			Map<String, String> tags = p.keys;
+			if( tags == null || tags.size() == 0 )
+				continue;
+
+			for(Entry<String, String> prop: tags.entrySet() )
+			{
+				String key = prop.getKey();
+				String value = prop.getValue();
+				if( value == null || value.trim().length() == 0 )
+					commands.add( new ChangePropertyCommand(Collections.singleton(primitives.get(i)), key, null) );
+				else
+				{
+					String replacementKey = spellCheckKeyData.get(key);
+					if( replacementKey != null )
+						commands.add( new ChangePropertyKeyCommand(Collections.singleton(primitives.get(i)), key, replacementKey) );	
+				}
+			}
+		}
+
+		if( commands.size() == 0 )
+			return null;
+		else if( commands.size() == 1 )
+			return commands.get(0);
+		else
+			return new SequenceCommand(tr("Fix properties"), commands);
+	}
+
+	@Override
+	public boolean isFixable(TestError testError)
+	{
+		if( testError.getTester() instanceof TagChecker)
+		{
+			int code = testError.getInternalCode();
+			return code == INVALID_KEY || code == EMPTY_VALUES;
+		}
+
+		return false;
 	}
 }
Index: /applications/editors/josm/plugins/validator/tagchecker.cfg
===================================================================
--- /applications/editors/josm/plugins/validator/tagchecker.cfg	(revision 9269)
+++ /applications/editors/josm/plugins/validator/tagchecker.cfg	(revision 9269)
@@ -0,0 +1,30 @@
+# JOSM TagChecker validator file
+
+# format:
+# each line specifies a certain error to be reported
+# <data type>: <key><expression><value>
+#
+# data type can be:
+#  node        - a node point
+#  way         - a way
+#  relation    - a relation
+#  *           - all data types
+#
+# key and value are expressions describing certain keys and values of these keys
+# regulator expressions are supported. In this case the expressions starts and
+# ends with // signs. The * sign indicates any string.
+#
+# expression can be:
+#  !=          - the key/value combination does not match
+#  ==          - the key/value combination does match
+#
+# to have more complicated expressions, multiple elements can be grouped together
+# with and logical and (&&).
+#
+# Empty lines and space signs are ignored
+
+node : bridge == *
+node : highway == tertiary
+node : highway == secondary
+way  : highway == secondary && ref != *
+way  : highway == tertiary && ref != *
