Index: utils/josm/plugins/validator/.classpath
===================================================================
--- utils/josm/plugins/validator/.classpath	(revision 2453)
+++ utils/josm/plugins/validator/.classpath	(revision 2453)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/josm"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JDK 5"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: utils/josm/plugins/validator/.project
===================================================================
--- utils/josm/plugins/validator/.project	(revision 2453)
+++ utils/josm/plugins/validator/.project	(revision 2453)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>validator</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: utils/josm/plugins/validator/README
===================================================================
--- utils/josm/plugins/validator/README	(revision 2453)
+++ utils/josm/plugins/validator/README	(revision 2453)
@@ -0,0 +1,1 @@
+A OSM data validator that checks for common errors.
Index: utils/josm/plugins/validator/build.xml
===================================================================
--- utils/josm/plugins/validator/build.xml	(revision 2453)
+++ utils/josm/plugins/validator/build.xml	(revision 2453)
@@ -0,0 +1,48 @@
+<project name="validator" default="build" basedir=".">
+
+	<!-- point to your JOSM directory -->
+	<property name="josm" location="../josm/dist/josm-custom.jar" />
+
+
+	
+	<target name="init">
+		<mkdir dir="build"/>
+	</target>
+
+	<target name="compile" depends="init">
+		<javac srcdir="src" classpath="${josm}" destdir="build" debug="true">
+			<include name="**/*.java" />
+		</javac>
+	</target>
+
+	<target name="build" depends="clean, compile">
+		<copy todir="build/resources">
+			<fileset dir="resources"/>
+		</copy>
+		<copy todir="build/images">
+			<fileset dir="images"/>
+		</copy>
+		<jar destfile="validator.jar" basedir="build">
+			<manifest>
+				<attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.validator.OSMValidatorPlugin"/>
+				<attribute name="Plugin-Description" value="A OSM data validator"/>
+				<attribute name="Author" value="Francisco R. Santos &lt;frsantos@gmail.com>"/>
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="clean">
+		<delete dir="build" />
+	</target>
+
+	<target name="install" depends="build">
+		<copy file="validator.jar" todir="${user.home}/.josm/plugins"/>
+	</target>
+
+	<target name="test" depends="install">
+		<java jar="${josm}" fork="true">
+			<arg value="/home/frsantos/Desktop/JOSM/Tracks/Auto_1007_13.gpx"/>
+		</java>
+	</target>
+
+</project>
Index: utils/josm/plugins/validator/resources/spellCheck.data
===================================================================
--- utils/josm/plugins/validator/resources/spellCheck.data	(revision 2453)
+++ utils/josm/plugins/validator/resources/spellCheck.data	(revision 2453)
@@ -0,0 +1,999 @@
++abutters
+-abuters
+-abbutter
+-abbutters
+-abuttors
+-abuutters
+-ubutters
++abutter
++address
++amenity
+-amenety
+-amenitry
+-amnity
+-amenity 
+-amentiy
+-aminity
+-amneity
+-amnenity
+-aminety
+-amentity
+-ammenity
+-ameinty
+-anemity
+-amneity
+-amemity
+-ameity
+-amenity:
+-amenty
+-Amenity
+-maenity
++amenitylanduse
++atm
++bicycle
+-bycycle
+-biycle
+-bycicle
+-bicyle
++bike
+- bike
++biological
++by
++City
++class
+-Class
++classification
++ele
++emity
++code_departement
++code_INSEE
++commercial
+-comercial
++comment
++confirmed
++created_by
+-created by
+-cretaed_by
+-crated_by
+-creared_by
+-creayed_by
+-{created_by
+- created_by
+-creeated_by
+-created_bu
++crossing
++denomination
+-denomionation
+-denomation
+-demonination
+-demomination
+-denomition
+-denomincation
+-denominatation
+-denoination
++domination
++faith
++footway
++foot
+- foot
+-foor
++height
+-hieght
++highway
+-huighway
+-highwayt
+-ghway
+-highaway
+-highwway
+-ighway
+-higjway
+-hioghway
+-hiway
+-hihjway
+-higheway
+-highwaY
+-hughway
+-hihgway
+-higoway
+-highwat
+-highwah
+-gighway
+-higyway
+-hichway
+-HIGHWAY
+-hingway
+-hhighway
+-highwayu
+-hyighway
+-hiughway
+-highwya
+-hifhway
+-hihway
+-hifgway
+-highway:
+-highjway
+-highway 
+-highwy
+-hgihway
+-highawy
+-highwau
+-highay
+-higbway
+-hignway
+-higway
+-highwayx
+- highway
+-hoghway
+-highwa
+-Highway
+-hghway
++highways
++highspeed
++highwaytype
++highway_type
++highwayunclassified
++horse
++hvg
++iata_ref
++icao_ref
++image
+-imaqge
++island
++layer
+-layer 
+-elevation
+-leyer
+-lyaer
+- layer
++layer2
++layers
++layout
++leisure
+-leissure
+-leiruse
+-lesure
+-leisure 
+-Leisure
+-liesure
+-lieusure
+-lesiure
+-leasure
++naam
++name
+-nname
+-naem
+-nmae
+-nacme
+-n ame
+-name`
+-namw
+-bame
+-nam
+- name
+-namr
+-name 
+-anme
+-Name
+-name:
+-NAME
+-name;
++name_1
++name_4
++named_by
++names
++note
+-Note
+-note 
++Number
++oneway
+-onewway
+-onewau
+-oneay
+-neway
+-oneweay
+-onewa
+-omeway
+-one_way
+-onway
+- oneway
+-oeway
++onte
++osmarender:nameDirection
+-name_direction
+-name-direction
+-osmarender:name_direction
++place
+-place 
+-Place
++passing
++railway
+- railway
+-Railway
+-raillway
++regional_name
++ref
+-ref 
+-ref:
+-Ref
++ref:source
++ref_nat
++ref_int
++retail
++time
++seats
++square
++soccer
++source
+-sorce
+-soruce
+-soure
+-souce
+-sourec
+-aource
+-sourse
++source2
++source:ncn_ref
++source:highway
++status
++way
++waterway
+- waterway
+-waterwa
+-Waterway
+-waterwy
+-wateway
++wood
++unknown
+-unknwon
++ 
++1
++4wd
++ car
++ class
++ horse
++ sport
++ open 09:00 - 16:00 daily
++ General McArthur lived here
++ Also coaches
++80n:ibm
++amenities
++bar
++batteries
++Bezeichnung
++boder
++Brand
++Food
++Fussweg
++Hauptstrasse
++POI
++Park
++Strasse
++abutment
++abutts
++access
++accident_and_emergency
++aerialway
+-areilway
+-areialway
+-arielway
++aeroway
+-aeoroway
+-areoway
+-airoway
++ageofdgpsdata
++airport
+-aeroport
++airport_ident
++airport_ref
++airway
++alias
++alt
++altname
++alt_name
++alt_name_2
++alternative_name
++altitude
++alt_ref
++ame
++annotation
++annotate
++angle
++angle_to_last
++appearance
++approximate
++area
++art
++ascii_name
++author
++autocar
++b test tag
++badminton
++barnvagn
++barrier
++bb:name
++beach
++bicycleRoute
++bicycle_Route
++blackadder:name
++blackadder:commerce
++blackadder:cuisine
++blackadder:service
++blackadder:civic
++bicycles
++boat
++border
++borded
++border_edit
++border_type
++boundary
++boundary_name
++boundary_type
++branch_code:fa
++branch_name:fa
++brand
++brewery
++bridge
+-brdige
+-bridgde
+-birdge
+-   bridge
+-bidge
+-brige
+-brigde
+-bridgw
++bridleway
++building
+-buillding
+-buiding
+-bulding
++build_date
++bus
++buss
++busway
++bus_routes
++building_type
++cafe
++capacity
++Car
++car
++cars
++carsharing
++category
++caution
++charge
++chemin
++checked_by
++cheshire_cycleway_ref
++christian_denomination
++cladding
++city
+-citya
++city_id
++clothes
++cmt
++complete
+- complete
+-complite
++condition
++construction
++controlled
++converted_by
++core
++cost
++country
++course
++cover
++covered
++creator
++cycle
++cycleRoute
++cycleway
+-cycleway:
++cykel
++cuisine
++cusine
++cutting
++d_lat
++d_lon
++danger
++date
++date_off
++day_off
++day_on
++dead-end
++deadend
++depth
++desc
++description
+-descripion
+-desription
+-decription
++descriptions
++ details:naco
++destination
++difficulty
++direction
++dispensing
++distance
++distance_meter
++disused
++ECautomaton
++edited_some_more_by
++editor
++editor note
++ef
++elevated
++embankment
++emergency
++emergency_ward
++error
++exit
++exit_nr
++exit_name
++external_description
++external_link
++ev_charge
++facility
++farezone
++fastfood
++FACC_CODE
++feature
+-featuer
++feature: NGIA map
+- feature: NGIA map
++features
++feet
++ferry
++fenced
++fire
++first_number
++fix
++fixme
++foobar
++food
++footpath
+-fottpath
++forrest
++foto
++free
++freight
+-frieght
++from_to
++from_zip
++fuel_diesel
++fuel_lpg
++fuel_octane_91
++fuel_octane_95
++fuel_octane_98
++full_name:fa
+-full_name:Fa
++full_name
++gate
++geoname_id
++glass
++glutenfree
++gluten_free
++goods
++gps_network
++grade
++grind
++gym
++halt
++hazard
++hame
++hdop
++helped_by
++heritage
++hgv
++highway E-number
++highway_border
+-highway_boarder
+-highway_boreder
++hill
++historic
++historic name
++historical
++history
++Higgy:ref
++hotelclass
++hospital:operator
++hour_off
++hour_on
++hours
++house_numbers
++iata
++icao
++id
+-ID
++import_ref
++incomplete
++incline_steep
++incline
++industrial
++info
++ info :naco
++infopoint
++informal_name
++int_name
+-int name
+-iint_name
++int_ref
++interpolation
++intersection
++is
++is_in
+-is in
+- is_in
++in_in
++is_in:de
++is_in:es
++junction
+-junctioin
+-juntion
+-junctiion
+-jounction
+-jumction
+- junction
+-juction
+-Junction
+-junction 
++junction_ref
++junction:ref
++junction:name
++key
++Kingsmede
++label
++landuse
+-land_use
+-lansuse
+-lanudse
+-lanuse
++lane
++lanes
+-Lanes
++last_number
++ Last Edit: Batchoy 2006-12-14
++learning
++length
++level
++license
++liftStation
++liftType
++lighthouse
++line
++lines
++link:naming
++linje
++lit
++loc_ref
++loc_name
+-loc_name 
++loc_name:fa
++local_name
+-local_nama
++local_ref
++locality
++long_name
++lorries
++ma,e
++main
++man_made
+-man_made 
+-mand_made
+-nan_made
+-madmade
+-manmade
++mapkey
++maplint:error
++maplint:notice
++maplint:warning
++mapping_status
++markedtrail
++marker
++marching_step
++min_speed
++minspeed
++max_speed
++max speed
++maxheight
+-max_hieght
+-max_height
++maxspeed
+-mayspeed
+-maxspeed 
+-maxpeed
++maxweight
+-maxwieght
+-max_weight
+-max_wieght
+-maxweihgt
++maxwidth
++membership
++memorial
++menu
++military
++mixed
++monument
++more_data
++motor
++motorbike
++motorcar
+- motorcar
+-motocar
++motor_car
++motorcars
+-motocars
++motorcycle
+-motocycle
+-motorcycle 
++motorway
++motorway station
++motorway_junction
++munro
++museum
++name.2
++name.alt
++name.en
++name:ar
++name:af
++name:cy
++name:de
++name:en
++name:es
++name:eu
++name:fa
++name:fi
++name:fr
++name:gd
++name:la
++name:my
++name:nl
++name:sv
++name.se
++name.short
++name:zh-Latn
++name1
++name2
++name int
++name_int
++name:source
++name_source
++name:cym
++name_ie
++name_loc
++name_segment
++namelayer
++name:ref
++nat_name
++nat_ref
++nat_pref
++nat_reg
++natural
+-nataural
+- natural
+-natrual
+-nautral
+-natural 
++natural2
++nature
++navigable
++ncn_name
++ncn_ref
++ncn:ref
++needs_to_be
++network
++net_ref
++newsagent_code
++nickb_marker
++nlanes
++noat
++node
++noentry
++noexit
+- noexit
++notes
++note_1
+-note_!
++note_2
+-note2
++note_3
++note_4
++note_
++noturn
++number
++numbers
++obstacles
++obstruction
++official
++ojw2
++ojw_test
++old_name
+-oldname
++old_name:fa
++old_ref
++opened
++open_in
++operator
++opm:capacity
++opm:difficulty
++opm:liftStation
++opm:liftType
+-opm:lifttype
++osmarender:renderName
+-osmarender:rendername
+-soamrender:renderName
++osmarender:renderRef
+-osmrender:renderRef
++owner
++owners
++park_and_ride
++parking
++parking:cost
++parking:spaces
++passenger
++path
++paved
++pcv_only
++pdop
++pedestrian
++pedestrians
++permissive
++phone
++phone_number
++physical
++place_postal
++place numbers
++place_code
++place_name
+- place_name
++place_numbers
++place_of_worship
++plave
++playe
++plant
++poi
++point_of_interest
++population
++popul
++port
++position_accuracy
++post_code
++postal_code
+- postal_code
+-posatl_code
++postcode
++power
++primary
++private
++provided
++Properties
++problem
++psv
++public
++public_transportation
++punting
++quality
++rail
++railroad
++railway_tracks
++ramp
++recreation
++recycling
++recycling:batteries
++recycling:bicycles
++recycling:books
++recycling:glas
+-recyling:glas
++recycling:shoes
++recycling:clothes
++recycling:engine_oil
++recycling:glass_bottles
++recycling:green_waste
++recycling:magazines
++recycling:mobile_phones
++recycling:newspaper
++recycling:newspapers
++recycling:printer_cartridges
++recycling:cardboard
++recycling:music
++recycling:paper
+-reycling:paper
++recycle:plastic_bottles
++recycle:paper
++recycle:magazines
++recycle:cardboard
++recycle:cans
++recycling:cans
++recycling:plastic_bottles
++recycline:cardboard
++recycle:glass_bottles
++recycling:cork
++recycling:glass
++recycling:plastic
++recycling:plastic_bags
++recycling:plasic_bottles
++recycling:scrap_metal
++recycling:white_goods
++recycling:wood
++recycling:tyres
++reference
++ref_direction
++reg_name
++reg_ref
++region_id
++religion
+-relgion
++residence
++residential
+-residentail
++restriction
+-restrction
+-Restriction
++restricted
++restrictions
+-Restrictions
+-Restrictions 
++riverwidth
++river_width
++road
++rollerblade
++roundabout
++route
++routing
++rue
++runway
++sat
++sculpture
++shape
++sheltered
++shortcut
++sidewalk
++sign
++single_track
++shop
++shopping
++size
++ski
++slope
++snowboarding
++some_data
++source:loc_name
++source:old_name
++source:old_ref
++source:name
+-source:Name
++source:ref
++source_ref
++source_ref:ref
++source_ref:name
++source:uri
++source_uri
++source:url
+-source_url
++source:oneway
++southglos:heritagetrail
++species
++speed
++speed_limit
++speed limit
++speedlimit
++speedevil
++sport
++sport_2
++sports
++stairs
++start_date
++state
++station
++stream
++street
++street_name
++steps
++subtype
++suburb
++suggested
++surface
+-sruface
++surfaced
++svg:font-size
++svg:stroke-width
++svg_font-size
++svg:stroke-dasharray
++swimming
++sym
++symbol
++symbolic
++tag
++taxi
++telephone
++telephone:operator
++telephone_number
++telephone_type
++test
++testing
++testnode
++ this was a children's playarea with a path leading through it. Didn't want to look too dodgy :)
++thoroughfare
++time_diff
++times
++tractor
++track
++tracktype_1
++to_zip
++todo
++TODO
++toll
++topspeed
++tourism
+-tourim
+-tourism 
+-toursim
+-touristm
++tourist
++tourist_attraction
++towards
++town
++towpath
++tracks
++traffic
++traffic_signals
++train
++tracktype
+-tractype
+-tracltype
+-trackype
++tram
++tramline
++true
++truck
++tube
++tune
++tunnel
+-tunne
+-tunnel 
+-Tunnel
+-tunel
+-tunnely
++turn_right
++typ
++type
+-tyoe
++type.en
++uk:row
++unclassified
++upload_tag
++uploader
++uploaded_by
++uri
++url
++use
++use_status
++utility
++vdop
++vehicle
++viaduct
++vicar
++view
++village
++visited_by
++voltage
++warning
++water
++waterfall
++wayclass
++waypoint
++web
++website
++website:official
++weight_limit
++wide
++width
++width_restriction
++wiki
++wikipedia
++wikipedia:es
++wiki:nl
++wrong
++www
++zip
++{}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreeRenderer.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreeRenderer.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ErrorTreeRenderer.java	(revision 2453)
@@ -0,0 +1,47 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import org.openstreetmap.josm.plugins.validator.util.MultipleNameVisitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Tree renderer for displaying errors
+ * @author frsantos
+ */
+public class ErrorTreeRenderer extends DefaultTreeCellRenderer 
+{
+    /** Serializable ID */
+    private static final long serialVersionUID = 5567632718124640198L;
+
+    @Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean selected, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) 
+	{
+		super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
+		
+		DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
+		Object nodeInfo = node.getUserObject();		
+		
+		if (nodeInfo instanceof Severity) 
+		{
+			Severity s = (Severity)nodeInfo;
+			setIcon(ImageProvider.get("data", s.getIcon()));
+		}
+		else if (nodeInfo instanceof TestError) 
+		{
+			TestError error = (TestError)nodeInfo;
+			MultipleNameVisitor v = new MultipleNameVisitor();
+			v.visit(error.getPrimitives());
+			setText(v.getText());
+			setIcon(v.getIcon());
+		}
+
+		return this;
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/OSMValidatorPlugin.java	(revision 2453)
@@ -0,0 +1,160 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.validator.tests.*;
+import org.openstreetmap.josm.plugins.validator.util.Util;
+
+/**
+ * 
+ * A OSM data validator
+ * 
+ * @author Francisco R. Santos <frsantos@gmail.com>
+ */
+public class OSMValidatorPlugin extends Plugin 
+{
+    /** The validate action */
+    ValidateAction validateAction = new ValidateAction();
+    
+    /** The validation dialog */
+    ValidatorDialog validationDialog;
+    
+    /** The list of errors */
+    List<TestError> errors = new ArrayList<TestError>(30);
+
+    /** 
+     * All available tests 
+     * TODO: is there any way to find out automagically all available tests? 
+     */
+    public static Class[] allAvailableTests = new Class[]
+    { 
+    	DuplicateNode.class, 
+    	DuplicateSegment.class, 
+    	SingleNodeSegment.class, 
+    	TaggedSegment.class, 
+    	UnorderedWay.class, 
+    	SpellCheck.class,
+    	UntaggedNode.class, 
+    	OrphanSegment.class, 
+    };
+
+	/**
+	 * Creates the plugin, and starts the HTTP server
+	 */
+	public OSMValidatorPlugin()
+	{
+        initializeTests( getTests(true) );
+	}
+	
+    @Override
+	public PreferenceSetting getPreferenceSetting() 
+	{
+		return new PreferenceEditor();
+	}
+	
+	@Override
+	public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) 
+	{
+		if (newFrame != null)
+		{
+		    validationDialog = new ValidatorDialog();
+	        newFrame.addToggleDialog(validationDialog);
+		}
+	}
+
+	
+	/**
+	 * Utility method for classes that can't access the plugin directly
+	 * 
+	 * @return The plugin object
+	 */
+	public static OSMValidatorPlugin getPlugin() 
+	{
+		return (OSMValidatorPlugin)Util.getPlugin(OSMValidatorPlugin.class);
+	}
+	
+	/**
+	 * Gets a collection with the available tests
+	 * 
+	 * @param onlyActive if true, gets only active tests
+	 * @return A collection with the available tests
+	 */
+	public static Collection<Test> getTests(boolean onlyActive)
+	{
+		Map<String, Test> enabledTests = new LinkedHashMap<String, Test>();
+		for(Class<Test> testClass : getAllAvailableTests() )
+		{
+			Test test;
+			try {
+				test = testClass.newInstance();
+			}
+			catch( Exception e)
+			{
+				e.printStackTrace();
+				continue;
+			}
+			test.enabled = true;
+			enabledTests.put(testClass.getSimpleName(), test);
+		}
+
+		Pattern regexp = Pattern.compile("(\\w+)=(true|false),?");
+		Matcher m = regexp.matcher(Main.pref.get("tests"));
+		int pos = 0;
+		while( m.find(pos) )
+		{
+			String testName = m.group(1);
+			Test test = enabledTests.get(testName);
+			if( test != null )
+			{
+				test.enabled = Boolean.valueOf(m.group(2)).booleanValue();
+				if( onlyActive && !test.enabled)
+					enabledTests.remove(test.getClass().getSimpleName() );
+			}
+			pos = m.end();
+		}
+		return enabledTests.values();
+	}
+	
+    /**
+     * Gets the list of all available test classes
+     * 
+     * @return An array of the test classes
+     */
+    public static Class[] getAllAvailableTests()
+    {
+        return allAvailableTests;
+    }
+    
+	/**
+	 * Initializes all tests
+	 * @param allTests The tests to initialize
+	 */
+	public void initializeTests(Collection<Test> allTests)
+	{
+		for( Test test : allTests )
+		{
+			try
+			{
+				if( test.enabled )
+				{
+					test.getClass().getMethod("initialize", new Class[] { OSMValidatorPlugin.class} ).invoke(null, new Object[] {this});
+				}
+			} 
+			catch(Exception e) 
+			{
+				e.printStackTrace();
+				JOptionPane.showMessageDialog(null, tr("Error initializing test {0}.", test.getClass().getSimpleName()));
+			}
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/PreferenceEditor.java	(revision 2453)
@@ -0,0 +1,85 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Collection;
+
+import javax.swing.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.validator.util.Util;
+import org.openstreetmap.josm.plugins.validator.util.Util.Version;
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * Preference settings for the validator plugin
+ * 
+ * @author frsantos
+ */
+public class PreferenceEditor implements PreferenceSetting
+{
+	/** The list of all tests */
+	private Collection<Test> allTests;
+
+    public void addGui(PreferenceDialog gui)
+    {
+		JPanel testPanel = new JPanel(new GridBagLayout());
+		testPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+		
+		allTests = OSMValidatorPlugin.getTests(false);
+		for(final Test test: allTests) 
+		{
+			final JCheckBox testCheck = new JCheckBox(test.name, test.enabled);
+			testCheck.setToolTipText(test.description);
+			testPanel.add(testCheck, GBC.eop().insets(20,0,0,0));
+			test.addGui(testPanel);
+
+			testCheck.addActionListener(new ActionListener(){
+				public void actionPerformed(ActionEvent e) {
+					test.enabled = testCheck.isSelected();
+				}
+			});
+		}
+		
+		JScrollPane testPane = new JScrollPane(testPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+		testPane.setBorder(null);
+
+		Version ver = Util.getVersion();
+		String description = tr("A OSM data validator that checks for common errors made by users and editor programs.");
+		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);
+		tab.add(testPane, GBC.eol().fill(GBC.BOTH));
+		tab.add(GBC.glue(0,10), GBC.eol());
+    }
+
+	public void ok() 
+	{
+		String tests = "";
+		
+		for (Test test : allTests)
+		{
+			boolean enabled = test.enabled;
+			String name = test.getClass().getSimpleName();
+			tests += name + "=" + enabled + ",";
+			
+			if (enabled)
+			{
+				test.ok();
+			}
+		}
+		
+		if (tests.endsWith(","))
+			tests = tests.substring(0, tests.length() - 1);
+		
+		OSMValidatorPlugin.getPlugin().initializeTests( allTests );
+		
+		Main.pref.put("tests", tests);
+	}
+
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Severity.java	(revision 2453)
@@ -0,0 +1,46 @@
+package org.openstreetmap.josm.plugins.validator;
+
+/** The error severity */
+public enum Severity {
+	/** Error messages */
+	ERROR("Errors", "error.gif"),
+	/** Warning messages */ 
+	WARNING("Warnings", "warning.gif"), 
+	/** Other messages */ 
+	OTHER("Other", "other.gif"); 
+	
+	/** Description of the severity code */
+	private final String message;
+	
+	/** Associated icon */
+	private final String icon;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param message Description
+	 * @param icon Associated icon
+	 */
+    Severity(String message, String icon) 
+    {
+        this.message = message;
+		this.icon = icon;
+    }
+
+	@Override
+	public String toString() 
+	{
+		return message;
+	}
+
+	/** 
+	 * Gets the associated icon
+	 * @return the associated icon
+	 */
+	public String getIcon() 
+	{
+		return icon;
+	}
+    
+    
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/Test.java	(revision 2453)
@@ -0,0 +1,118 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import java.util.*;
+
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.data.osm.*;
+import org.openstreetmap.josm.data.osm.visitor.Visitor;
+
+/**
+ * Parent class for all validation tests.
+ * <p>
+ * A test is a primitive visitor, so that it can access to all data to be
+ * validated. These primitives are always visited in the same order: nodes
+ * first, then segments, and ways last.
+ * 
+ * @author frsantos
+ */
+public class Test implements Visitor
+{
+	/** Name of the test */
+	protected String name;
+	
+	/** Description of the test */
+	protected String description;
+	
+	/** Whether this test is enabled. Used by peferences */
+	protected boolean enabled;
+
+	/** The list of errors */
+	protected List<TestError> errors = new ArrayList<TestError>(30);
+
+	/**
+	 * Constructor
+	 * @param name Name of the test
+	 * @param description Description of the test
+	 */
+	public Test(String name, String description)
+	{
+		this.name = name;
+		this.description = description;
+	}
+	
+	/**
+	 * Constructor
+	 * @param name Name of the test
+	 */
+	public Test(String name)
+	{
+		this.name = name;
+	}
+	
+	/**
+	 * Initializes any global data used this tester.
+	 * @param plugin The plugin
+	 * @throws Exception When cannot initialize the test
+	 */ 
+	public static void initialize(@SuppressWarnings("unused") OSMValidatorPlugin plugin) throws Exception {}
+	
+	/**
+	 * Notification of the start of the test. The tester can initialize the
+	 * structures it may need for this test
+	 */ 
+	public void startTest() 
+	{
+		errors = new ArrayList<TestError>(30);
+	}
+	
+	/**
+	 * Gets the validation errors accumulated until this moment.
+	 * @return The list of errors 
+	 */
+	public List<TestError> getErrors() 
+	{
+		return errors;
+	}
+	
+	/**
+	 * Notification of the end of the test. The tester may perform additional
+	 * actions and destroy the used structures 
+	 */
+	public void endTest() {}
+
+    /**
+     * Visits all primitives to be tested. These primitives are always visited
+     * in the same order: nodes first, then segments, and ways last.
+     * 
+     * @param selection The primitives to be tested
+     */
+    public void visit(Collection<OsmPrimitive> selection) 
+    {
+        for (OsmPrimitive p : selection)
+        {
+            p.visit(this);
+        }
+    }
+
+    public void visit(Node n) {}
+
+	public void visit(Segment s) {}
+
+	public void visit(Way w) {}
+
+	/**
+	 * Allow the tester to manage its own preferences 
+	 * @param testPanel The panel to add any preferences component
+	 */
+	public void addGui(@SuppressWarnings("unused") JPanel testPanel) 
+	{
+	}
+
+	/**
+	 * Called when the used submits the preferences
+	 */
+	public void ok() 
+	{
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/TestError.java	(revision 2453)
@@ -0,0 +1,112 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+
+/**
+ * Validation error
+ * @author frsantos
+ */
+public class TestError
+{
+	/** Severity */
+	private Severity severity;
+	/** The error message */
+	private String message;
+	/** The affected primitives */
+	private List<? extends OsmPrimitive> primitives;
+	
+	/**
+	 * Constructor
+	 */
+	public TestError()
+	{
+	}
+
+	/**
+	 * Constructor
+	 * @param severity The severity of this error
+	 * @param message The error message
+	 * @param primitives The affected primitives
+	 */
+	public TestError(Severity severity, String message, List<? extends OsmPrimitive> primitives)
+	{
+		this.severity = severity;
+		this.message = message;
+		this.primitives = primitives;
+	}
+	
+	/**
+	 * Constructor
+	 * @param severity The severity of this error
+	 * @param message The error message
+	 * @param primitive The affected primitive
+	 */
+	public TestError(Severity severity, String message, OsmPrimitive primitive)
+	{
+		this.severity = severity;
+		this.message = message;
+		
+		List<OsmPrimitive> primitives = new ArrayList<OsmPrimitive>();
+		primitives.add(primitive);
+		
+		this.primitives = primitives;
+	}
+	
+	/**
+	 * Gets the error message
+	 * @return the error message
+	 */
+	public String getMessage() 
+	{
+		return message;
+	}
+	
+	/**
+	 * Sets the error message
+	 * @param message The error message
+	 */
+	public void setMessage(String message) 
+	{
+		this.message = message;
+	}
+	
+	/**
+	 * Gets the list of primitives affected by this error 
+	 * @return the list of primitives affected by this error
+	 */
+	public List<? extends OsmPrimitive> getPrimitives() 
+	{
+		return primitives;
+	}
+
+	/**
+	 * Sets the list of primitives affected by this error 
+	 * @param primitives the list of primitives affected by this error
+	 */
+
+	public void setPrimitives(List<OsmPrimitive> primitives) 
+	{
+		this.primitives = primitives;
+	}
+
+	/**
+	 * Gets the severity of this error
+	 * @return the severity of this error
+	 */
+	public Severity getSeverity() 
+	{
+		return severity;
+	}
+
+	/**
+	 * Sets the severity of this error
+	 * @param severity the severity of this error
+	 */
+	public void setSeverity(Severity severity) 
+	{
+		this.severity = severity;
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidateAction.java	(revision 2453)
@@ -0,0 +1,64 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.plugins.validator.util.AgregatePrimitivesVisitor;
+
+/**
+ * The action that does the validate thing.
+ * <p>
+ * This action iterates through all active tests and give them the data, so that
+ * each one can test it.
+ * 
+ * @author frsantos
+ */
+public class ValidateAction extends JosmAction 
+{
+	/** Serializable ID */
+    private static final long serialVersionUID = -2304521273582574603L;
+
+    /**
+	 * Constructor
+	 */
+	public ValidateAction()
+	{
+		super("Validation", "validator", "Performs the data validation", KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK + KeyEvent.ALT_MASK, true);
+	}
+
+	public void actionPerformed(ActionEvent ev)
+	{
+		OSMValidatorPlugin plugin = OSMValidatorPlugin.getPlugin();
+		plugin.errors = new ArrayList<TestError>();
+		plugin.validationDialog.setVisible(true);
+		
+		Collection<Test> tests = OSMValidatorPlugin.getTests(true);
+		if( tests.isEmpty() )
+			return;
+		
+		Collection<OsmPrimitive> selection = Main.ds.getSelected();
+		if( selection.isEmpty() )
+			selection = Main.ds.allNonDeletedPrimitives();
+		else
+		{
+			AgregatePrimitivesVisitor v = new AgregatePrimitivesVisitor();
+			selection = v.visit(selection);
+		}
+
+		for(Test test : tests) 
+        {
+		    test.startTest();
+		    test.visit(selection);
+			test.endTest();
+			plugin.errors.addAll( test.getErrors() );
+		}
+		tests = null;
+		
+		plugin.validationDialog.refresh();
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/ValidatorDialog.java	(revision 2453)
@@ -0,0 +1,214 @@
+package org.openstreetmap.josm.plugins.validator;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.tree.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
+import org.openstreetmap.josm.plugins.validator.util.Bag;
+import org.openstreetmap.josm.plugins.validator.util.Util;
+
+/**
+ * A small tool dialog for displaying the current selection. The selection manager
+ * respects clicks into the selection list. Ctrl-click will remove entries from
+ * the list while single click will make the clicked entry the only selection.
+ *
+ * @author imi
+ */
+public class ValidatorDialog extends ToggleDialog implements ActionListener
+{
+    /** Serializable ID */
+    private static final long serialVersionUID = 2952292777351992696L;
+
+    /**
+     * The validation data.
+     */
+	private DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
+
+	/**
+     * The display tree.
+     */
+    protected JTree tree = new JTree(treeModel);
+    
+
+    /**
+     * Constructor
+     */
+    public ValidatorDialog() 
+    {
+        super(tr("Validation errors"), "validator", tr("Open the validation window."), KeyEvent.VK_V, 150);
+        
+		tree.setRootVisible(false);
+		tree.setShowsRootHandles(true);
+		tree.expandRow(0);
+		tree.setVisibleRowCount(8);
+		tree.addMouseListener(new DblClickWatch());
+		tree.setCellRenderer(new ErrorTreeRenderer());
+		tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
+
+		add(new JScrollPane(tree), BorderLayout.CENTER);
+
+        JPanel buttonPanel = new JPanel(new GridLayout(1,2));
+
+        buttonPanel.add(Util.createButton("Select", "mapmode/selection/select", "Set the selected elements on the map to the selected items in the list above.", this)); 
+        add(buttonPanel, BorderLayout.SOUTH);
+        buttonPanel.add(Util.createButton("Validate", "dialogs/refresh", "Validate the data.", this)); 
+        add(buttonPanel, BorderLayout.SOUTH);
+    }
+
+    @Override 
+    public void setVisible(boolean v) {
+		if (v)
+			buildTree();
+		else if (tree != null)
+			treeModel.setRoot(new DefaultMutableTreeNode());
+		if( action != null && action.button != null )
+			action.button.setSelected(v);
+		super.setVisible(v);
+	}
+    
+    
+	/**
+	 * Builds the errors tree
+	 */
+	private void buildTree() 
+	{
+		DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
+
+		List<TestError> errorList = OSMValidatorPlugin.getPlugin().errors;
+		if( errorList == null || errorList.isEmpty() )
+		{
+			treeModel.setRoot(rootNode);
+			return;
+		}
+		
+		Map<Severity, Bag<String, TestError>> errorTree = new HashMap<Severity, Bag<String, TestError>>();
+		for(Severity s : Severity.values())
+		{
+			errorTree.put(s, new Bag<String, TestError>(20));
+		}
+		
+		for(TestError e : errorList)
+		{
+			errorTree.get(e.getSeverity()).add(e.getMessage(), e);
+		}
+		
+		for(Severity s : Severity.values())
+		{
+			Bag<String,	TestError> severityErrors = errorTree.get(s);
+			if( severityErrors.isEmpty() )
+				continue;
+			
+			// Severity node
+			DefaultMutableTreeNode severityNode = new DefaultMutableTreeNode(s);
+			rootNode.add(severityNode);
+			
+			for(Entry<String, List<TestError>> msgErrors : severityErrors.entrySet()  )
+			{
+				// Message node
+				List<TestError> errors = msgErrors.getValue();
+				String msg = msgErrors.getKey() + " (" + errors.size() + ")";
+				DefaultMutableTreeNode messageNode = new DefaultMutableTreeNode(msg);
+				severityNode.add(messageNode);
+				
+				for (TestError error : errors) 
+				{
+					// Error node
+					DefaultMutableTreeNode errorNode = new DefaultMutableTreeNode(error);
+					messageNode.add(errorNode);
+				}
+			}
+		}
+
+		treeModel.setRoot(rootNode);
+		tree.scrollRowToVisible(treeModel.getChildCount(rootNode)-1);
+	}
+	
+    /**
+     * Sets the selection of the map to the current selected items.
+     */
+    @SuppressWarnings("unchecked")
+    public void setSelectedItems() 
+    {
+        Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>(40);
+
+        TreePath[] selectedPaths = tree.getSelectionPaths();
+        for( TreePath path : selectedPaths)
+        {
+        	DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
+    		Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
+    		while( children.hasMoreElements() )
+    		{
+        		DefaultMutableTreeNode childNode = children.nextElement();
+        		Object nodeInfo = childNode.getUserObject();
+        		if( nodeInfo instanceof TestError)
+        		{
+        			TestError error = (TestError)nodeInfo;
+        			sel.addAll( error.getPrimitives() );
+        		}
+    		}
+        }
+        Main.ds.setSelected(sel);
+    }
+
+	public void actionPerformed(ActionEvent e) 
+	{
+		if( e.getActionCommand().equals("Select"))
+			setSelectedItems();
+		else if( e.getActionCommand().equals("Validate"))
+	    	OSMValidatorPlugin.getPlugin().validateAction.actionPerformed(e);
+	}
+	
+	/**
+	 * Refresh the error messages display
+	 */
+	public void refresh()
+	{
+		buildTree();
+	}
+	
+	/**
+	 * Watches for double clicks and from editing or new property, depending on the
+	 * location, the click was.
+	 * @author imi
+	 */
+	public class DblClickWatch extends MouseAdapter 
+	{
+        @SuppressWarnings("unchecked")
+        @Override 
+		public void mouseClicked(MouseEvent e) 
+		{
+			if (e.getClickCount() < 2 || e.getSource() instanceof JScrollPane)
+				return;
+			else 
+			{
+		        Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>(40);
+
+	        	DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
+	    		Enumeration<DefaultMutableTreeNode> children = node.breadthFirstEnumeration();
+	    		while( children.hasMoreElements() )
+	    		{
+	        		DefaultMutableTreeNode childNode = children.nextElement();
+	        		Object nodeInfo = childNode.getUserObject();
+	        		if( nodeInfo instanceof TestError)
+	        		{
+	        			TestError error = (TestError)nodeInfo;
+	        			sel.addAll( error.getPrimitives() );
+	        		}
+	    		}
+	    		Main.ds.setSelected(sel);
+			}
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateNode.java	(revision 2453)
@@ -0,0 +1,57 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+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 org.openstreetmap.josm.plugins.validator.util.Bag;
+/**
+ * Tests if there are duplicate nodes
+ * 
+ * @author frsantos
+ */
+public class DuplicateNode extends Test 
+{
+	/** Bag of all nodes */
+	Bag<LatLon, Node> nodes;
+	
+	/**
+	 * Constructor
+	 */
+	public DuplicateNode() 
+	{
+		super(tr("Duplicated nodes."),
+			  tr("This test checks that there are no nodes at the very same location."));
+	}
+
+
+	@Override
+	public void startTest() 
+	{
+		nodes = new Bag<LatLon, Node>(1000);
+	}
+
+	@Override
+	public void endTest() 
+	{
+		for(List<Node> duplicated : nodes.values() )
+		{
+			if( duplicated.size() > 1)
+			{
+				errors.add( new TestError(Severity.ERROR, tr("Duplicated nodes"), duplicated) );
+			}
+		}
+		nodes = null;
+	}
+
+	@Override
+	public void visit(Node n) 
+	{
+		nodes.add(n.coor, n);
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateSegment.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateSegment.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/DuplicateSegment.java	(revision 2453)
@@ -0,0 +1,97 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+import org.openstreetmap.josm.plugins.validator.util.Bag;
+/**
+ * Tests if there are duplicate segments
+ * 
+ * @author frsantos
+ */
+public class DuplicateSegment extends Test 
+{
+	/** Bag of all segments */
+	Bag<CompoundLatLon, Segment> segments;
+	
+	/**
+	 * Constructor
+	 */
+	public DuplicateSegment() 
+	{
+		super(tr("Duplicated segments."),
+			  tr("This test checks that two nodes are not used by more than one segment."));
+		
+	}
+
+
+	@Override
+	public void startTest() 
+	{
+		segments = new Bag<CompoundLatLon, Segment>(1000);
+	}
+
+	@Override
+	public void endTest() 
+	{
+		for(List<Segment> duplicated : segments.values() )
+		{
+			if( duplicated.size() > 1)
+			{
+				errors.add( new TestError(Severity.ERROR, tr("Duplicated segments"), duplicated) );
+			}
+		}
+		segments = null;
+	}
+
+	@Override
+	public void visit(Segment s) 
+	{
+		if( !s.incomplete ) segments.add( new CompoundLatLon(s), s);
+	}
+	
+	/**
+	 * Compound LatLong for easy duplicity check
+	 * @author frsantos
+	 */
+	class CompoundLatLon
+	{
+		/** From position */
+		LatLon from;
+		/** To position */
+		LatLon to;
+		
+		/**
+		 * Constructor
+		 * @param s The segment
+		 */
+		public CompoundLatLon(Segment s)
+		{
+			this.from = s.from.coor;
+			this.to = s.to.coor;
+		}
+
+		@Override
+		public boolean equals(Object obj) 
+		{
+			if (obj == null || getClass() != obj.getClass() )
+				return super.equals(obj);
+			
+			CompoundLatLon other = (CompoundLatLon)obj;
+			return from.equals(other.from) && to.equals(other.to) ||
+				   to.equals(other.from) && from.equals(other.to);
+		}
+
+		@Override
+		public int hashCode() 
+		{
+			return from.hashCode() + to.hashCode();
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/OrphanSegment.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/OrphanSegment.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/OrphanSegment.java	(revision 2453)
@@ -0,0 +1,61 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Check that every segment is in a way
+ * 
+ * @author frsantos
+ */
+public class OrphanSegment extends Test 
+{
+	/** Bag of all nodes */
+	Set<Segment> segments;
+	
+	/**
+	 * Constructor
+	 */
+	public OrphanSegment() 
+	{
+		super(tr("Orphaned segments."),
+		      tr("This test checks that every segment is in a way."));
+	}
+
+
+	@Override
+	public void startTest() 
+	{
+		segments = new HashSet<Segment>(1000);
+	}
+
+	@Override
+	public void endTest() 
+	{
+		for(Segment segment : segments )
+		{
+			errors.add( new TestError(Severity.OTHER, tr("Segments not in a way"), segment) );
+		}
+		segments = null;
+	}
+
+	@Override
+	public void visit(Segment s) 
+	{
+		segments.add(s);
+	}
+	
+	@Override
+	public void visit(Way w) 
+	{
+		segments.removeAll(w.segments);
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SingleNodeSegment.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SingleNodeSegment.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SingleNodeSegment.java	(revision 2453)
@@ -0,0 +1,36 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Checks that from/to nodes in a segment are different
+ * 
+ * @author frsantos
+ */
+public class SingleNodeSegment extends Test 
+{
+	/** Tags allowed in a segment */
+	public static String[] allowedTags = new String[] { "created_by" };
+	/**
+	 * Constructor
+	 */
+	public SingleNodeSegment() 
+	{
+		super(tr("Single node segments."),
+			  tr("This test checks that there are no segments with the same node as start and destination."));
+	}
+
+	@Override
+	public void visit(Segment s) 
+	{
+		if( !s.incomplete && s.from.equals(s.to) )
+		{
+			errors.add( new TestError(Severity.ERROR, tr("Single node segments"), s) );
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/SpellCheck.java	(revision 2453)
@@ -0,0 +1,281 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.Map.Entry;
+
+import javax.swing.JCheckBox;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.*;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset;
+import org.openstreetmap.josm.gui.annotation.AnnotationPreset.*;
+import org.openstreetmap.josm.gui.preferences.AnnotationPresetPreference;
+import org.openstreetmap.josm.plugins.validator.*;
+import org.openstreetmap.josm.plugins.validator.util.Bag;
+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 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;
+	
+	/** 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;
+
+	/**
+	 * Constructor
+	 */
+	public SpellCheck() 
+	{
+		super(tr("Spell checker."),
+			  tr("This plugin checks misspelled property keys and values."));
+	}
+
+	public static void initialize(OSMValidatorPlugin plugin) throws Exception
+	{
+		initializeSpellCheck(plugin);
+		initializePresets(plugin);
+	}
+
+	/**
+	 * 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.
+	 * 
+	 * @param plugin The validator plugin 
+	 * @throws FileNotFoundException 
+	 * @throws IOException 
+	 */
+	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 );
+	}
+	
+	/**
+	 * Reads the presets data.
+	 * 
+	 * @param plugin The validator plugin
+	 * @throws Exception
+	 */
+	public static void initializePresets(@SuppressWarnings("unused") OSMValidatorPlugin plugin) throws Exception
+	{
+		if( Main.pref.getBoolean(PREF_CHECK_VALUES) )
+			return;
+		
+		Collection<AnnotationPreset> presets = AnnotationPresetPreference.annotationPresets;
+		if( presets == null || presets.isEmpty() )
+		{
+			// Skip re-reading presets if there are none available
+			return;
+		}
+		
+		spellCheckValueData = new Bag<String, String>();
+		readPresetFromPreferences();
+		
+		// TODO: allow per user word definitions
+	}
+	
+	
+	@Override
+	public void visit(Node n)
+	{
+		checkPrimitive(n);
+	}
+
+
+	@Override
+	public void visit(Segment s) 
+	{
+		checkPrimitive(s);
+	}
+
+
+	@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( (value==null || value.trim().length() == 0) && !withErrors.contains(p, "EV"))
+			{
+				errors.add( new TestError(Severity.WARNING, tr("Tags with empty value"), p) );
+				withErrors.add(p, "EV");
+			}
+			if( spellCheckKeyData.containsKey(key) && !withErrors.contains(p, "IPK"))
+			{
+				errors.add( new TestError(Severity.WARNING, tr("Invalid property key"), p) );
+				withErrors.add(p, "IPK");
+			}
+			if( checkValues && value != null && value.length() > 0 )
+			{
+				List<String> values = spellCheckValueData.get(key);
+				if( values != null && !values.contains(prop.getValue()) && !withErrors.contains(p, "UPV"))
+				{
+					errors.add( new TestError(Severity.OTHER, tr("Unknown property value"), p) );
+					withErrors.add(p, "UPV");
+				}
+			}
+		}
+	}
+	
+	/**
+	 * 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", AnnotationPreset.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 annotations presets
+	 */
+	public static void readPresetFromPreferences() 
+	{
+		String allAnnotations = Main.pref.get("annotation.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) 
+			{
+				e.printStackTrace();
+				JOptionPane.showMessageDialog(Main.parent, tr("Could not read annotation preset source: {0}",source));
+			} 
+			catch (SAXException e) 
+			{
+				e.printStackTrace();
+				JOptionPane.showMessageDialog(Main.parent, tr("Error parsing {0}: ", source)+e.getMessage());
+			}
+		}
+	}
+
+	@Override
+	public void startTest() 
+	{
+		checkValues = Main.pref.getBoolean("tests." + getClass().getSimpleName() + ".checkValues");
+	}
+
+	@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));
+	}
+
+	@Override
+	public void ok() 
+	{
+		Main.pref.put(PREF_CHECK_VALUES, prefCheckValues.isSelected());
+	}
+}
+	
+	
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TaggedSegment.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TaggedSegment.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/TaggedSegment.java	(revision 2453)
@@ -0,0 +1,47 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Map;
+
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Check if a segment has tags
+ * 
+ * @author frsantos
+ */
+public class TaggedSegment extends Test 
+{
+	/** Tags allowed in a segment */
+	public static String[] allowedTags = new String[] { "created_by", "converted_by", "source" };
+	
+	/**
+	 * Constructor
+	 */
+	public TaggedSegment() 
+	{
+		super(tr("Tagged segments"),
+			  tr("This test checks that no segment segment is tagged. Only ways should be tagged."));
+	}
+
+	@Override
+	public void visit(Segment s) 
+	{
+		Map<String, String> tags = s.keys;
+		if( tags == null )
+			return;
+		
+		int numAllowedTags = 0;
+		for( String tag : allowedTags)
+			if( tags.containsKey(tag) ) numAllowedTags++;
+		
+		if( tags.size() - numAllowedTags > 0 )
+		{
+			errors.add( new TestError(Severity.WARNING, tr("Segments with tags"), s) );
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnorderedWay.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnorderedWay.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UnorderedWay.java	(revision 2453)
@@ -0,0 +1,41 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+
+/**
+ * Check for unordered ways
+ * 
+ * @author frsantos
+ */
+public class UnorderedWay extends Test 
+{
+	/**
+	 * Constructor
+	 */
+	public UnorderedWay() 
+	{
+		super(tr("Unordered ways."),
+			  tr("This test checks that all segments in a way are properly ordered."));
+	}
+
+	@Override
+	public void visit(Way w) 
+	{
+		Segment last = null;
+		for(Segment s: w.segments)
+		{
+			if( last != null && !last.incomplete && !s.incomplete && !last.to.equals(s.from) )
+			{
+				errors.add( new TestError(Severity.WARNING, tr("Unordered ways"), w) );
+				break;
+			}
+			last = s;
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/tests/UntaggedNode.java	(revision 2453)
@@ -0,0 +1,76 @@
+package org.openstreetmap.josm.plugins.validator.tests;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Segment;
+import org.openstreetmap.josm.plugins.validator.Severity;
+import org.openstreetmap.josm.plugins.validator.Test;
+import org.openstreetmap.josm.plugins.validator.TestError;
+/**
+ * Checks for untagged nodes that are in no segment
+ * 
+ * @author frsantos
+ */
+public class UntaggedNode extends Test 
+{
+	/** Tags allowed in a segment */
+	public static String[] allowedTags = new String[] { "created_by" };
+	
+	/** Bag of all nodes */
+	Set<Node> emptyNodes;
+
+	/**
+	 * Constructor
+	 */
+	public UntaggedNode() 
+	{
+		super(tr("Untagged nodes."),
+			  tr("This test checks for untagged nodes that are not part of any segment."));
+	}
+
+	@Override
+	public void startTest() 
+	{
+		emptyNodes = new HashSet<Node>(100);
+	}
+	
+	@Override
+	public void visit(Node n) 
+	{
+		int numTags = 0;
+		Map<String, String> tags = n.keys;
+		if( tags != null )
+		{
+			numTags = tags.size();
+			for( String tag : allowedTags)
+				if( tags.containsKey(tag) ) numTags--;
+		}
+		
+		if( numTags == 0 )
+		{
+			emptyNodes.add(n);
+		}
+	}
+	
+	@Override
+	public void visit(Segment s) 
+	{
+		emptyNodes.remove(s.from);
+		emptyNodes.remove(s.to);
+	}
+	
+	@Override
+	public void endTest() 
+	{
+		for(Node node : emptyNodes)
+		{
+			errors.add( new TestError(Severity.OTHER, tr("Untagged and unconnected nodes"), node) );
+		}
+		emptyNodes = null;
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/AgregatePrimitivesVisitor.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/AgregatePrimitivesVisitor.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/AgregatePrimitivesVisitor.java	(revision 2453)
@@ -0,0 +1,90 @@
+package org.openstreetmap.josm.plugins.validator.util;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+import org.openstreetmap.josm.data.osm.*;
+import org.openstreetmap.josm.data.osm.visitor.Visitor;
+
+/**
+ * A visitor that aggregates all primitives it visits.
+ * <p>
+ * The primitives are sorted according to their tyep: first Nodes, then
+ * Segments, and Ways last.
+ * 
+ * @author frsantos
+ */
+public class AgregatePrimitivesVisitor implements Visitor
+{
+	/** Aggregated data */
+	Collection<OsmPrimitive> aggregatedData;
+
+	/**
+	 * Constructor
+	 */
+	public AgregatePrimitivesVisitor() 
+	{
+		aggregatedData = new TreeSet<OsmPrimitive>( new PrimitiveComparator());
+	}
+
+	/**
+	 * Visits a collection of primitives 
+	 * @param data The collection of primitives 
+	 * @return The aggregated primitives
+	 */
+	public Collection<OsmPrimitive> visit(Collection<OsmPrimitive> data) 
+	{
+		for (OsmPrimitive osm : data) 
+		{
+			osm.visit(this);
+		}
+		
+		return aggregatedData;
+	}
+
+	public void visit(Node n) 
+	{
+		aggregatedData.add(n);
+	}
+
+	public void visit(Segment s) 
+	{
+		aggregatedData.add(s);
+		if( s.from != null ) visit(s.from);
+		if( s.to != null )   visit(s.to);
+	}
+
+	public void visit(Way w) 
+	{
+		aggregatedData.add(w);
+		for(Segment s : w.segments)
+			visit(s);
+	}
+
+	/**
+	 * A comparator that orders Nodes first, then Segments and Ways last.
+	 * 
+	 * @author frsantos
+	 */
+	class PrimitiveComparator implements Comparator<OsmPrimitive>
+	{
+		public int compare(OsmPrimitive o1, OsmPrimitive o2) 
+		{
+			if( o1 instanceof Node)
+			{
+				return o2 instanceof Node ? o1.compareTo(o2) : -1;
+			}
+			else if( o1 instanceof Way)
+			{
+				return o2 instanceof Way ? o1.compareTo(o2) : 1;
+			}
+			else // o1 is a segment
+			{
+				if( o2 instanceof Node ) return 1;
+				if( o2 instanceof Way ) return -1;
+				return o1.compareTo(o2);
+			}
+		}
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Bag.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Bag.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Bag.java	(revision 2453)
@@ -0,0 +1,78 @@
+package org.openstreetmap.josm.plugins.validator.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 
+ * A very simple bag to store multiple occurences of a same key.
+ * <p>
+ * The bag will keep, for each key, a list of values.
+ * 
+ * @author frsantos
+ *
+ * @param <K> The key class
+ * @param <V> The value class
+ */
+public class Bag<K,V> extends HashMap<K, List<V>> 
+{
+    /** Serializable ID */
+    private static final long serialVersionUID = 5374049172859211610L;
+
+    /**
+	 * Returns the list of elements with the same key
+	 * @param key The key to obtain the elements
+	 * @return the list of elements with the same key
+	 */
+	public List<V> get(K key) 
+	{
+		return super.get(key);
+	}
+
+	/**
+	 * Adds an element to the bag
+	 * @param key The key of the element
+	 * @param value The element to add
+	 */
+	public void add(K key, V value) 
+	{
+		List<V> values = get(key);
+		if( values == null )
+		{
+			values = new ArrayList<V>();
+			put(key, values);
+		}
+		values.add(value);
+	}
+
+	/**
+	 * Constructor
+	 */
+	public Bag() 
+	{
+		super();
+	}
+
+	/**
+	 * Constructor
+	 * 
+	 * @param initialCapacity The initial capacity
+	 */
+	public Bag(int initialCapacity) 
+	{
+		super(initialCapacity);
+	}
+
+	/**
+	 * Returns true if the bag contains a value for a key
+	 * @param key The key
+	 * @param value The value
+	 * @return true if the key contains the value
+	 */
+	public boolean contains(K key, V value)
+	{
+		List<V> values = get(key);
+		return (values == null) ? false : values.contains(value);
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/MultipleNameVisitor.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/MultipleNameVisitor.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/MultipleNameVisitor.java	(revision 2453)
@@ -0,0 +1,76 @@
+package org.openstreetmap.josm.plugins.validator.util;
+
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.util.Collection;
+
+import javax.swing.Icon;
+import javax.swing.JLabel;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Able to create a name and an icon for a collection of elements.
+ * 
+ * @author frsantos
+ */
+public class MultipleNameVisitor extends NameVisitor 
+{
+	/** The class name of the combined primitives */
+	String multipleClassname;
+	/** Size of the collection */
+	int size;
+	
+	/**
+	 * Visits a collection of primitives 
+	 * @param data The collection of primitives 
+	 */
+	public void visit(Collection<? extends OsmPrimitive> data) 
+	{
+		size = data.size();
+		multipleClassname = null;
+		for (OsmPrimitive osm : data) 
+		{
+			osm.visit(this);
+			if (multipleClassname == null)
+				multipleClassname = className;
+			else if (!multipleClassname.equals(className))
+				multipleClassname = "object";
+		}
+	}
+
+	@Override
+	public JLabel toLabel() 
+	{
+		if( size == 1 )
+			return super.toLabel();
+		else
+			return new JLabel( size + " " + trn(multipleClassname, multipleClassname + "s", size), ImageProvider.get("data", multipleClassname), JLabel.HORIZONTAL);
+	}
+	
+	/**
+	 * Gets the name of the items
+	 * @return the name of the items
+	 */
+	public String getText()
+	{
+		if( size == 1 )
+			return name;
+		else
+			return size + " " + trn(multipleClassname, multipleClassname + "s", size);
+	}
+
+	/**
+	 * Gets the icon of the items
+	 * @return the icon of the items
+	 */
+	public Icon getIcon()
+	{
+		if( size == 1 )
+			return icon;
+		else
+			return ImageProvider.get("data", multipleClassname);
+	}
+}
Index: utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Util.java
===================================================================
--- utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Util.java	(revision 2453)
+++ utils/josm/plugins/validator/src/org/openstreetmap/josm/plugins/validator/util/Util.java	(revision 2453)
@@ -0,0 +1,166 @@
+package org.openstreetmap.josm.plugins.validator.util;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.JButton;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.plugins.Plugin;
+import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Utility class
+ * 
+ * @author frsantos
+ */
+public class Util 
+{
+
+    /**
+     * Utility method to retrieve the plugin for classes that can't access to the plugin object directly.
+     * 
+     * @param clazz The plugin class
+     * @return The YWMS plugin
+     */
+    public static Plugin getPlugin(Class<? extends Plugin> clazz)
+    {
+    	String classname = clazz.getName();
+        for (PluginProxy plugin : Main.plugins)
+        {
+            if( plugin.info.className.equals(classname) )
+            {
+                return (Plugin)plugin.plugin;
+            }
+        }
+        
+        return null;
+    }
+    
+	/** 
+	 * Returns the plugin's directory of a plugin
+	 * <p>
+	 * Utility method for classes that can't acces the plugin object
+	 * 
+	 * @param clazz The plugin class to look for
+	 * @return The directory of the plugin
+	 */
+	public static String getStaticPluginDir(Class<? extends Plugin> clazz)
+	{
+	    Plugin plugin = getPlugin(clazz);
+	    return ( plugin != null ) ? plugin.getPluginDir() : null;
+	}
+
+	/**
+	 * Utility method for creating buttons
+	 * @param name The name of the button
+	 * @param icon Icon of the button
+	 * @param tooltip Tooltip
+	 * @param action The action performed when clicking the button
+	 * @return The created button
+	 */
+    public static JButton createButton(String name, String icon, String tooltip, ActionListener action) 
+    {
+		JButton button = new JButton(tr(name), ImageProvider.get(icon));
+		button.setActionCommand(name);
+		button.addActionListener(action);
+		button.setToolTipText(tr(tooltip));
+		button.putClientProperty("help", "Dialog/SelectionList/" + name);
+		return button;
+	}
+    
+    
+	/**
+	 * Returns the version
+	 * @return The version of the application
+	 */
+	public static Version getVersion()
+    {
+    	String revision;
+    	try 
+    	{
+			revision = loadFile(Util.class.getResource("/resources/REVISION"));
+		} 
+    	catch (Exception e) 
+    	{
+			return null;
+		}
+
+		Pattern versionPattern = Pattern.compile(".*?Revision: ([0-9]*).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
+		Matcher match = versionPattern.matcher(revision);
+		String version = match.matches() ? match.group(1) : "UNKNOWN";
+
+		Pattern timePattern = Pattern.compile(".*?Last Changed Date: ([^\n]*).*", Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
+		match = timePattern.matcher(revision);
+		String time = match.matches() ? match.group(1) : "UNKNOWN";
+		
+		return new Version(version, time);
+    }
+
+    /**
+     * Utility class for displaying versions
+     * 
+     * @author frsantos
+     */
+    public static class Version
+    {
+    	/** The revision */
+    	public String revision;
+    	/** The build time */
+    	public String time;
+    	
+        /**
+         * Constructor
+         * @param revision
+         * @param time
+         */
+        public Version(String revision, String time) 
+        {
+			this.revision = revision;
+			this.time = time;
+		}
+    }
+    
+    
+    /**
+     * Loads a text file in a String
+     * 
+     * @param resource The URL of the file
+     * @return A String with the file contents
+     * @throws IOException when error reading the file
+     */
+    public static String loadFile(URL resource) throws IOException
+    {
+    	BufferedReader in = null;
+		try 
+		{
+			in = new BufferedReader(new InputStreamReader(resource.openStream()));
+			StringBuilder sb = new StringBuilder();
+			for (String line = in.readLine(); line != null; line = in.readLine()) 
+			{
+				sb.append(line);
+				sb.append('\n');
+			}
+			return sb.toString();
+		}
+		finally
+		{
+			if( in != null )
+			{
+				try {
+					in.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+    }
+}
