Index: /data/empty.xml
===================================================================
--- /data/empty.xml	(revision 60)
+++ /data/empty.xml	(revision 61)
@@ -1,3 +1,2 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<osm />
-
+<osm version='0.3' generator='JOSM'>
+</osm>
Index: /src/org/openstreetmap/josm/actions/DownloadAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 60)
+++ /src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 61)
@@ -32,4 +32,5 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.GeoPoint;
+import org.openstreetmap.josm.data.Preferences.PreferencesException;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.BookmarkList;
@@ -55,4 +56,6 @@
 public class DownloadAction extends JosmAction {
 
+    private enum DownloadStatus {FINISHED, REDISPLAY}
+
 	JTextField[] latlon = new JTextField[]{
 			new JTextField(9),
@@ -68,4 +71,27 @@
 
 	public void actionPerformed(ActionEvent e) {
+		
+		//TODO: Remove this in later versions (temporary only)
+		if (Main.pref.osmDataServer.endsWith("/0.2") || Main.pref.osmDataServer.endsWith("/0.2/")) {
+			int answer = JOptionPane.showConfirmDialog(Main.main, 
+					"You seem to have an outdated server entry in your preferences.\n" +
+					"\n" +
+					"As of JOSM Release 1.2, you must no longer specify the API version in\n" +
+					"the osm url. For the OSM standard server, use http://www.openstreetmap.org/api" +
+					"\n" +
+					"Fix settings and continue?", "Outdated server url detected.", JOptionPane.YES_NO_OPTION);
+			if (answer != JOptionPane.YES_OPTION)
+				return;
+			int cutPos = Main.pref.osmDataServer.endsWith("/0.2") ? 4 : 5;
+			Main.pref.osmDataServer = Main.pref.osmDataServer.substring(0, Main.pref.osmDataServer.length()-cutPos);
+			try {
+				Main.pref.save();
+			} catch (PreferencesException x) {
+				x.printStackTrace();
+				JOptionPane.showMessageDialog(Main.main, "Could not save the preferences chane:\n" +
+						x.getMessage());
+			}
+		}
+
 		JPanel dlg = new JPanel(new GridBagLayout());
 
@@ -208,10 +234,12 @@
 		
 		// Finally: the dialog
-		int r = JOptionPane.showConfirmDialog(Main.main, dlg, "Choose an area", 
-				JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
-		if (r != JOptionPane.OK_OPTION)
-			return;
-
-		startDownload();
+		while(true) {
+			int r = JOptionPane.showConfirmDialog(Main.main, dlg, "Choose an area", 
+					JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+			if (r != JOptionPane.OK_OPTION)
+				return;
+			if (startDownload() == DownloadStatus.FINISHED) 
+				break;
+		}
 	}
 
@@ -219,11 +247,11 @@
 	 * Read the values from the edit fields and start the download.
 	 */
-	private void startDownload() {
+	private DownloadStatus startDownload() {
 		Bookmark b = readBookmark();
 		if (b == null) {
 			JOptionPane.showMessageDialog(Main.main, "Please enter the desired coordinates or click on a bookmark.");
-			return;
-		}
-		
+			return DownloadStatus.REDISPLAY;
+		}
+
 		final OsmServerReader osmReader = new OsmServerReader(b.latlon[0], b.latlon[1], b.latlon[2], b.latlon[3]);
 		new Thread(new PleaseWaitRunnable("Downloading data"){
@@ -275,4 +303,5 @@
 			}
 		}).start();
+		return DownloadStatus.FINISHED;
 	}
 	
Index: c/org/openstreetmap/josm/actions/mapmode/AddTrackAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/mapmode/AddTrackAction.java	(revision 60)
+++ 	(revision )
@@ -1,156 +1,0 @@
-package org.openstreetmap.josm.actions.mapmode;
-
-import java.awt.Rectangle;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.AddCommand;
-import org.openstreetmap.josm.data.osm.LineSegment;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Track;
-import org.openstreetmap.josm.gui.MapFrame;
-import org.openstreetmap.josm.gui.SelectionManager;
-import org.openstreetmap.josm.gui.SelectionManager.SelectionEnded;
-
-/**
- * Add a new track from all selected line segments.
- *
- * If there is a selection when the mode is entered, all line segments in this
- * selection form a new track, except the user holds down Shift.
- *
- * The user can click on a line segment. If he holds down Shift, no track is 
- * created yet. If he holds down Alt, the whole track is considered instead of 
- * the clicked line segment. If the user holds down Ctrl, no track is created 
- * and the clicked line segment get removed from the list.
- *
- * Also, the user may select a rectangle as in selection mode. No node, area or
- * track can be selected this way.
- *
- * @author imi
- *
- */
-public class AddTrackAction extends MapMode implements SelectionEnded {
-
-	/**
-	 * The selection manager for this action, keeping track of all selections.
-	 */
-	SelectionManager selectionManager;
-	
-	/**
-	 * Create a new AddTrackAction.
-	 * @param mapFrame The MapFrame this action belongs to.
-	 */
-	public AddTrackAction(MapFrame mapFrame) {
-		super("Add Way", "addtrack", "Combine line segments to a new way.", KeyEvent.VK_W, mapFrame);
-		this.selectionManager = new SelectionManager(this, false, mv);
-	}
-
-	@Override
-	public void registerListener() {
-		super.registerListener();
-		selectionManager.register(mv);
-	}
-
-	@Override
-	public void unregisterListener() {
-		super.unregisterListener();
-		selectionManager.unregister(mv);
-	}
-
-	
-	@Override
-	public void actionPerformed(ActionEvent e) {
-		makeTrack();
-		super.actionPerformed(e);
-	}
-
-	/**
-	 * If Shift is pressed, only add the selected line segments to the selection.
-	 * If Ctrl is pressed, only remove the selected line segments from the selection.
-	 * If both, Shift and Ctrl is pressed, do nothing.
-	 * 
-	 * Else, form a new track out of all line segments in the selection and
-	 * clear the selection afterwards.
-	 * 
-	 * If Alt is pressed, consider all linesegments of all tracks a selected
-	 * line segment is part of. Also consider all line segments that cross the
-	 * selecting rectangle, instead only those that are fully within.
-	 * 
-	 */
-	public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
-		if (shift && ctrl)
-			return; // not allowed together
-
-		if (!ctrl && !shift)
-			Main.main.ds.clearSelection(); // new selection will replace the old.
-
-		Collection<OsmPrimitive> selectionList = selectionManager.getObjectsInRectangle(r,alt);
-		for (OsmPrimitive osm : selectionList)
-			osm.setSelected(!ctrl);
-
-		mv.repaint(); // from now on, the map has to be repainted.
-
-		if (ctrl || shift)
-			return; // no new track yet.
-		
-		makeTrack();
-	}
-
-	/**
-	 * Just make a track of all selected items.
-	 */
-	private void makeTrack() {
-		Collection<OsmPrimitive> selection = Main.main.ds.getSelected();
-		if (selection.isEmpty())
-			return;
-
-		// form a new track
-		LinkedList<LineSegment> lineSegments = new LinkedList<LineSegment>();
-		for (OsmPrimitive osm : selection) {
-			if (osm instanceof Track)
-				lineSegments.addAll(((Track)osm).segments);
-			else if (osm instanceof LineSegment)
-				lineSegments.add((LineSegment)osm);
-		}
-		
-		// sort the line segments in best possible order. This is done by:
-		// 0  if no elements in list, quit
-		// 1  taking the first ls as pivot, remove it from list
-		// 2  searching for a connection at start or end of pivot
-		// 3  if found, attach it, remove it from list, goto 2
-		// 4  if not found, save the pivot-string and goto 0
-		LinkedList<LineSegment> sortedLineSegments = new LinkedList<LineSegment>();
-		while (!lineSegments.isEmpty()) {
-			LinkedList<LineSegment> pivotList = new LinkedList<LineSegment>();
-			pivotList.add(lineSegments.getFirst());
-			lineSegments.removeFirst();
-			for (boolean found = true; found;) {
-				found = false;
-				for (Iterator<LineSegment> it = lineSegments.iterator(); it.hasNext();) {
-					LineSegment ls = it.next();
-					if (ls.start == pivotList.getLast().end) {
-						pivotList.addLast(ls);
-						it.remove();
-						found = true;
-					} else if (ls.end == pivotList.getFirst().start) {
-						pivotList.addFirst(ls);
-						it.remove();
-						found = true;
-					}
-				}
-			}
-			sortedLineSegments.addAll(pivotList);
-		}
-		
-		Track t = new Track();
-		for (LineSegment ls : sortedLineSegments)
-			t.segments.add(ls);
-		mv.editLayer().add(new AddCommand(Main.main.ds, t));
-		Main.main.ds.clearSelection();
-		mv.repaint();
-	}
-}
Index: /src/org/openstreetmap/josm/actions/mapmode/AddWayAction.java
===================================================================
--- /src/org/openstreetmap/josm/actions/mapmode/AddWayAction.java	(revision 61)
+++ /src/org/openstreetmap/josm/actions/mapmode/AddWayAction.java	(revision 61)
@@ -0,0 +1,123 @@
+package org.openstreetmap.josm.actions.mapmode;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.AddCommand;
+import org.openstreetmap.josm.data.osm.LineSegment;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Track;
+import org.openstreetmap.josm.gui.MapFrame;
+
+/**
+ * Add a new track from all selected line segments.
+ *
+ * If there is a selection when the mode is entered, all line segments in this
+ * selection form a new track, except the user holds down Shift.
+ *
+ * The user can click on a line segment. If he holds down Shift, no track is 
+ * created yet. If he holds down Alt, the whole track is considered instead of 
+ * the clicked line segment. If the user holds down Ctrl, no track is created 
+ * and the clicked line segment get removed from the list.
+ *
+ * Also, the user may select a rectangle as in selection mode. No node, area or
+ * track can be selected this way.
+ *
+ * @author imi
+ *
+ */
+public class AddWayAction extends MapMode {
+
+	private MapMode followMode;
+	
+	/**
+	 * Create a new AddWayAction.
+	 * @param mapFrame The MapFrame this action belongs to.
+	 * @param followMode The mode to go into when finished creating a way.
+	 */
+	public AddWayAction(MapFrame mapFrame, MapMode followMode) {
+		super("Add Way", "addway", "Combine line segments to a new way.", KeyEvent.VK_W, mapFrame);
+		this.followMode = followMode;
+	}
+
+	@Override
+	public void actionPerformed(ActionEvent e) {
+		makeTrack();
+		super.actionPerformed(e);
+		mapFrame.selectMapMode(followMode);
+	}
+
+	/**
+	 * Just make a track of all selected items.
+	 */
+	private void makeTrack() {
+		Collection<OsmPrimitive> selection = Main.main.ds.getSelected();
+		if (selection.isEmpty())
+			return;
+
+		// form a new track
+		LinkedList<LineSegment> lineSegments = new LinkedList<LineSegment>();
+		int numberOfSelectedWays = 0;
+		for (OsmPrimitive osm : selection) {
+			if (osm instanceof Track)
+				numberOfSelectedWays++;
+			else if (osm instanceof LineSegment)
+				lineSegments.add((LineSegment)osm);
+		}
+		
+		if (numberOfSelectedWays > 0) {
+			String ways = "way" + (numberOfSelectedWays==1?"":"s");
+			int answer = JOptionPane.showConfirmDialog(Main.main, numberOfSelectedWays+" "+ways+" have been selected.\n" +
+					"Do you wish to select all segments belonging to the "+ways+" instead?");
+			if (answer == JOptionPane.CANCEL_OPTION)
+				return;
+			if (answer == JOptionPane.YES_OPTION) {
+				for (OsmPrimitive osm : selection)
+					if (osm instanceof Track)
+						lineSegments.addAll(((Track)osm).segments);
+			}
+		}
+		
+		// sort the line segments in best possible order. This is done by:
+		// 0  if no elements in list, quit
+		// 1  taking the first ls as pivot, remove it from list
+		// 2  searching for a connection at start or end of pivot
+		// 3  if found, attach it, remove it from list, goto 2
+		// 4  if not found, save the pivot-string and goto 0
+		LinkedList<LineSegment> sortedLineSegments = new LinkedList<LineSegment>();
+		while (!lineSegments.isEmpty()) {
+			LinkedList<LineSegment> pivotList = new LinkedList<LineSegment>();
+			pivotList.add(lineSegments.getFirst());
+			lineSegments.removeFirst();
+			for (boolean found = true; found;) {
+				found = false;
+				for (Iterator<LineSegment> it = lineSegments.iterator(); it.hasNext();) {
+					LineSegment ls = it.next();
+					if (ls.start == pivotList.getLast().end) {
+						pivotList.addLast(ls);
+						it.remove();
+						found = true;
+					} else if (ls.end == pivotList.getFirst().start) {
+						pivotList.addFirst(ls);
+						it.remove();
+						found = true;
+					}
+				}
+			}
+			sortedLineSegments.addAll(pivotList);
+		}
+		
+		Track t = new Track();
+		for (LineSegment ls : sortedLineSegments)
+			t.segments.add(ls);
+		mv.editLayer().add(new AddCommand(Main.main.ds, t));
+		Main.main.ds.clearSelection();
+		mv.repaint();
+	}
+}
Index: /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 60)
+++ /src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 61)
@@ -145,3 +145,7 @@
 		keys.put(key, value);
 	}
+	
+	public String get(Key key) {
+		return (keys == null) ? null : keys.get(key);
+	}
 }
Index: /src/org/openstreetmap/josm/gui/MapFrame.java
===================================================================
--- /src/org/openstreetmap/josm/gui/MapFrame.java	(revision 60)
+++ /src/org/openstreetmap/josm/gui/MapFrame.java	(revision 61)
@@ -17,5 +17,5 @@
 import org.openstreetmap.josm.actions.mapmode.AddLineSegmentAction;
 import org.openstreetmap.josm.actions.mapmode.AddNodeAction;
-import org.openstreetmap.josm.actions.mapmode.AddTrackAction;
+import org.openstreetmap.josm.actions.mapmode.AddWayAction;
 import org.openstreetmap.josm.actions.mapmode.DeleteAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
@@ -81,9 +81,10 @@
 		toolBarActions.setFloatable(false);
 		toolBarActions.add(new IconToggleButton(this, new ZoomAction(this)));
-		toolBarActions.add(new IconToggleButton(this, new SelectionAction(this)));
+		final SelectionAction selectionAction = new SelectionAction(this);
+		toolBarActions.add(new IconToggleButton(this, selectionAction));
 		toolBarActions.add(new IconToggleButton(this, new MoveAction(this)));
 		toolBarActions.add(new IconToggleButton(this, new AddNodeAction(this)));
 		toolBarActions.add(new IconToggleButton(this, new AddLineSegmentAction(this)));
-		toolBarActions.add(new IconToggleButton(this, new AddTrackAction(this)));
+		toolBarActions.add(new IconToggleButton(this, new AddWayAction(this, selectionAction)));
 		toolBarActions.add(new IconToggleButton(this, new DeleteAction(this)));
 
Index: /src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java
===================================================================
--- /src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(revision 60)
+++ /src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java	(revision 61)
@@ -3,4 +3,5 @@
 import java.awt.BorderLayout;
 import java.awt.Dimension;
+import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -9,8 +10,11 @@
 import java.awt.event.MouseEvent;
 import java.util.Collection;
+import java.util.Map.Entry;
 
 import javax.swing.DefaultListModel;
 import javax.swing.JButton;
 import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.ListSelectionModel;
@@ -18,4 +22,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.SelectionChangedListener;
+import org.openstreetmap.josm.data.osm.Key;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.ImageProvider;
@@ -61,4 +66,6 @@
 		add(new JScrollPane(displaylist), BorderLayout.CENTER);
 
+		JPanel buttonPanel = new JPanel(new GridLayout(1,2));
+		
 		JButton button = new JButton("Select", ImageProvider.get("mapmode", "selection"));
 		button.setToolTipText("Set the selected elements on the map to the selected items in the list above.");
@@ -68,6 +75,31 @@
 			}
 		});
-		add(button, BorderLayout.SOUTH);
+		buttonPanel.add(button);
 
+		button = new JButton("Search", ImageProvider.get("dialogs", "search"));
+		button.setToolTipText("Search for objects.");
+		button.addActionListener(new ActionListener(){
+			public void actionPerformed(ActionEvent e) {
+				String search = JOptionPane.showInputDialog(Main.main, "Please enter a search string", "Search", JOptionPane.INFORMATION_MESSAGE);
+				if (search == null)
+					return;
+				Main.main.ds.clearSelection();
+				for (OsmPrimitive osm : Main.main.ds.allNonDeletedPrimitives()) {
+					if (osm.keys != null) {
+						for (Entry<Key, String> ent : osm.keys.entrySet()) {
+							if (search.indexOf(ent.getKey().name) != -1 || search.indexOf(ent.getValue()) != -1) {
+								osm.setSelected(true);
+								break;
+							}
+						}
+					}
+				}
+				selectionChanged(Main.main.ds.getSelected());
+				Main.main.getMapFrame().repaint();
+			}
+		});
+		buttonPanel.add(button);
+		
+		add(buttonPanel, BorderLayout.SOUTH);
 		selectionChanged(Main.main.ds.getSelected());
 	}
Index: /src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- /src/org/openstreetmap/josm/io/OsmReader.java	(revision 60)
+++ /src/org/openstreetmap/josm/io/OsmReader.java	(revision 61)
@@ -16,4 +16,5 @@
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 
 import uk.co.wilson.xml.MinML2;
@@ -93,6 +94,10 @@
 				readCommon(atts);
 			} else if (qName.equals("seg")) {
-				if (current instanceof Track)
-					((Track)current).segments.add(lineSegments.get(getLong(atts, "id")));
+				if (current instanceof Track) {
+					LineSegment ls = lineSegments.get(getLong(atts, "id"));
+					if (ls == null)
+						fatalError(new SAXParseException("Line segment "+getLong(atts, "id")+"has not been transfered before.", null));
+					((Track)current).segments.add(ls);
+				}
 			} else if (qName.equals("tag")) {
 				current.put(Key.get(atts.getValue("k")), atts.getValue("v"));
@@ -102,4 +107,12 @@
 		} catch (NullPointerException x) {
 			throw new SAXException("NullPointerException. Possible some missing tags.", x);
+		}
+	}
+
+	
+	@Override
+	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+		if (qName.equals("node") || qName.equals("segment") || qName.equals("way") || qName.equals("area")) {
+			current.visit(adder);
 		}
 	}
@@ -122,11 +135,4 @@
 	}
 
-	@Override
-	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
-		if (qName.equals("node") || qName.equals("segment") || qName.equals("way") || qName.equals("area")) {
-			current.visit(adder);
-		}
-	}
-
 	private double getDouble(Attributes atts, String value) {
 		return Double.parseDouble(atts.getValue(value));
Index: /src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 60)
+++ /src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 61)
@@ -6,4 +6,5 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.io.StringWriter;
 import java.io.Writer;
 import java.net.HttpURLConnection;
@@ -166,4 +167,8 @@
 				OsmWriter.outputSingle(out, osm, true);
 				out.close();
+				
+				StringWriter o = new StringWriter();
+				OsmWriter.outputSingle(o, osm, true);
+				System.out.println(o.getBuffer().toString());
 			}
 
@@ -172,5 +177,8 @@
 				osm.id = readId(con.getInputStream());
 			System.out.println("got return: "+retCode+" with id "+osm.id);
+			String retMsg = con.getResponseMessage();
 			con.disconnect();
+			if (retCode != 200)
+				throw new RuntimeException(retCode+" "+retMsg);
 		} catch (UnknownHostException e) {
 			throw new RuntimeException("Unknown host: "+e.getMessage(), e);
Index: /src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- /src/org/openstreetmap/josm/io/OsmWriter.java	(revision 60)
+++ /src/org/openstreetmap/josm/io/OsmWriter.java	(revision 61)
@@ -13,4 +13,5 @@
 import org.openstreetmap.josm.data.osm.Track;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.xml.sax.SAXException;
 
 /**
@@ -20,4 +21,12 @@
  */
 public class OsmWriter implements Visitor {
+
+	private class RuntimeEncodingException extends RuntimeException {
+		public RuntimeEncodingException(Throwable t) {
+			super(t);
+		}
+		public RuntimeEncodingException() {
+		}
+	}
 
 	/**
@@ -38,4 +47,16 @@
 	private final boolean osmConform;
 
+	private final 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;");
+	}
+	
 	/**
 	 * Output the data to the stream
@@ -46,4 +67,5 @@
 	public static void output(Writer 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)
@@ -56,11 +78,16 @@
 	}
 
-	public static void outputSingle(Writer out, OsmPrimitive osm, boolean osmConform) {
-		OsmWriter writer = new OsmWriter(out, osmConform);
-		writer.out.println("<osm version='0.3' generator='JOSM'>");
-		osm.visit(writer);
-		writer.out.println("</osm>");
+	public static void outputSingle(Writer out, OsmPrimitive osm, boolean osmConform) throws SAXException {
+		try {
+			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'>");
+			osm.visit(writer);
+			writer.out.println("</osm>");
+		} catch (RuntimeEncodingException e) {
+			throw new SAXException("Your Java installation does not support the required UTF-8 encoding", (Exception)e.getCause());
+		}		
 	}
-	
+
 	private OsmWriter(Writer out, boolean osmConform) {
 		if (out instanceof PrintWriter)
@@ -70,5 +97,5 @@
 		this.osmConform = osmConform;
 	}
-	
+
 	public void visit(Node n) {
 		addCommon(n, "node");
@@ -112,11 +139,31 @@
 				out.println(">");
 			for (Entry<Key, String> e : osm.keys.entrySet())
-				out.println("    <tag k='"+e.getKey().name+"' v='"+e.getValue()+"' />");
-			out.println("  </"+tagname+">");
+				out.println("    <tag k='"+ encode(e.getKey().name) +
+						"' v='"+encode(e.getValue())+ "' />");
+			out.println("  </" + tagname + ">");
 		} else if (tagOpen)
 			out.println(" />");
 		else
-			out.println("  </"+tagname+">");
+			out.println("  </" + tagname + ">");
 	}
+
+	/**
+	 * Encode the given string in XML1.0 format.
+	 * Optimized to fast pass strings that don't need encoding (normal case).
+	 */
+	public String encode(String unencoded) {
+		StringBuilder buffer = null;
+		for (int i = 0; i < unencoded.length(); ++i) {
+			String encS = 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();
+	}
+
 
 	/**
@@ -140,3 +187,2 @@
 	}
 }
-
Index: /test/org/openstreetmap/josm/test/OsmWriterTest.java
===================================================================
--- /test/org/openstreetmap/josm/test/OsmWriterTest.java	(revision 60)
+++ /test/org/openstreetmap/josm/test/OsmWriterTest.java	(revision 61)
@@ -10,4 +10,5 @@
 import junit.framework.TestCase;
 
+import org.jdom.Attribute;
 import org.jdom.Element;
 import org.jdom.JDOMException;
@@ -57,4 +58,18 @@
 	}
 
+	@Bug(59)
+	public void testSpecialChars() throws Exception {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 32; i < 0xd800; ++i)
+			sb.append((char)i);
+		String s = sb.toString();
+		n1.put(Key.get(s), s);
+		reparse();
+		assertEquals(1, nodes.get(0).getChildren().size());
+		Attribute key = ((Element)nodes.get(0).getChildren().get(0)).getAttribute("k");
+		assertEquals(s, key.getValue());
+		Attribute value = ((Element)nodes.get(0).getChildren().get(0)).getAttribute("v");
+		assertEquals(s, value.getValue());
+	}
 	
 	public void testLineSegment() throws Exception {
@@ -110,5 +125,5 @@
 	 */
 	@Bug(47)
-	public void testDeleteNewDoesReallyRemove() throws IOException, JDOMException {
+	public void testDeleteNewDoesReallyRemove() throws Exception {
 		ds.tracks.iterator().next().setDeleted(true);
 		reparse();
@@ -120,5 +135,5 @@
 	 * Verify that action tag is set correctly.
 	 */
-	public void testActionTag() throws IOException, JDOMException {
+	public void testActionTag() throws Exception {
 		int id = 1;
 		for (OsmPrimitive osm : ds.allPrimitives())
