Index: /.classpath
===================================================================
--- /.classpath	(revision 9)
+++ /.classpath	(revision 10)
@@ -3,5 +3,5 @@
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry sourcepath="/home/imi/download/jdom-1.0.zip" kind="lib" path="lib/jdom.jar"/>
+	<classpathentry sourcepath="/home/imi/src/jdom-1.0/src" kind="lib" path="lib/jdom.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
Index: /src/org/openstreetmap/josm/actions/SaveGpxAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/SaveGpxAction.java	(revision 9)
+++ /src/org/openstreetmap/josm/actions/SaveGpxAction.java	(revision 10)
@@ -4,10 +4,14 @@
 import java.awt.event.KeyEvent;
 import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
 
 import javax.swing.AbstractAction;
 import javax.swing.ImageIcon;
 import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.gui.Main;
+import org.openstreetmap.josm.io.GpxWriter;
 
 /**
@@ -29,5 +33,9 @@
 	
 	@SuppressWarnings("unchecked")
-	public void actionPerformed(ActionEvent e) {
+	public void actionPerformed(ActionEvent event) {
+		if (Main.main.getMapFrame() == null) {
+			JOptionPane.showMessageDialog(Main.main, "No document open so nothing to save.");
+			return;
+		}
 		JFileChooser fc = new JFileChooser("data");
 		fc.showSaveDialog(Main.main);
@@ -35,4 +43,14 @@
 		if (gpxFile == null)
 			return;
+		
+		try {
+			FileWriter fileWriter = new FileWriter(gpxFile);
+			GpxWriter out = new GpxWriter(fileWriter, Main.main.getMapFrame().mapView.dataSet);
+			out.output();
+			fileWriter.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+			JOptionPane.showMessageDialog(Main.main, "An error occoured while saving.\n"+e.getMessage());
+		}
 	}
 
Index: /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 9)
+++ /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 10)
@@ -66,5 +66,5 @@
 		return true;
 	}
-	
+
 	/**
 	 * Mark the primitive as selected or not selected and fires a selection 
Index: /src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- /src/org/openstreetmap/josm/io/GpxReader.java	(revision 9)
+++ /src/org/openstreetmap/josm/io/GpxReader.java	(revision 10)
@@ -92,4 +92,76 @@
 
 	/**
+	 * Read a data set from the element.
+	 * @param e 	The element to parse
+	 * @return		The DataSet read from the element
+	 */
+	private DataSet parseDataSet(Element e) {
+		DataSet data = new DataSet();
+		// read waypoints not contained in tracks or areas
+		for (Object o : e.getChildren("wpt", GPX))
+			addNode(data, parseWaypoint((Element)o));
+	
+		// read tracks
+		for (Object trackElement : e.getChildren("trk", GPX))
+			parseTrack((Element)trackElement, data);
+	
+		return data;
+	}
+
+	/**
+	 * Parse and read a track from the element. Store it in the dataSet, as well
+	 * as all nodes in it.
+	 *
+	 * @param e		The element that contain the track.
+	 * @param ds	The DataSet to store the data in.
+	 */
+	private void parseTrack(Element e, DataSet ds) {
+		Track track = new Track();
+		for (Object o : e.getChildren()) {
+			Element child = (Element)o;
+			if (child.getName().equals("extensions"))
+				parseKeyValueExtensions(track, child);
+			else if (child.getName().equals("link"))
+				parseKeyValueLink(track, child);
+			else if (child.getName().equals("trkseg")) {
+				Node start = null;
+				for (Object w : child.getChildren("trkpt", GPX)) {
+					Node node = parseWaypoint((Element)w);
+					node = addNode(ds, node);
+					if (start == null)
+						start = node;
+					else {
+						LineSegment lineSegment = new LineSegment(start, node);
+						parseKeyValueExtensions(lineSegment, ((Element)w).getChild("extensions", GPX));
+						track.add(lineSegment);
+						start = null;
+					}
+				}
+			} else
+				parseKeyValueTag(track, child);
+		}
+		ds.addTrack(track);
+	}
+	
+
+	/**
+	 * Adds the node to allNodes if it is not already listed. Does respect the
+	 * preference setting "mergeNodes". Return the node in the list that correspond
+	 * to the node in the list (either the new added or the old found).
+	 * 
+	 * @param data The DataSet to add the node to.
+	 * @param node The node that should be added.
+	 * @return Either the parameter node or the old node found in the dataset. 
+	 */
+	private Node addNode (DataSet data, Node node) {
+		if (Main.pref.mergeNodes)
+			for (Node n : data.nodes)
+				if (node.coor.lat == n.coor.lat && node.coor.lon == n.coor.lon)
+					return n;
+		data.nodes.add(node);
+		return node;
+	}
+
+	/**
 	 * 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 
@@ -138,8 +210,9 @@
 	 * 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) {
+	private void parseKeyValueLink(OsmPrimitive osm, Element e) {
 		if (e != null) {
 			if (osm.keys == null)
@@ -149,55 +222,3 @@
 		}
 	}
-
-	/**
-	 * Read a data set from the element.
-	 * @param e 	The element to parse
-	 * @return		The DataSet read from the element
-	 */
-	private DataSet parseDataSet(Element e) {
-		DataSet data = new DataSet();
-		// read waypoints not contained in tracks or areas
-		for (Object o : e.getChildren("wpt", GPX))
-			addNode(data, parseWaypoint((Element)o));
-
-		// read tracks
-		for (Object trackElement : e.getChildren("trk", GPX)) {
-			Track track = new Track();
-			for (Object trackSegmentElement : ((Element)trackElement).getChildren("trkseg", GPX)) {
-				Node start = null;
-				for (Object w : ((Element)trackSegmentElement).getChildren("trkpt", GPX)) {
-					Node node = parseWaypoint((Element)w);
-					node = addNode(data, node);
-					if (start == null)
-						start = node;
-					else {
-						LineSegment lineSegment = new LineSegment(start, node);
-						track.add(lineSegment);
-						start = null;
-					}
-				}
-			}
-			data.addTrack(track);
-		}
-
-		return data;
-	}
-	
-	/**
-	 * Adds the node to allNodes if it is not already listed. Does respect the
-	 * preference setting "mergeNodes". Return the node in the list that correspond
-	 * to the node in the list (either the new added or the old found).
-	 * 
-	 * @param data The DataSet to add the node to.
-	 * @param node The node that should be added.
-	 * @return Either the parameter node or the old node found in the dataset. 
-	 */
-	private Node addNode (DataSet data, Node node) {
-		if (Main.pref.mergeNodes)
-			for (Node n : data.nodes)
-				if (node.coor.lat == n.coor.lat && node.coor.lon == n.coor.lon)
-					return n;
-		data.nodes.add(node);
-		return node;
-	}
 }
Index: /src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/GpxWriter.java	(revision 10)
+++ /src/org/openstreetmap/josm/io/GpxWriter.java	(revision 10)
@@ -0,0 +1,231 @@
+package org.openstreetmap.josm.io;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Map.Entry;
+
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.Namespace;
+import org.jdom.output.Format;
+import org.jdom.output.XMLOutputter;
+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.Track;
+
+/**
+ * Exports a dataset to GPX data. All information available are tried to store in
+ * the gpx. If no corresponding tag is available in GPX, use 
+ * <code>&lt;extensions&gt;</code> instead.
+ * 
+ * GPX-Track segments are stored as 2-node-pairs, so no &lt;trkseg&gt; with more
+ * or less than 2 &lt;trkpt&gt; are exported.
+ * 
+ * @author imi
+ */
+public class GpxWriter {
+
+	/**
+	 * The GPX namespace used.
+	 * TODO unify with GpxReader
+	 */
+	private static final Namespace GPX = Namespace.getNamespace("http://www.topografix.com/GPX/1/0");
+	/**
+	 * The OSM namespace used (for extensions).
+	 * TODO unify with GpxReader
+	 */
+	private static final Namespace OSM = Namespace.getNamespace("osm", "http://www.openstreetmap.org");
+
+	/**
+	 * This is the output writer to store the resulting data in.
+	 */
+	private Writer out;
+	/**
+	 * The DataSet this outputter operates on.
+	 */
+	private final DataSet ds;
+	
+	/**
+	 * Create a GpxWrite from an output writer. As example to write in a file,
+	 * use FileWriter.
+	 *
+	 * @param out The Writer to store the result data in.
+	 * @param ds The dataset to store.
+	 */
+	public GpxWriter(Writer out, DataSet ds) {
+		this.ds = ds;
+		this.out = out;
+	}
+
+
+	/**
+	 * Do the output in the former set writer.
+	 * @exception IOException In case of IO errors, throw this exception.
+	 */
+	public void output() throws IOException {
+		Element root = parseDataSet();
+		root.addNamespaceDeclaration(OSM);
+		Document d = new Document(root);
+		XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
+		xmlOut.output(d, out);
+	}
+
+
+	/**
+	 * Write the whole DataSet in an JDom-Element and return the new element.
+	 * @return The created element, out of the dataset.
+	 */
+	@SuppressWarnings("unchecked")
+	private Element parseDataSet() {
+		Element e = new Element("gpx", GPX);
+		e.setAttribute("version", "1.0");
+		e.setAttribute("creator", "JOSM Beta");
+		// for getting all unreferenced waypoints in the wpt-list
+		LinkedList<Node> nodes = new LinkedList<Node>(ds.nodes);
+
+		// tracks
+		for (Track t : ds.tracks()) {
+			Element tElem = new Element("trk", GPX);
+			if (t.keys != null) {
+				HashMap<Key, String> keys = new HashMap<Key, String>(t.keys);
+				addAndRemovePropertyTag("name", tElem, keys);
+				addAndRemovePropertyTag("cmt", tElem, keys);
+				addAndRemovePropertyTag("desc", tElem, keys);
+				addAndRemovePropertyTag("src", tElem, keys);
+				addAndRemovePropertyLinkTag(tElem, keys);
+				addAndRemovePropertyTag("number", tElem, keys);
+				addAndRemovePropertyTag("type", tElem, keys);
+				addPropertyExtensions(tElem, keys);
+			}
+			// line segments
+			for (LineSegment ls : t.segments()) {
+				Element lsElem = new Element("trkseg", GPX);
+				if (ls.keys != null)
+				addPropertyExtensions(lsElem, ls.keys);
+				lsElem.getChildren().add(parseWaypoint(ls.getStart(), "trkpt"));
+				lsElem.getChildren().add(parseWaypoint(ls.getEnd(), "trkpt"));
+				nodes.remove(ls.getStart());
+				nodes.remove(ls.getEnd());
+				tElem.getChildren().add(lsElem);
+			}
+			e.getChildren().add(tElem);
+		}
+		
+		// waypoints (missing nodes)
+		for (Node n : nodes)
+			e.getChildren().add(parseWaypoint(n, "wpt"));
+
+		return e;
+	}
+
+	/**
+	 * Parse a waypoint (node) and store it into an JDOM-Element. Return that
+	 * element.
+	 * 
+	 * @param n The Node to parse and store
+	 * @param name The name of the tag (different names for nodes in GPX)
+	 * @return The resulting GPX-Element
+	 */
+	private Element parseWaypoint(Node n, String name) {
+		Element e = new Element(name, GPX);
+		e.setAttribute("lat", Double.toString(n.coor.lat));
+		e.setAttribute("lon", Double.toString(n.coor.lon));
+		if (n.keys != null) {
+			HashMap<Key, String> keys = new HashMap<Key, String>(n.keys);
+			addAndRemovePropertyTag("ele", e, keys);
+			addAndRemovePropertyTag("time", e, keys);
+			addAndRemovePropertyTag("magvar", e, keys);
+			addAndRemovePropertyTag("geoidheight", e, keys);
+			addAndRemovePropertyTag("name", e, keys);
+			addAndRemovePropertyTag("cmt", e, keys);
+			addAndRemovePropertyTag("desc", e, keys);
+			addAndRemovePropertyTag("src", e, keys);
+			addAndRemovePropertyLinkTag(e, keys);
+			addAndRemovePropertyTag("sym", e, keys);
+			addAndRemovePropertyTag("type", e, keys);
+			addAndRemovePropertyTag("fix", e, keys);
+			addAndRemovePropertyTag("sat", e, keys);
+			addAndRemovePropertyTag("hdop", e, keys);
+			addAndRemovePropertyTag("vdop", e, keys);
+			addAndRemovePropertyTag("pdop", e, keys);
+			addAndRemovePropertyTag("ageofdgpsdata", e, keys);
+			addAndRemovePropertyTag("dgpsid", e, keys);
+			addPropertyExtensions(e, keys);
+		}
+		return e;
+	}
+
+
+	/**
+	 * Add a link-tag to the element, if the property list contain a value named 
+	 * "link". The property is removed from the map afterwards.
+	 * 
+	 * For the format, @see GpxReader#parseKeyValueLink(OsmPrimitive, Element).
+	 * @param e		The element to add the link to.
+	 * @param keys	The map containing the link property.
+	 */
+	@SuppressWarnings("unchecked")
+	private void addAndRemovePropertyLinkTag(Element e, Map<Key, String> keys) {
+		Key key = Key.get("link");
+		String value = keys.get(key);
+		if (value != null) {
+			StringTokenizer st = new StringTokenizer(value, ";");
+			if (st.countTokens() != 2)
+				return;
+			Element link = new Element("link", GPX);
+			link.getChildren().add(new Element("type", GPX).setText(st.nextToken()));
+			link.getChildren().add(0,new Element("text", GPX).setText(st.nextToken()));
+			e.getChildren().add(link);
+			keys.remove(key);
+		}
+	}
+
+
+	/**
+	 * Helper to add a property with a given name as tag to the element. This
+	 * will look like &lt;name&gt;<i>keys.get(name)</i>&lt;/name&gt; 
+	 * 
+	 * After adding, the property is removed from the map.
+	 * 
+	 * If the property does not exist, nothing is done.
+	 * 
+	 * @param name The properties name
+	 * @param e The element to add the tag to.
+	 * @param osm The data to get the property from.
+	 */
+	@SuppressWarnings("unchecked")
+	private void addAndRemovePropertyTag(String name, Element e, Map<Key, String> keys) {
+		Key key = Key.get(name);
+		String value = keys.get(key);
+		if (value != null) {
+			e.getChildren().add(new Element(name, GPX).setText(value));
+			keys.remove(key);
+		}
+	}
+	
+	/**
+	 * Add the property in the entry as &lt;extensions&gt; to the element 
+	 * @param e		The element to add the property to.
+	 * @param prop	The property to add.
+	 */
+	@SuppressWarnings("unchecked")
+	private void addPropertyExtensions(Element e, Map<Key, String> keys) {
+		if (keys.isEmpty())
+			return;
+		Element extensions = e.getChild("extensions");
+		if (extensions == null)
+			e.getChildren().add(extensions = new Element("extensions", GPX));
+		for (Entry<Key, String> prop : keys.entrySet()) {
+			Element propElement = new Element("property", OSM);
+			propElement.setAttribute("key", prop.getKey().name);
+			propElement.setAttribute("value", prop.getValue());
+			extensions.getChildren().add(propElement);
+		}
+	}
+}
