Index: /src/org/openstreetmap/josm/actions/ExternalToolsAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/ExternalToolsAction.java	(revision 142)
+++ /src/org/openstreetmap/josm/actions/ExternalToolsAction.java	(revision 143)
@@ -7,5 +7,5 @@
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.Collection;
@@ -33,6 +33,11 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.AddVisitor;
+import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
 import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer;
+import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
+import org.openstreetmap.josm.io.GpxWriter;
 import org.openstreetmap.josm.io.OsmReader;
 import org.openstreetmap.josm.io.OsmWriter;
@@ -55,157 +60,209 @@
 
 	private final class ExecuteToolRunner extends PleaseWaitRunnable {
-    	private final Process p;
-    	private DataSet dataSet;
-    	private DataSet fromDataSet;
-    	private ExecuteToolRunner(String msg, Process p) {
-    		super(msg);
-    		this.p = p;
-    		currentAction.setText(tr("Executing {0}",getValue(NAME)));
-    	}
-    
-    	@Override protected void realRun() throws SAXException, IOException {
-    		if (!input.isEmpty()) {
-    			fromDataSet = new DataSet();
-    			AddVisitor adder = new AddVisitor(fromDataSet, flags.contains("include_references"));
-    			if (input.contains("selection")) {
-    				Collection<OsmPrimitive> sel = Main.ds.getSelected();
-    				for (OsmPrimitive osm : sel)
-    					osm.visit(adder);
-    				if (flags.contains("include_back_references")) {
-    					CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
-    					for (OsmPrimitive osm : sel)
-    						osm.visit(v);
-    					AddVisitor a = new AddVisitor(fromDataSet);
-    					for (OsmPrimitive osm : v.data)
-    						osm.visit(a);
-    				}
-    			}
-    			if (input.contains("all"))
-    				for (OsmPrimitive osm : Main.ds.allPrimitives())
-    					osm.visit(adder);
-    			if (input.contains("screen")) {
-    				if (Main.map == null) {
-    					errorMessage = tr("The Tool requires some data to be loaded.");
-    					cancel();
-    					return;
-    				}
-    				LatLon bottomLeft = Main.map.mapView.getLatLon(0,Main.map.mapView.getHeight());
-    				LatLon topRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(), 0);
-    				Bounds b = new Bounds(bottomLeft, topRight);
-    				Collection<Node> nodes = new HashSet<Node>();
-    				for (Node n : Main.ds.nodes) {
-    					if (n.coor.isWithin(b)) {
-    						n.visit(adder);
-    						nodes.add(n);
-    					}
-    				}
-    				Collection<Segment> segments = new HashSet<Segment>();
-    				for (Segment s : Main.ds.segments) {
-    					if (nodes.contains(s.from) || nodes.contains(s.to)) {
-    						s.visit(adder);
-    						segments.add(s);
-    					}
-    				}
-    				for (Way w : Main.ds.ways) {
-    					for (Segment s : w.segments) {
-    						if (segments.contains(s)) {
-    							w.visit(adder);
-    							break;
-    						}
-    					}
-    				}
-    			}
-    			OutputStream out = p.getOutputStream();
-    			OsmWriter.output(out, fromDataSet, false);
-    			out.close();
-    		}
-    		if (output != null)
-    			dataSet = OsmReader.parseDataSet(p.getInputStream(), currentAction, progress);
-    	}
-    
-    	@Override protected void cancel() {
-    		p.destroy();
-    	}
-    
-    	@Override protected void finish() {
-    		if (dataSet == null || output == null || output.equals("discard"))
-    			return; // user cancelled or no stdout to process
-    		Collection<OsmPrimitive> allNew = dataSet.allPrimitives();
-    		Collection<OsmPrimitive> allOld = fromDataSet.allPrimitives();
-    		if (output.equals("replace")) {
-    			Command cmd = createCommand(allOld, allNew);
-    			if (cmd != null) {
-    				Main.main.editLayer().add(cmd);
-    				Main.ds.clearSelection();
-    			}
-    		} else if (output.equals("selection")) {
-    			Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
-    			for (OsmPrimitive osm : Main.ds.allPrimitives())
-    				if (allNew.contains(osm))
-    					sel.add(osm);
-    			Main.ds.setSelected(sel);
-    		}
-    	}
-    
-    	/**
-    	 * Create a command that replaces all objects in from with those in to. The lists will be
-    	 * changed by createCommand.
-    	 */
-    	private Command createCommand(Collection<OsmPrimitive> from, Collection<OsmPrimitive> to) {
-    		// remove all objects in from/to, that are present in both lists.
-    		for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
-    			OsmPrimitive osm = toIt.next();
-    			for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
-    				if (fromIt.next().realEqual(osm)) {
-    					toIt.remove();
-    					fromIt.remove();
-    					break;
-    				}
-    			}
-    		}
-    
-    		Collection<Command> cmd = new LinkedList<Command>();
-    
-    		// extract all objects that have changed
-    		for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
-    			OsmPrimitive toOsm = toIt.next();
-    			for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
-    				OsmPrimitive fromOsm = fromIt.next();
-    				if (fromOsm.equals(toOsm)) {
-    					toIt.remove();
-    					fromIt.remove();
-    					cmd.add(new ChangeCommand(fromOsm, toOsm));
-    					break;
-    				}
-    			}
-    		}
-    		for (OsmPrimitive fromOsm : Main.ds.allPrimitives()) {
-    			for (Iterator<OsmPrimitive> it = to.iterator(); it.hasNext();) {
-    				OsmPrimitive toOsm = it.next();
-    				if (fromOsm.equals(toOsm)) {
-    					it.remove();
-    					cmd.add(new ChangeCommand(fromOsm, toOsm));
-    					break;
-    				}
-    			}
-    		}
-    
-    		// extract all added objects
-    		for (OsmPrimitive osm : to)
-    			cmd.add(new AddCommand(osm));
-    
-    		// extract all deleted objects. Delete references as well.
-    		CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
-    		for (OsmPrimitive osm : from)
-    			osm.visit(v);
-    		v.data.addAll(from);
-    		if (!v.data.isEmpty())
-    			cmd.add(new DeleteCommand(v.data));
-    
-    		if (cmd.isEmpty())
-    			return null;
-    		return new SequenceCommand(tr("Executing {0}",getValue(NAME)), cmd);
-    	}
-    }
+		private final Process p;
+		private DataSet dataSet;
+		private DataSet fromDataSet;
+		private ExecuteToolRunner(String msg, Process p) {
+			super(msg);
+			this.p = p;
+			currentAction.setText(tr("Executing {0}",getValue(NAME)));
+		}
+
+		@Override protected void realRun() throws SAXException, IOException {
+			if (!input.isEmpty()) {
+				fromDataSet = new DataSet();
+				final Collection<GpsPoint> gpxPoints = new LinkedList<GpsPoint>();
+				final boolean addOsm = !flags.contains("noosm");
+				final boolean addGpx = flags.contains("gpx");
+
+				AddVisitor adder = new AddVisitor(fromDataSet, flags.contains("include_references"));
+				if (input.contains("selection")) {
+					Collection<OsmPrimitive> sel = Main.ds.getSelected();
+					if (addOsm) {
+						for (OsmPrimitive osm : sel)
+							osm.visit(adder);
+						if (flags.contains("include_back_references")) {
+							CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
+							for (OsmPrimitive osm : sel)
+								osm.visit(v);
+							AddVisitor a = new AddVisitor(fromDataSet);
+							for (OsmPrimitive osm : v.data)
+								osm.visit(a);
+						}
+					}
+					if (addGpx) {
+						AllNodesVisitor v = new AllNodesVisitor();
+						for (OsmPrimitive osm : sel)
+							osm.visit(v);
+						Bounds b = new Bounds();
+						for (Node n : v.nodes)
+							b.extend(n.coor);
+						for (Layer l : Main.map.mapView.getAllLayers()) {
+							if (!(l instanceof RawGpsLayer))
+								continue;
+							RawGpsLayer layer = (RawGpsLayer)l;
+							for (Collection<GpsPoint> c : layer.data)
+								for (GpsPoint p : c)
+									if (p.latlon.isWithin(b))
+										gpxPoints.add(p);
+						}
+					}
+				}
+				if (input.contains("all")) {
+					if (addOsm)
+						for (OsmPrimitive osm : Main.ds.allPrimitives())
+							osm.visit(adder);
+					for (Layer l : Main.map.mapView.getAllLayers())
+						if (l instanceof RawGpsLayer)
+							for (Collection<GpsPoint> c : ((RawGpsLayer)l).data)
+								for (GpsPoint p : c)
+									gpxPoints.add(p);
+				}
+				if (input.contains("screen")) {
+					if (Main.map == null) {
+						errorMessage = tr("The Tool requires some data to be loaded.");
+						cancel();
+						return;
+					}
+					LatLon bottomLeft = Main.map.mapView.getLatLon(0,Main.map.mapView.getHeight());
+					LatLon topRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(), 0);
+					Bounds b = new Bounds(bottomLeft, topRight);
+					if (addOsm) {
+						Collection<Node> nodes = new HashSet<Node>();
+						for (Node n : Main.ds.nodes) {
+							if (n.coor.isWithin(b)) {
+								n.visit(adder);
+								nodes.add(n);
+							}
+						}
+						Collection<Segment> segments = new HashSet<Segment>();
+						for (Segment s : Main.ds.segments) {
+							if (nodes.contains(s.from) || nodes.contains(s.to)) {
+								s.visit(adder);
+								segments.add(s);
+							}
+						}
+						for (Way w : Main.ds.ways) {
+							for (Segment s : w.segments) {
+								if (segments.contains(s)) {
+									w.visit(adder);
+									break;
+								}
+							}
+						}
+					}
+					if (addGpx) {
+						for (Layer l : Main.map.mapView.getAllLayers())
+							if (l instanceof RawGpsLayer)
+								for (Collection<GpsPoint> c : ((RawGpsLayer)l).data)
+									for (GpsPoint p : c)
+										if (p.latlon.isWithin(b))
+											gpxPoints.add(p);
+					}
+				}
+				OsmWriter.output(p.getOutputStream(), new OsmWriter.Osm(){
+					public void write(PrintWriter out) {
+						if (addOsm)
+							new OsmWriter.All(fromDataSet, false).write(out);
+						if (addGpx) {
+							Collection<Collection<GpsPoint>> c = new LinkedList<Collection<GpsPoint>>();
+							c.add(gpxPoints);
+							GpxWriter.Trk writer = new GpxWriter.Trk(c);
+							writer.header(out);
+							if (!gpxPoints.isEmpty())
+								writer.write(out);
+							writer.footer(out);
+						}
+					}
+				});
+			}
+			if (output != null)
+				dataSet = OsmReader.parseDataSet(p.getInputStream(), currentAction, progress);
+		}
+
+		@Override protected void cancel() {
+			p.destroy();
+		}
+
+		@Override protected void finish() {
+			if (dataSet == null || output == null || output.equals("discard"))
+				return; // user cancelled or no stdout to process
+			Collection<OsmPrimitive> allNew = dataSet.allPrimitives();
+			Collection<OsmPrimitive> allOld = fromDataSet.allPrimitives();
+			if (output.equals("replace")) {
+				Command cmd = createCommand(allOld, allNew);
+				if (cmd != null) {
+					Main.main.editLayer().add(cmd);
+					Main.ds.clearSelection();
+				}
+			} else if (output.equals("selection")) {
+				Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
+				for (OsmPrimitive osm : Main.ds.allPrimitives())
+					if (allNew.contains(osm))
+						sel.add(osm);
+				Main.ds.setSelected(sel);
+			}
+		}
+
+		/**
+		 * Create a command that replaces all objects in from with those in to. The lists will be
+		 * changed by createCommand.
+		 */
+		private Command createCommand(Collection<OsmPrimitive> from, Collection<OsmPrimitive> to) {
+			// remove all objects in from/to, that are present in both lists.
+			for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
+				OsmPrimitive osm = toIt.next();
+				for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
+					if (fromIt.next().realEqual(osm)) {
+						toIt.remove();
+						fromIt.remove();
+						break;
+					}
+				}
+			}
+
+			Collection<Command> cmd = new LinkedList<Command>();
+
+			// extract all objects that have changed
+			for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
+				OsmPrimitive toOsm = toIt.next();
+				for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
+					OsmPrimitive fromOsm = fromIt.next();
+					if (fromOsm.equals(toOsm)) {
+						toIt.remove();
+						fromIt.remove();
+						cmd.add(new ChangeCommand(fromOsm, toOsm));
+						break;
+					}
+				}
+			}
+			for (OsmPrimitive fromOsm : Main.ds.allPrimitives()) {
+				for (Iterator<OsmPrimitive> it = to.iterator(); it.hasNext();) {
+					OsmPrimitive toOsm = it.next();
+					if (fromOsm.equals(toOsm)) {
+						it.remove();
+						cmd.add(new ChangeCommand(fromOsm, toOsm));
+						break;
+					}
+				}
+			}
+
+			// extract all added objects
+			for (OsmPrimitive osm : to)
+				cmd.add(new AddCommand(osm));
+
+			// extract all deleted objects. Delete references as well.
+			CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
+			for (OsmPrimitive osm : from)
+				osm.visit(v);
+			v.data.addAll(from);
+			if (!v.data.isEmpty())
+				cmd.add(new DeleteCommand(v.data));
+
+			if (cmd.isEmpty())
+				return null;
+			return new SequenceCommand(tr("Executing {0}",getValue(NAME)), cmd);
+		}
+	}
 
 	public ExternalToolsAction(String name, String[] exec, String[] flags, String[] input, String output) {
@@ -242,5 +299,5 @@
 			@Override public void startDocument() {
 				current.push(main);
-            }
+			}
 			@Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
 				if (qname.equals("group")) {
Index: /src/org/openstreetmap/josm/actions/GpxExportAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 142)
+++ /src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 143)
@@ -30,4 +30,5 @@
 import org.openstreetmap.josm.gui.layer.RawGpsLayer;
 import org.openstreetmap.josm.io.GpxWriter;
+import org.openstreetmap.josm.io.XmlWriter;
 import org.openstreetmap.josm.tools.GBC;
 
@@ -117,13 +118,13 @@
 			Main.pref.put("lastCopyright", copyright.getText());
 		
-		try {
-			GpxWriter w = new GpxWriter(new FileOutputStream(file), layer.name, desc.getText(),
+		XmlWriter.OsmWriterInterface w;
+		if (layer instanceof RawGpsLayer)
+			w = new GpxWriter.Trk(((RawGpsLayer)layer).data);
+		else
+			w = new GpxWriter.All(Main.ds, layer.name, desc.getText(),
 					authorName.getText(), email.getText(), copyright.getText(),
 					copyrightYear.getText(), keywords.getText());
-			if (layer instanceof RawGpsLayer)
-				w.output(((RawGpsLayer)layer).data);
-			else
-				w.output(Main.ds);
-			w.close();
+		try {
+			XmlWriter.output(new FileOutputStream(file), w);
 		} catch (IOException x) {
 			x.printStackTrace();
Index: /src/org/openstreetmap/josm/actions/SaveAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/SaveAction.java	(revision 142)
+++ /src/org/openstreetmap/josm/actions/SaveAction.java	(revision 143)
@@ -54,5 +54,5 @@
 				GpxExportAction.exportGpx(file, layer);
 			} else if (ExtensionFileFilter.filters[ExtensionFileFilter.OSM].acceptName(file.getPath())) {
-				OsmWriter.output(new FileOutputStream(file), Main.ds, false);
+				OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(Main.ds, false));
 			} else if (ExtensionFileFilter.filters[ExtensionFileFilter.CSV].acceptName(file.getPath())) {
 				JOptionPane.showMessageDialog(Main.parent, tr("CSV output not supported yet."));
Index: /src/org/openstreetmap/josm/data/osm/visitor/Visitor.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/visitor/Visitor.java	(revision 142)
+++ /src/org/openstreetmap/josm/data/osm/visitor/Visitor.java	(revision 143)
@@ -1,6 +1,6 @@
 package org.openstreetmap.josm.data.osm.visitor;
 
+import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Segment;
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Way;
 
Index: /src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java
===================================================================
--- /src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 142)
+++ /src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java	(revision 143)
@@ -77,4 +77,5 @@
 	 */
 	private JTable history = new JTable(data);
+	private JScrollPane historyPane = new JScrollPane(history);
 
 	private Map<OsmPrimitive, List<HistoryItem>> cache = new HashMap<OsmPrimitive, List<HistoryItem>>();
@@ -85,5 +86,5 @@
 	public HistoryDialog() {
 		super(tr("History"), "history", tr("Display the history of all selected items."), KeyEvent.VK_H, 150);
-		history.setVisible(false);
+		historyPane.setVisible(false);
 		notLoaded.setVisible(true);
 
@@ -115,5 +116,5 @@
 		JPanel centerPanel = new JPanel(new GridBagLayout());
 		centerPanel.add(notLoaded, GBC.eol().fill(GBC.BOTH));
-		centerPanel.add(new JScrollPane(history), GBC.eol().fill(GBC.BOTH));
+		centerPanel.add(historyPane, GBC.eol().fill(GBC.BOTH));
 		add(centerPanel, BorderLayout.CENTER);
 
@@ -160,5 +161,5 @@
 		Collection<OsmPrimitive> sel = Main.ds.getSelected();
 		if (!cache.keySet().containsAll(sel)) {
-			history.setVisible(false);
+			historyPane.setVisible(false);
 			notLoaded.setVisible(true);
 		} else {
@@ -169,5 +170,5 @@
 			for (HistoryItem i : orderedHistory)
 				data.addRow(new Object[]{i.osm, i.osm.timestamp, i.visible});
-			history.setVisible(true);
+			historyPane.setVisible(true);
 			notLoaded.setVisible(false);
 		}
Index: /src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/GpxWriter.java	(revision 142)
+++ /src/org/openstreetmap/josm/io/GpxWriter.java	(revision 143)
@@ -1,8 +1,5 @@
 package org.openstreetmap.josm.io;
 
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
 import java.util.Collection;
 import java.util.LinkedList;
@@ -16,5 +13,4 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
-import org.openstreetmap.josm.tools.XmlWriter;
 
 /**
@@ -28,42 +24,8 @@
  * @author imi
  */
-public class GpxWriter {
-
-	/**
-	 * This is the output writer to store the resulting data in.
-	 */
-	private PrintWriter out;
-
-	public GpxWriter(OutputStream os, String name, String desc, String author, String email, String copyright, String year, String keywords) {
-		try {
-			out = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
-		} catch (UnsupportedEncodingException e) {
-			throw new RuntimeException(e);
-		}
-		out.println(XmlWriter.header());
-		out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
-		out.println("  <metadata>");
-		if (!name.equals(""))
-			out.println("    <name>"+XmlWriter.encode(name)+"</name>");
-		if (!desc.equals(""))
-			out.println("    <desc>"+XmlWriter.encode(desc)+"</desc>");
-		if (!author.equals("")) {
-			out.println("    <author>");
-			out.println("      <name>"+XmlWriter.encode(author)+"</name>");
-			if (!email.equals(""))
-				out.println("      <email>"+XmlWriter.encode(email)+"</email>");
-			out.println("    </author>");
-			if (!copyright.equals("")) {
-				out.println("    <copyright author='"+XmlWriter.encode(author)+"'>");
-				if (!year.equals(""))
-					out.println("      <year>"+XmlWriter.encode(year)+"</year>");
-				out.println("      <license>"+XmlWriter.encode(copyright)+"</license>");
-				out.println("    </copyright>");
-			}
-		}
-		if (!keywords.equals("")) {
-			out.println("    <keywords>"+XmlWriter.encode(keywords)+"</keywords>");
-		}
-		// don't finish here, to give output functions the chance to add <bounds>
+public class GpxWriter extends XmlWriter {
+
+	public GpxWriter(PrintWriter out) {
+		super(out);
 	}
 
@@ -75,78 +37,131 @@
 	 * all remaining nodes are added as wpt.
 	 */
-	public void output(DataSet data) {
-		Collection<OsmPrimitive> all = data.allNonDeletedPrimitives();
-		if (all.isEmpty()) {
+	public static final class All implements XmlWriter.OsmWriterInterface {
+		private final DataSet data;
+		private final String name;
+		private final String desc;
+		private final String author;
+		private final String email;
+		private final String copyright;
+		private final String year;
+		private final String keywords;
+		private boolean metadataClosed = false;
+
+		public All(DataSet data, String name, String desc, String author, String email, String copyright, String year, String keywords) {
+			this.data = data;
+			this.name = name;
+			this.desc = desc;
+			this.author = author;
+			this.email = email;
+			this.copyright = copyright;
+			this.year = year;
+			this.keywords = keywords;
+		}
+
+		public void header(PrintWriter out) {
+			out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
+			out.println("  <metadata>");
+			if (!name.equals(""))
+				out.println("    <name>"+XmlWriter.encode(name)+"</name>");
+			if (!desc.equals(""))
+				out.println("    <desc>"+XmlWriter.encode(desc)+"</desc>");
+			if (!author.equals("")) {
+				out.println("    <author>");
+				out.println("      <name>"+XmlWriter.encode(author)+"</name>");
+				if (!email.equals(""))
+					out.println("      <email>"+XmlWriter.encode(email)+"</email>");
+				out.println("    </author>");
+				if (!copyright.equals("")) {
+					out.println("    <copyright author='"+XmlWriter.encode(author)+"'>");
+					if (!year.equals(""))
+						out.println("      <year>"+XmlWriter.encode(year)+"</year>");
+					out.println("      <license>"+XmlWriter.encode(copyright)+"</license>");
+					out.println("    </copyright>");
+				}
+			}
+			if (!keywords.equals("")) {
+				out.println("    <keywords>"+XmlWriter.encode(keywords)+"</keywords>");
+			}
+			// don't finish here, to give output functions the chance to add <bounds>
+		}
+
+		public void write(PrintWriter out) {
+			Collection<OsmPrimitive> all = data.allNonDeletedPrimitives();
+			if (all.isEmpty())
+				return;
+			GpxWriter writer = new GpxWriter(out);
+			// calculate bounds
+			Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
+			for (Node n : data.nodes)
+				if (!n.deleted)
+					b.extend(n.coor);
+			out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
 			out.println("  </metadata>");
+			metadataClosed = true;
+
+			// add ways
+			for (Way w : data.ways) {
+				if (w.deleted)
+					continue;
+				out.println("  <trk>");
+				Segment oldLs = null;
+				for (Segment ls : w.segments) {
+					if (ls.incomplete)
+						continue;
+					// end old segemnt, if no longer match a chain
+					if (oldLs != null && !oldLs.to.coor.equals(ls.from.coor)) {
+						out.println("    </trkseg>");
+						writer.outputNode(oldLs.to, false);
+						all.remove(oldLs.to);
+						oldLs = null;
+					}
+					// start new segment if necessary
+					if (oldLs == null)
+						out.println("    <trkseg>");
+					writer.outputNode(ls.from, false);
+					all.remove(ls.from);
+					oldLs = ls;
+					all.remove(ls);
+				}
+				// write last node if there
+				if (oldLs != null) {
+					writer.outputNode(oldLs.to, false);
+					all.remove(oldLs.to);
+					out.println("    </trkseg>");
+				}
+				out.println("  </trk>");
+				all.remove(w);
+			}
+
+			// add remaining segments
+			Collection<Segment> segments = new LinkedList<Segment>();
+			for (OsmPrimitive osm : all)
+				if (osm instanceof Segment && !((Segment)osm).incomplete)
+					segments.add((Segment)osm);
+			if (!segments.isEmpty()) {
+				out.println("  <trk>");
+				for (Segment ls : segments) {
+					out.println("    <trkseg>");
+					writer.outputNode(ls.from, false);
+					all.remove(ls.from);
+					writer.outputNode(ls.to, false);
+					all.remove(ls.to);
+					out.println("    </trkseg>");
+					all.remove(ls);
+				}
+				out.println("  </trk>");
+			}
+
+			// finally add the remaining nodes
+			for (OsmPrimitive osm : all)
+				if (osm instanceof Node)
+					writer.outputNode((Node)osm, true);
+		}
+
+		public void footer(PrintWriter out) {
+			if (!metadataClosed)
+				out.println("  </metadata>");
 			out.println("</gpx>");
-			return;
-		}
-		// calculate bounds
-		Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
-		for (Node n : data.nodes)
-			if (!n.deleted)
-				b.extend(n.coor);
-		out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
-		out.println("  </metadata>");
-
-		// add ways
-		for (Way w : data.ways) {
-			if (w.deleted)
-				continue;
-			out.println("  <trk>");
-			Segment oldLs = null;
-			for (Segment ls : w.segments) {
-				if (ls.incomplete)
-					continue;
-				// end old segemnt, if no longer match a chain
-				if (oldLs != null && !oldLs.to.coor.equals(ls.from.coor)) {
-					out.println("    </trkseg>");
-					outputNode(oldLs.to, false);
-					all.remove(oldLs.to);
-					oldLs = null;
-				}
-				// start new segment if necessary
-				if (oldLs == null)
-					out.println("    <trkseg>");
-				outputNode(ls.from, false);
-				all.remove(ls.from);
-				oldLs = ls;
-				all.remove(ls);
-			}
-			// write last node if there
-			if (oldLs != null) {
-				outputNode(oldLs.to, false);
-				all.remove(oldLs.to);
-				out.println("    </trkseg>");
-			}
-			out.println("  </trk>");
-			all.remove(w);
-		}
-
-		// add remaining segments
-		Collection<Segment> segments = new LinkedList<Segment>();
-		for (OsmPrimitive osm : all)
-			if (osm instanceof Segment && !((Segment)osm).incomplete)
-				segments.add((Segment)osm);
-		if (!segments.isEmpty()) {
-			out.println("  <trk>");
-			for (Segment ls : segments) {
-				out.println("    <trkseg>");
-				outputNode(ls.from, false);
-				all.remove(ls.from);
-				outputNode(ls.to, false);
-				all.remove(ls.to);
-				out.println("    </trkseg>");
-				all.remove(ls);
-			}
-			out.println("  </trk>");
-		}
-
-		// finally add the remaining nodes
-		for (OsmPrimitive osm : all)
-			if (osm instanceof Node)
-				outputNode((Node)osm, true);
-
-		out.println("</gpx>");
+		}
 	}
 
@@ -156,40 +171,52 @@
 	 * trk with as many trkseg as there are collections in the outer collection.
 	 */
-	public void output(Collection<Collection<GpsPoint>> data) {
-		if (data.size() == 0) {
+	public static final class Trk implements XmlWriter.OsmWriterInterface {
+		private final Collection<Collection<GpsPoint>> data;
+		public Trk(Collection<Collection<GpsPoint>> data) {
+			this.data = data;
+		}
+
+		public void header(PrintWriter out) {
+			out.println("<gpx version='1.1' creator='JOSM' xmlns='http://www.topografix.com/GPX/1/1'>");
+		}
+
+		public void write(PrintWriter out) {
+			if (data.size() == 0)
+				return;
+			// calculate bounds
+			Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
+			for (Collection<GpsPoint> c : data)
+				for (GpsPoint p : c)
+					b.extend(p.latlon);
 			out.println("  </metadata>");
+			out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
+			out.println("  </metadata>");
+
+			out.println("  <trk>");
+			for (Collection<GpsPoint> c : data) {
+				out.println("    <trkseg>");
+				LatLon last = null;
+				for (GpsPoint p : c) {
+					// skip double entries
+					if (p.latlon.equals(last))
+						continue;
+					last =  p.latlon;
+					LatLon ll = p.latlon;
+					out.print("      <trkpt lat='"+ll.lat()+"' lon='"+ll.lon()+"'");
+					if (p.time != null && p.time.length()!=0) {
+						out.println(">");
+						out.println("        <time>"+p.time+"</time>");
+						out.println("      </trkpt>");
+					} else
+						out.println(" />");
+				}
+				out.println("    </trkseg>");
+			}
+			out.println("  </trk>");
+		}
+
+		public void footer(PrintWriter out) {
 			out.println("</gpx>");
-			return;
-		}
-		// calculate bounds
-		Bounds b = new Bounds(new LatLon(Double.MAX_VALUE, Double.MAX_VALUE), new LatLon(Double.MIN_VALUE, Double.MIN_VALUE));
-		for (Collection<GpsPoint> c : data)
-			for (GpsPoint p : c)
-				b.extend(p.latlon);
-		out.println("    <bounds minlat='"+b.min.lat()+"' minlon='"+b.min.lon()+"' maxlat='"+b.max.lat()+"' maxlon='"+b.max.lon()+"' />");
-		out.println("  </metadata>");
-
-		out.println("  <trk>");
-		for (Collection<GpsPoint> c : data) {
-			out.println("    <trkseg>");
-			LatLon last = null;
-			for (GpsPoint p : c) {
-				// skip double entries
-				if (p.latlon.equals(last))
-					continue;
-				last =  p.latlon;
-				LatLon ll = p.latlon;
-				out.print("      <trkpt lat='"+ll.lat()+"' lon='"+ll.lon()+"'");
-				if (p.time != null && p.time.length()!=0) {
-					out.println(">");
-					out.println("        <time>"+p.time+"</time>");
-					out.println("      </trkpt>");
-				} else
-					out.println(" />");
-			}
-			out.println("    </trkseg>");
-		}
-		out.println("  </trk>");
-		out.println("</gpx>");
+        }
 	}
 
@@ -224,8 +251,3 @@
 			out.println(" />");
 	}
-
-	public void close() {
-		out.flush();
-		out.close();
-	}
 }
Index: /src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 142)
+++ /src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 143)
@@ -164,5 +164,5 @@
 			if (addBody) {
 				OutputStream out = activeConnection.getOutputStream();
-				OsmWriter.outputSingle(out, osm, true);
+				OsmWriter.output(out, new OsmWriter.Single(osm, true));
 				out.close();
 			}
@@ -178,5 +178,5 @@
 			if (retCode != 200) {
 				ByteArrayOutputStream o = new ByteArrayOutputStream();
-				OsmWriter.outputSingle(o, osm, true);
+				OsmWriter.output(o, new OsmWriter.Single(osm, true));
 				System.out.println(new String(o.toByteArray(), "UTF-8").toString());
 				throw new RuntimeException(retCode+" "+retMsg);
Index: /src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/OsmWriter.java	(revision 142)
+++ /src/org/openstreetmap/josm/io/OsmWriter.java	(revision 143)
@@ -1,8 +1,5 @@
 package org.openstreetmap.josm.io;
 
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
 import java.util.HashMap;
 import java.util.Map.Entry;
@@ -14,5 +11,4 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
-import org.openstreetmap.josm.tools.XmlWriter;
 
 /**
@@ -21,10 +17,5 @@
  * @author imi
  */
-public class OsmWriter implements Visitor {
-
-	/**
-	 * The output writer to save the values to.
-	 */
-	private PrintWriter out;
+public class OsmWriter extends XmlWriter implements Visitor {
 
 	/**
@@ -40,39 +31,63 @@
 	private final boolean osmConform;
 
+	public abstract static class Osm implements OsmWriterInterface {
+		public void header(PrintWriter out) {
+			out.println("<osm version='0.3' generator='JOSM'>");
+		}
+		public void footer(PrintWriter out) {
+			out.println("</osm>");
+		}
+	}
+	
 	/**
-	 * Output the data to the stream
-	 * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this
-	 * 		case, not all information can be retrieved later (as example, modified state
-	 * 		is lost and id's remain 0 instead of decrementing from -1)
+	 * An output writer for function output that writes everything of the given dataset into
+	 * the xml
 	 */
-	public static void output(OutputStream out, DataSet ds, boolean osmConform) {
-		OsmWriter writer = new OsmWriter(out, osmConform);
-		writer.out.println("<?xml version='1.0' encoding='UTF-8'?>");
-		writer.out.println("<osm version='0.3' generator='JOSM'>");
-		for (Node n : ds.nodes)
-			writer.visit(n);
-		for (Segment ls : ds.segments)
-			writer.visit(ls);
-		for (Way w : ds.ways)
-			writer.visit(w);
-		writer.out.println("</osm>");
-		writer.close();
+	public static final class All extends Osm {
+		private final DataSet ds;
+		private final boolean osmConform;
+
+		/**
+		 * Construct an writer function
+		 * @param osmConform <code>true</code>, if the xml should be 100% osm conform. In this
+		 * 		case, not all information can be retrieved later (as example, modified state
+		 * 		is lost and id's remain 0 instead of decrementing from -1)
+		 */
+		public All(DataSet ds, boolean osmConform) {
+			this.ds = ds;
+			this.osmConform = osmConform;
+		}
+
+		public void write(PrintWriter out) {
+			Visitor writer = new OsmWriter(out, osmConform);
+			for (Node n : ds.nodes)
+				writer.visit(n);
+			for (Segment ls : ds.segments)
+				writer.visit(ls);
+			for (Way w : ds.ways)
+				writer.visit(w);
+        }
 	}
 
-	public static void outputSingle(OutputStream out, OsmPrimitive osm, boolean osmConform) {
-		OsmWriter writer = new OsmWriter(out, osmConform);
-		writer.out.println(XmlWriter.header());
-		writer.out.println("<osm version='0.3' generator='JOSM'>");
-		osm.visit(writer);
-		writer.out.println("</osm>");
-		writer.close();
+	/**
+	 * An output writer for functino output that writes only one specific primitive into
+	 * the xml
+	 */
+	public static final class Single extends Osm {
+		private final OsmPrimitive osm;
+		private final boolean osmConform;
+
+		public Single(OsmPrimitive osm, boolean osmConform) {
+			this.osm = osm;
+			this.osmConform = osmConform;
+		}
+
+		public void write(PrintWriter out) {
+			osm.visit(new OsmWriter(out, osmConform));
+        }
 	}
 
-	private OsmWriter(OutputStream out, boolean osmConform) {
-		try {
-	        this.out = new PrintWriter(new OutputStreamWriter(out, "UTF-8"));
-        } catch (UnsupportedEncodingException e) {
-        	throw new RuntimeException(e);
-        }
+	private OsmWriter(PrintWriter out, boolean osmConform) {
+		super(out);
 		this.osmConform = osmConform;
 	}
@@ -146,7 +161,3 @@
 		}
 	}
-
-	public void close() {
-	    out.close();
-    }
 }
Index: /src/org/openstreetmap/josm/io/XmlWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/XmlWriter.java	(revision 143)
+++ /src/org/openstreetmap/josm/io/XmlWriter.java	(revision 143)
@@ -0,0 +1,85 @@
+package org.openstreetmap.josm.io;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+
+/**
+ * Helper class to use for xml outputting classes.
+ * 
+ * @author imi
+ */
+public class XmlWriter {
+
+	/**
+	 * The interface to write the data into an Osm stream
+	 * @author immanuel.scholz
+	 */
+	public static interface OsmWriterInterface {
+		void header(PrintWriter out);
+		void write(PrintWriter out);
+		void footer(PrintWriter out);
+	}
+
+
+	protected XmlWriter(PrintWriter out) {
+		this.out = out;
+	}
+
+	/**
+	 * Encode the given string in XML1.0 format.
+	 * Optimized to fast pass strings that don't need encoding (normal case).
+	 */
+	public static String encode(String unencoded) {
+		StringBuilder buffer = null;
+		for (int i = 0; i < unencoded.length(); ++i) {
+			String encS = XmlWriter.encoding.get(unencoded.charAt(i));
+			if (encS != null) {
+				if (buffer == null)
+					buffer = new StringBuilder(unencoded.substring(0,i));
+				buffer.append(encS);
+			} else if (buffer != null)
+				buffer.append(unencoded.charAt(i));
+		}
+		return (buffer == null) ? unencoded : buffer.toString();
+	}
+
+	/**
+	 * Write the header and start tag, then call the runnable to add all real tags and finally
+	 * "closes" the xml by writing the footer.
+	 */
+	public static void output(OutputStream outStream, OsmWriterInterface outputWriter) {
+		PrintWriter out;
+		try {
+			out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			throw new RuntimeException(e);
+		}
+		out.println("<?xml version='1.0' encoding='UTF-8'?>");
+		outputWriter.header(out);
+		outputWriter.write(out);
+		outputWriter.footer(out);
+		out.flush();
+		out.close();
+	}
+
+
+
+	/**
+	 * The output writer to save the values to.
+	 */
+	protected final PrintWriter out;
+	final private static HashMap<Character, String> encoding = new HashMap<Character, String>();
+	static {
+		encoding.put('<', "&lt;");
+		encoding.put('>', "&gt;");
+		encoding.put('"', "&quot;");
+		encoding.put('\'', "&apos;");
+		encoding.put('&', "&amp;");
+		encoding.put('\n', "&#xA;");
+		encoding.put('\r', "&#xD;");
+		encoding.put('\t', "&#x9;");
+	}
+}
Index: c/org/openstreetmap/josm/tools/XmlWriter.java
===================================================================
--- /src/org/openstreetmap/josm/tools/XmlWriter.java	(revision 142)
+++ 	(revision )
@@ -1,50 +1,0 @@
-package org.openstreetmap.josm.tools;
-
-import java.util.HashMap;
-
-/**
- * Helper class to use for xml outputting classes.
- * 
- * @author imi
- */
-public class XmlWriter {
-
-	/**
-	 * Encode the given string in XML1.0 format.
-	 * Optimized to fast pass strings that don't need encoding (normal case).
-	 */
-	public static String encode(String unencoded) {
-		StringBuilder buffer = null;
-		for (int i = 0; i < unencoded.length(); ++i) {
-			String encS = XmlWriter.encoding.get(unencoded.charAt(i));
-			if (encS != null) {
-				if (buffer == null)
-					buffer = new StringBuilder(unencoded.substring(0,i));
-				buffer.append(encS);
-			} else if (buffer != null)
-				buffer.append(unencoded.charAt(i));
-		}
-		return (buffer == null) ? unencoded : buffer.toString();
-	}
-
-	/**
-	 * @return The standard XML1.0 header. Encoding is utf-8
-	 */
-	public static String header() {
-		return "<?xml version='1.0' encoding='UTF-8'?>";
-	}
-
-	
-	
-	final private static HashMap<Character, String> encoding = new HashMap<Character, String>();
-	static {
-		encoding.put('<', "&lt;");
-		encoding.put('>', "&gt;");
-		encoding.put('"', "&quot;");
-		encoding.put('\'', "&apos;");
-		encoding.put('&', "&amp;");
-		encoding.put('\n', "&#xA;");
-		encoding.put('\r', "&#xD;");
-		encoding.put('\t', "&#x9;");
-	}
-}
Index: /test/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java
===================================================================
--- /test/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 142)
+++ /test/org/openstreetmap/josm/data/osm/visitor/MergeVisitorTest.java	(revision 143)
@@ -11,5 +11,4 @@
 import org.openstreetmap.josm.data.osm.Segment;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
 import org.openstreetmap.josm.testframework.Bug;
 import org.openstreetmap.josm.testframework.DataSetTestCaseHelper;
@@ -267,8 +266,44 @@
 	/**
 	 * Deleted segments should be deleted when merged over unchanged segments.
-	 * Deleted segments should also raise an conflict when merged over changed segments. 
-	 */
-	//TODO
-
+	 */
+	public void testMergeDeletedOverUnchangedDeletes() {
+		DataSet ds = new DataSet();
+		Segment oldSegment = createSegment(ds, false, false, 23);
+		Segment s = createSegment(null, false, true, 23);
+		
+		MergeVisitor v = new MergeVisitor(ds);
+		v.visit(s);
+		v.fixReferences();
+		
+		assertEquals(true, oldSegment.deleted);
+	}
+	
+
+	/**
+	 * Deleted segments should raise an conflict when merged over changed segments. 
+	 */
+	public void testMergeDeletedOverChangedConflict() {
+		DataSet ds = new DataSet();
+		createSegment(ds, false, false, 23).modified = true;
+		Segment s = createSegment(null, false, true, 23);
+		
+		MergeVisitor v = new MergeVisitor(ds);
+		v.visit(s);
+		v.fixReferences();
+		
+		assertEquals(1, v.conflicts.size());
+	}
+
+
+	private Segment createSegment(DataSet ds, boolean incomplete, boolean deleted, int id) {
+		Node n1 = DataSetTestCaseHelper.createNode(ds);
+		Node n2 = DataSetTestCaseHelper.createNode(ds);
+		Segment s = DataSetTestCaseHelper.createSegment(ds, n1, n2);
+		s.incomplete = incomplete;
+		s.id = id;
+		s.deleted = deleted;
+		return s;
+	}
+	
 	/**
 	 * Create that amount of nodes and add them to the dataset. The id will be 1,2,3,4...
Index: /test/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java
===================================================================
--- /test/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java	(revision 142)
+++ /test/org/openstreetmap/josm/testframework/DataSetTestCaseHelper.java	(revision 143)
@@ -44,5 +44,6 @@
 		Way t = new Way();
 		t.segments.addAll(Arrays.asList(segments));
-		ds.ways.add(t);
+		if (ds != null)
+			ds.ways.add(t);
 		return t;
 	}
@@ -53,5 +54,6 @@
 	public static Segment createSegment(DataSet ds, Node n1, Node n2) {
 		Segment ls = new Segment(n1, n2);
-		ds.segments.add(ls);
+		if (ds != null)
+			ds.segments.add(ls);
 		return ls;
 	}
