Index: /src/org/openstreetmap/josm/actions/OpenGpxAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/OpenGpxAction.java	(revision 8)
+++ /src/org/openstreetmap/josm/actions/OpenGpxAction.java	(revision 9)
@@ -13,9 +13,10 @@
 import javax.swing.filechooser.FileFilter;
 
-import org.jdom.JDOMException;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.Main;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.io.GpxReader;
+import org.openstreetmap.josm.io.DataReader.ConnectionException;
+import org.openstreetmap.josm.io.DataReader.ParseException;
 
 /**
@@ -53,13 +54,16 @@
 		
 		try {
-			DataSet dataSet = new GpxReader().parse(new FileReader(gpxFile));
+			DataSet dataSet = new GpxReader(new FileReader(gpxFile)).parse();
 			MapFrame map = new MapFrame(dataSet);
 			Main.main.setMapFrame(gpxFile.getName(), map);
-		} catch (JDOMException x) {
+		} catch (ParseException x) {
 			x.printStackTrace();
-			JOptionPane.showMessageDialog(Main.main, "Illegal GPX document:\n"+x.getMessage());
+			JOptionPane.showMessageDialog(Main.main, x.getMessage());
 		} catch (IOException x) {
 			x.printStackTrace();
-			JOptionPane.showMessageDialog(Main.main, "Could not read '"+gpxFile.getName()+"':\n"+x.getMessage());
+			JOptionPane.showMessageDialog(Main.main, "Could not read '"+gpxFile.getName()+"'\n"+x.getMessage());
+		} catch (ConnectionException x) {
+			x.printStackTrace();
+			JOptionPane.showMessageDialog(Main.main, x.getMessage());
 		}
 	}
Index: /src/org/openstreetmap/josm/data/GeoPoint.java
===================================================================
--- /src/org/openstreetmap/josm/data/GeoPoint.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/GeoPoint.java	(revision 9)
@@ -48,24 +48,4 @@
 
 	/**
-	 * GeoPoints are equal, if their lat/lon are equal or, if lat or lon are NaN, 
-	 * if their x/y are equal.
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof GeoPoint))
-			return false;
-		GeoPoint gp = (GeoPoint)obj;
-		
-		if (Double.isNaN(lat) || Double.isNaN(lon))
-			return x == gp.x && y == gp.y;
-		return lat == gp.lat && lon == gp.lon;
-	}
-
-	@Override
-	public int hashCode() {
-		return super.hashCode();
-	}
-
-	/**
 	 * Return the squared distance of the northing/easting values between 
 	 * this and the argument.
Index: /src/org/openstreetmap/josm/data/osm/Key.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/Key.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/osm/Key.java	(revision 9)
@@ -57,19 +57,4 @@
 	}
 
-	/**
-	 * Keys are equal, when their name is equal, regardless of their other keys.
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof Key))
-			return false;
-		return name.equals(((Key)obj).name);
-	}
-
-	@Override
-	public int hashCode() {
-		return name.hashCode();
-	}
-
 	@Override
 	public void visit(Visitor visitor) {
Index: /src/org/openstreetmap/josm/data/osm/LineSegment.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/LineSegment.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/osm/LineSegment.java	(revision 9)
@@ -85,22 +85,4 @@
 	}
 
-	/**
-	 * Line segments are equal, if their starting and ending node and their
-	 * keys are equal.
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof LineSegment))
-			return false;
-		return super.equals(obj) && 
-			getStart().equals(((LineSegment)obj).getStart()) &&
-			getEnd().equals(((LineSegment)obj).getEnd());
-	}
-
-	@Override
-	public int hashCode() {
-		return super.hashCode() + getStart().hashCode() + getEnd().hashCode();
-	}
-
 	@Override
 	public void visit(Visitor visitor) {
Index: /src/org/openstreetmap/josm/data/osm/Node.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/Node.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/osm/Node.java	(revision 9)
@@ -35,25 +35,4 @@
 	
 	/**
-	 * Nodes are equal when their coordinates are equal.
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof Node))
-			return false;
-		Node n = (Node)obj;
-		if (coor == null)
-			return n.coor == null;
-		return coor.equals(n.coor) && super.equals(obj);
-	}
-
-	/**
-	 * Compute the hashcode from the OsmPrimitive's hash and the coor's hash.
-	 */
-	@Override
-	public int hashCode() {
-		return (coor == null ? 0 : coor.hashCode()) + super.hashCode();
-	}
-
-	/**
 	 * Return a list only this added.
 	 */
Index: /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 9)
@@ -68,27 +68,4 @@
 	
 	/**
-	 * Osm primitives are equal, when their keys are equal. 
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (obj == null)
-			return false;
-		if (!(obj instanceof OsmPrimitive))
-			return false;
-		OsmPrimitive osm = (OsmPrimitive)obj;
-		if (keys == null)
-			return osm.keys == null;
-		return keys.equals(osm.keys);
-	}
-
-	/**
-	 * Compute the hashCode from the keys.
-	 */
-	@Override
-	public int hashCode() {
-		return keys == null ? 0 : keys.hashCode();
-	}
-
-	/**
 	 * Mark the primitive as selected or not selected and fires a selection 
 	 * changed later, if the value actualy changed.
Index: /src/org/openstreetmap/josm/data/osm/Track.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/Track.java	(revision 8)
+++ /src/org/openstreetmap/josm/data/osm/Track.java	(revision 9)
@@ -90,31 +90,4 @@
 
 	/**
-	 * Tracks are equal, when all segments and the keys are equal
-	 */
-	@Override
-	public boolean equals(Object obj) {
-		if (!(obj instanceof Track))
-			return false;
-		if (!super.equals(obj))
-			return false;
-		Track t = (Track)obj;
-		int size = segments.size();
-		if (size != t.segments.size())
-			return false;
-		for (int i = 0; i < size; ++i)
-			if (!segments.get(i).equals(t.segments.get(i)))
-				return false;
-		return true;
-	}
-
-	@Override
-	public int hashCode() {
-		int hash = super.hashCode();
-		for (LineSegment ls : segments)
-			hash += ls.hashCode();
-		return hash;
-	}
-
-	/**
 	 * Return the last node in the track. This is the node, which no line segment
 	 * has as start, but at least one has it as end. If there are not exact one
Index: /src/org/openstreetmap/josm/gui/Main.java
===================================================================
--- /src/org/openstreetmap/josm/gui/Main.java	(revision 8)
+++ /src/org/openstreetmap/josm/gui/Main.java	(revision 9)
@@ -142,5 +142,6 @@
 		panel.removeAll();
 		panel.add(mapFrame, BorderLayout.CENTER);
-		panel.add(mapFrame.getToolBarActions(), BorderLayout.WEST);
+		panel.add(mapFrame.toolBarActions, BorderLayout.WEST);
+		panel.add(mapFrame.statusLine, BorderLayout.SOUTH);
 		panel.setVisible(true);
 	}
Index: /src/org/openstreetmap/josm/gui/MapFrame.java
===================================================================
--- /src/org/openstreetmap/josm/gui/MapFrame.java	(revision 8)
+++ /src/org/openstreetmap/josm/gui/MapFrame.java	(revision 9)
@@ -47,5 +47,9 @@
 	 * The toolbar with the action icons
 	 */
-	private JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
+	public JToolBar toolBarActions = new JToolBar(JToolBar.VERTICAL);
+	/**
+	 * The status line below the map
+	 */
+	public MapStatus statusLine;
 
 	/**
@@ -77,5 +81,5 @@
 		toolGroup.setSelected(((AbstractButton)toolBarActions.getComponent(0)).getModel(), true);
 		selectMapMode((MapMode)((AbstractButton)toolBarActions.getComponent(0)).getAction());
-		
+
 		// autoScale
 		toolBarActions.addSeparator();
@@ -93,5 +97,5 @@
 		// properties
 		toolBarActions.add(new IconToggleButton(this, new PropertiesDialog(this)));
-		
+
 		// selection dialog
 		SelectionListDialog selectionList = new SelectionListDialog(dataSet);
@@ -104,4 +108,7 @@
 		});
 		toolBarActions.add(buttonSelection);
+
+		// status line below the map
+		statusLine = new MapStatus(mapView);
 	}
 
@@ -117,10 +124,3 @@
 		mapMode.registerListener();
 	}
-
-	/**
-	 * @return Returns the toolBarActions.
-	 */
-	public JToolBar getToolBarActions() {
-		return toolBarActions;
-	}
 }
Index: /src/org/openstreetmap/josm/gui/MapStatus.java
===================================================================
--- /src/org/openstreetmap/josm/gui/MapStatus.java	(revision 9)
+++ /src/org/openstreetmap/josm/gui/MapStatus.java	(revision 9)
@@ -0,0 +1,193 @@
+package org.openstreetmap.josm.gui;
+
+import java.awt.AWTEvent;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.util.Map.Entry;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.Popup;
+import javax.swing.PopupFactory;
+
+import org.openstreetmap.josm.data.GeoPoint;
+import org.openstreetmap.josm.data.osm.Key;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.visitor.SelectionComponentVisitor;
+
+/**
+ * A component that manages some status information display about the map.
+ * It keeps a status line below the map up to date and displays some tooltip
+ * information if the user hold the mouse long enough at some point.
+ * 
+ * All this is done in background to not disturb other processes.
+ * 
+ * The background thread does not alter any data of the map (read only thread).
+ * Also it is rather fail safe. In case of some error in the data, it just do
+ * nothing instead of whining and complaining.
+ * 
+ * @author imi
+ */
+public class MapStatus extends JPanel {
+
+	/**
+	 * The MapView this status belongs. 
+	 */
+	final MapView mv;
+	/**
+	 * The position of the mouse cursor.
+	 */
+	private JTextField positionText = new JTextField("-000.00000000000000 -000.00000000000000".length());
+	/**
+	 * The field holding the name of the object under the mouse.
+	 */
+	private JTextField nameText = new JTextField(30);
+	/**
+	 * The background thread thats collecting the data.
+	 */
+	private Runnable collector;
+
+	/**
+	 * The collector class that waits for notification and then update
+	 * the display objects.
+	 * 
+	 * @author imi
+	 */
+	private final class Collector implements Runnable {
+		/**
+		 * The last object displayed in status line.
+		 */
+		OsmPrimitive osmStatus;
+		/**
+		 * A visitor to retrieve name information about the osm primitive
+		 */
+		private SelectionComponentVisitor visitor = new SelectionComponentVisitor();
+		/**
+		 * The old modifiers, that was pressed the last time this collector ran.
+		 */
+		private int oldModifiers;
+		/**
+		 * The popup displayed to show additional information
+		 */
+		private Popup popup;
+
+		/**
+		 * Execution function for the Collector.
+		 */
+		public void run() {
+			for (;;) {
+				MouseState ms = new MouseState();
+				synchronized (this) {
+					try {wait();} catch (InterruptedException e) {}
+					ms.modifiers = mouseState.modifiers;
+					ms.mousePos = mouseState.mousePos;
+				}
+				if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null)
+					continue; // freeze display when holding down ctrl
+				OsmPrimitive osm = mv.getNearest(ms.mousePos, (ms.modifiers & MouseEvent.ALT_DOWN_MASK) != 0);
+				if (osm == osmStatus && ms.modifiers == oldModifiers)
+					continue;
+				osmStatus = osm;
+				oldModifiers = ms.modifiers;
+				if (osm != null) {
+					osm.visit(visitor);
+					nameText.setText(visitor.name);
+				} else
+					nameText.setText("");
+				
+				// Popup Information
+				if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 && osm != null) {
+					if (popup != null)
+						popup.hide();
+					
+					StringBuilder text = new StringBuilder("<html>");
+					text.append(visitor.name);
+					if (osm.keys != null) {
+						for (Entry<Key, String> e : osm.keys.entrySet()) {
+							text.append("<br>");
+							text.append(e.getKey().name);
+							text.append("=");
+							text.append(e.getValue());
+						}
+					}
+					JLabel l = new JLabel(text.toString(), visitor.icon, JLabel.HORIZONTAL);
+					
+					Point p = mv.getLocationOnScreen();
+					popup = PopupFactory.getSharedInstance().getPopup(mv, l, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16);
+					popup.show();
+				} else if (popup != null) {
+					popup.hide();
+					popup = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Everything, the collector is interested of. Access must be synchronized.
+	 * @author imi
+	 */
+	class MouseState {
+		Point mousePos;
+		int modifiers;
+	}
+	/**
+	 * The last sent mouse movement event.
+	 */
+	private MouseState mouseState = new MouseState();
+	
+	/**
+	 * Construct a new MapStatus and attach it to the map view.
+	 * @param mv The MapView the status line is part of.
+	 */
+	public MapStatus(final MapView mv) {
+		this.mv = mv;
+		
+		// Listen for mouse movements and set the position text field
+		mv.addMouseMotionListener(new MouseMotionListener(){
+			public void mouseDragged(MouseEvent e) {
+				mouseMoved(e);
+			}
+			public void mouseMoved(MouseEvent e) {
+				// Do not update the view, if ctrl is pressed.
+				if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
+					GeoPoint p = mv.getPoint(e.getX(),e.getY(),true);
+					positionText.setText(p.lat+" "+p.lon);
+				}
+			}
+		});
+		
+		// Listen to keyboard/mouse events for pressing/releasing alt key and
+		// inform the collector.
+		Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
+			public void eventDispatched(AWTEvent event) {
+				synchronized (collector) {
+					mouseState.modifiers = ((InputEvent)event).getModifiersEx();
+					if (event instanceof MouseEvent)
+						mouseState.mousePos = ((MouseEvent)event).getPoint();
+					collector.notify();
+				}
+			}
+		}, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
+
+		positionText.setEditable(false);
+		nameText.setEditable(false);
+		setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+		setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+		add(new JLabel("Lat/Lon "));
+		add(positionText);
+		add(new JLabel(" Object "));
+		add(nameText);
+
+		// The background thread
+		collector = new Collector();
+		new Thread(collector).start();
+	}
+}
Index: /src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- /src/org/openstreetmap/josm/gui/MapView.java	(revision 8)
+++ /src/org/openstreetmap/josm/gui/MapView.java	(revision 9)
@@ -84,17 +84,17 @@
 	
 	/**
-	 * Construct a MapView and attach it to a frame.
+	 * Construct a MapView.
 	 */
 	public MapView(DataSet dataSet) {
 		this.dataSet = dataSet;
 		addComponentListener(this);
-		
+
 		// initialize the movement listener
 		new MapMover(this);
-		
+
 		// initialize the projection
 		setProjection(Main.pref.projection.clone());
-		
-		// initialize the engine
+
+		// initialize the drawing engine
 		engine = new SimpleEngine(this);
 	}
@@ -222,4 +222,5 @@
 	}
 
+	
 	/**
 	 * Zoom to the given coordinate.
@@ -247,4 +248,58 @@
 	
 	/**
+	 * Draw the component.
+	 */
+	@Override
+	public void paint(Graphics g) {
+		engine.init(g);
+		engine.drawBackground(getPoint(0,0,true), getPoint(getWidth(), getHeight(), true));
+
+		for (Track t : dataSet.tracks())
+			engine.drawTrack(t);
+		for (LineSegment ls : dataSet.pendingLineSegments())
+			engine.drawPendingLineSegment(ls);
+		for (Node n : dataSet.nodes)
+			engine.drawNode(n);
+	}
+
+	/**
+	 * Notify from the projection, that something has changed.
+	 * @param e
+	 */
+	public void stateChanged(ChangeEvent e) {
+		projection.init(dataSet);
+		recalculateCenterScale();
+	}
+
+	/**
+	 * Set a new projection method. This call is not cheap, as it will
+	 * transform the whole underlying dataSet and repaint itself.
+	 * 
+	 * @param projection The new projection method to set.
+	 */
+	public void setProjection(Projection projection) {
+		if (projection == this.projection)
+			return;
+
+		Projection oldProjection = this.projection;
+		
+		if (this.projection != null)
+			this.projection.removeChangeListener(this);
+		this.projection = projection;
+		projection.addChangeListener(this);
+		
+		stateChanged(new ChangeEvent(this));
+		firePropertyChange("projection", oldProjection, projection);
+	}
+
+	/**
+	 * Return the projection method for this map view.
+	 * @return The projection method.
+	 */
+	public Projection getProjection() {
+		return projection;
+	}
+
+	/**
 	 * Return the current scale value.
 	 * @return The scale value currently used in display
@@ -254,4 +309,30 @@
 	}
 
+	/**
+	 * @return Returns the autoScale.
+	 */
+	public boolean isAutoScale() {
+		return autoScale;
+	}
+
+	/**
+	 * @param autoScale The autoScale to set.
+	 */
+	public void setAutoScale(boolean autoScale) {
+		if (this.autoScale != autoScale) {
+			this.autoScale = autoScale;
+			firePropertyChange("autoScale", !autoScale, autoScale);
+		}
+	}
+	/**
+	 * @return Returns the center point. A copy is returned, so users cannot
+	 * 		change the center by accessing the return value. Use zoomTo instead.
+	 */
+	public GeoPoint getCenter() {
+		return center.clone();
+	}
+
+	
+	
 	/**
 	 * Set the new dimension to the projection class. Also adjust the components 
@@ -285,5 +366,5 @@
 				scale = Math.max(scaleX, scaleY); // minimum scale to see all of the screen
 			}
-
+	
 			firePropertyChange("center", oldCenter, center);
 			if (oldAutoScale != autoScale)
@@ -303,82 +384,4 @@
 
 	/**
-	 * Draw the component.
-	 */
-	@Override
-	public void paint(Graphics g) {
-		engine.init(g);
-		engine.drawBackground(getPoint(0,0,true), getPoint(getWidth(), getHeight(), true));
-
-		for (Track t : dataSet.tracks())
-			engine.drawTrack(t);
-		for (LineSegment ls : dataSet.pendingLineSegments())
-			engine.drawPendingLineSegment(ls);
-		for (Node n : dataSet.nodes)
-			engine.drawNode(n);
-	}
-
-	/**
-	 * Notify from the projection, that something has changed.
-	 * @param e
-	 */
-	public void stateChanged(ChangeEvent e) {
-		projection.init(dataSet);
-		recalculateCenterScale();
-	}
-
-	/**
-	 * Set a new projection method. This call is not cheap, as it will
-	 * transform the whole underlying dataSet and repaint itself.
-	 * 
-	 * @param projection The new projection method to set.
-	 */
-	public void setProjection(Projection projection) {
-		if (projection == this.projection)
-			return;
-
-		Projection oldProjection = this.projection;
-		
-		if (this.projection != null)
-			this.projection.removeChangeListener(this);
-		this.projection = projection;
-		projection.addChangeListener(this);
-		
-		stateChanged(new ChangeEvent(this));
-		firePropertyChange("projection", oldProjection, projection);
-	}
-
-	/**
-	 * Return the projection method for this map view.
-	 * @return The projection method.
-	 */
-	public Projection getProjection() {
-		return projection;
-	}
-
-	/**
-	 * @return Returns the autoScale.
-	 */
-	public boolean isAutoScale() {
-		return autoScale;
-	}
-
-	/**
-	 * @param autoScale The autoScale to set.
-	 */
-	public void setAutoScale(boolean autoScale) {
-		if (this.autoScale != autoScale) {
-			this.autoScale = autoScale;
-			firePropertyChange("autoScale", !autoScale, autoScale);
-		}
-	}
-	/**
-	 * @return Returns the center point. A copy is returned, so users cannot
-	 * 		change the center by accessing the return value. Use zoomTo instead.
-	 */
-	public GeoPoint getCenter() {
-		return center.clone();
-	}
-
-	/**
 	 * Does nothing. Just to satisfy ComponentListener.
 	 */
Index: /src/org/openstreetmap/josm/io/DataReader.java
===================================================================
--- /src/org/openstreetmap/josm/io/DataReader.java	(revision 9)
+++ /src/org/openstreetmap/josm/io/DataReader.java	(revision 9)
@@ -0,0 +1,49 @@
+package org.openstreetmap.josm.io;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+
+/**
+ * All Classes that subclass DataReader are capable of importing data from a source
+ * to a DataSet.
+ *
+ * @author imi
+ */
+public interface DataReader {
+	
+	/**
+	 * Thrown from the parse command in case of parsing problems.
+	 * @author imi
+	 */
+	public class ParseException extends Exception {
+		public ParseException(String message, Throwable cause) {
+			super(message, cause);
+		}
+		public ParseException(String message) {
+			super(message);
+		}
+	}
+	/**
+	 * Thrown from the parse command in case of other problems like connection
+	 * failed or a file could not be read.
+	 * @author imi
+	 */
+	public class ConnectionException extends Exception {
+		public ConnectionException(String message, Throwable cause) {
+			super(message, cause);
+		}
+		public ConnectionException(String message) {
+			super(message);
+		}
+	}
+
+	/**
+	 * Called to parse the source and return the dataset.
+	 * @return The dataSet parsed.
+	 * @throws DataParseException The data is ill-formated. The data could be
+	 * 		retrieved but it contain errors that could not be handled.
+	 * @throws ConnectionException A problem with the connection to the data source
+	 * 		occoured. As example, the file could not be read or the server
+	 * 		does not repsonse
+	 */
+	public DataSet parse() throws ParseException, ConnectionException; 
+}
Index: /src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- /src/org/openstreetmap/josm/io/GpxReader.java	(revision 8)
+++ /src/org/openstreetmap/josm/io/GpxReader.java	(revision 9)
@@ -3,4 +3,5 @@
 import java.io.IOException;
 import java.io.Reader;
+import java.util.HashMap;
 
 import org.jdom.Element;
@@ -10,36 +11,58 @@
 import org.openstreetmap.josm.data.GeoPoint;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Key;
 import org.openstreetmap.josm.data.osm.LineSegment;
 import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Track;
 import org.openstreetmap.josm.gui.Main;
 
 /**
- * Reads an gpx stream and construct a DataSet out of it. Some information may not be 
- * imported, since JOSM does not fully support GPX.
+ * Reads an gpx stream and construct a DataSet out of it. 
+ * Some information may not be imported, but GpxReader tries its best to load
+ * all data possible in the key/value structure.
  * 
  * @author imi
  */
-public class GpxReader {
-
-	public static final Namespace XSD = Namespace.getNamespace("http://www.w3.org/2001/XMLSchema");
-	public static final Namespace GPX = Namespace.getNamespace("http://www.topografix.com/GPX/1/0");
+public class GpxReader implements DataReader {
+
+	/**
+	 * The GPX namespace used.
+	 */
+	private static final Namespace GPX = Namespace.getNamespace("http://www.topografix.com/GPX/1/0");
+	/**
+	 * The OSM namespace used (for extensions).
+	 */
+	private static final Namespace OSM = Namespace.getNamespace("osm");
+
+	/**
+	 * The data source from this reader.
+	 */
+	public Reader source;
 	
 	/**
+	 * Construct a parser from a specific data source.
+	 * @param source The data source, as example a FileReader to read from a file.
+	 */
+	public GpxReader(Reader source) {
+		this.source = source;
+	}
+	
+	/**
 	 * Read the input stream and return a DataSet from the stream.
-	 * 
-	 * @param in
-	 * @throws IOException 		An error with the provided stream occoured.
-	 * @throws JDOMException 	An parse error occoured.
-	 */
-	public DataSet parse(Reader in) throws JDOMException, IOException {
+	 */
+	public DataSet parse() throws ParseException, ConnectionException {
 		try {
 			final SAXBuilder builder = new SAXBuilder();
-			Element root = builder.build(in).getRootElement();
+			Element root = builder.build(source).getRootElement();
 			return parseDataSet(root);
 		} catch (NullPointerException npe) {
-			throw new JDOMException("NullPointerException. Probably a tag name mismatch.", npe);
+			throw new ParseException("NullPointerException. Probably a tag name mismatch.", npe);
 		} catch (ClassCastException cce) {
-			throw new JDOMException("ClassCastException. Probably a tag does not contain the correct type.", cce);
+			throw new ParseException("ClassCastException. Probably a tag does not contain the correct type.", cce);
+		} catch (JDOMException e) {
+			throw new ParseException("The data could not be parsed. Reason: "+e.getMessage(), e);
+		} catch (IOException e) {
+			throw new ConnectionException("The data could not be retrieved. Reason: "+e.getMessage(), e);
 		}
 	}
@@ -56,5 +79,73 @@
 			Float.parseFloat(e.getAttributeValue("lat")),
 			Float.parseFloat(e.getAttributeValue("lon")));
+		for (Object o : e.getChildren()) {
+			Element child = (Element)o;
+			if (child.getName().equals("extensions"))
+				parseKeyValueExtensions(data, child);
+			else if (child.getName().equals("link"))
+				parseKeyValueLink(data, child);
+			else
+				parseKeyValueTag(data, child);
+		}
 		return data;
+	}
+
+	/**
+	 * Parse the extensions tag and add all properties found as key/value. 
+	 * <code>osm.keys</code> may be <code>null</code>, in which case it is 
+	 * created first. If <code>e</code> is <code>null</code>, nothing
+	 * happens.
+	 * 
+	 * @param osm	The primitive to store the properties.
+	 * @param e		The extensions element to read the properties from.
+	 */
+	private void parseKeyValueExtensions(OsmPrimitive osm, Element e) {
+		if (e != null) {
+			if (osm.keys == null)
+				osm.keys = new HashMap<Key, String>();
+			for (Object o : e.getChildren("property", OSM)) {
+				Element child = (Element)o;
+				Key key = Key.get(child.getAttributeValue("name"));
+				osm.keys.put(key, child.getAttributeValue("value"));
+			}
+		}
+	}
+
+	/**
+	 * If the element is not <code>null</code>, read the data from it and put
+	 * it as the key with the name of the elements name in the given object.
+	 * 
+	 * The <code>keys</code> - field of the element could be <code>null</code>,
+	 * in which case it is created first.
+	 * 
+	 * @param osm     The osm primitive to put the key into.
+	 * @param e		  The element to look for data.
+	 */
+	private void parseKeyValueTag(OsmPrimitive osm, Element e) {
+		if (e != null) {
+			if (osm.keys == null)
+				osm.keys = new HashMap<Key, String>();
+			osm.keys.put(Key.get(e.getName()), e.getValue());
+		}
+	}
+
+	/**
+	 * Parse the GPX linkType data information and store it as value in the 
+	 * primitives <i>link</i> key. <code>osm.keys</code> may be 
+	 * <code>null</code>, in which case it is created first. If 
+	 * <code>e</code> is <code>null</code>, nothing happens.
+	 * 
+	 * The format stored is: mimetype;url
+	 * Example: text/html;http://www.openstreetmap.org
+	 * @param osm	The osm primitive to store the data in.
+	 * @param e		The element in gpx:linkType - format.
+	 */
+	private void parseKeyValueLink(Node osm, Element e) {
+		if (e != null) {
+			if (osm.keys == null)
+				osm.keys = new HashMap<Key, String>();
+			String link = e.getChildText("type") + ";" + e.getChildText("text");
+			osm.keys.put(Key.get("link"), link);
+		}
 	}
 
@@ -105,5 +196,5 @@
 		if (Main.pref.mergeNodes)
 			for (Node n : data.nodes)
-				if (node.equals(n))
+				if (node.coor.lat == n.coor.lat && node.coor.lon == n.coor.lon)
 					return n;
 		data.nodes.add(node);
