Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 1523)
@@ -304,6 +304,7 @@
                     Main.pref.put("gui.geometry", geometry);
                 }
-            } else
+            } else {
                 System.out.println("Ignoring malformed geometry: "+geometry);
+            }
         }
         if (bounds == null)
@@ -432,8 +433,8 @@
     {
         String full = Locale.getDefault().toString();
-        if(full.equals("iw_IL"))
+        if (full.equals("iw_IL"))
             return "he";
         /* list of non-single codes supported by josm */
-        else if(full.equals("en_GB"))
+        else if (full.equals("en_GB"))
             return full;
         return Locale.getDefault().getLanguage();
@@ -444,5 +445,5 @@
         String newGeometry = "";
         try {
-            if(((JFrame)parent).getExtendedState() == JFrame.NORMAL) {
+            if (((JFrame)parent).getExtendedState() == JFrame.NORMAL) {
                 Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize();
                 Rectangle bounds = parent.getBounds();
@@ -451,11 +452,11 @@
                 int x = (int)bounds.getX();
                 int y = (int)bounds.getY();
-                if(width > screenDimension.width)
+                if (width > screenDimension.width)
                     width = screenDimension.width;
-                if(height > screenDimension.height)
+                if (height > screenDimension.height)
                     width = screenDimension.height;
-                if(x < 0)
+                if (x < 0)
                     x = 0;
-                if(y < 0)
+                if (y < 0)
                     y = 0;
                 newGeometry = width + "x" + height + "+" + x + "+" + y;
Index: trunk/src/org/openstreetmap/josm/actions/CopyAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CopyAction.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/CopyAction.java	(revision 1523)
@@ -8,8 +8,8 @@
 import java.awt.event.KeyEvent;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.LinkedList;
-import java.util.Collection;
 
 import javax.swing.JOptionPane;
@@ -19,10 +19,10 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -65,5 +65,5 @@
         /* scan the selected objects, mapping them to copies; when copying a way or relation,
          * the copy references the copies of their child objects */
-        new Visitor(){
+        new AbstractVisitor() {
             public void visit(Node n) {
                 /* check if already in pasteBuffer - e.g. two ways are selected which share a node;
Index: trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/HistoryInfoAction.java	(revision 1523)
@@ -12,9 +12,9 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -31,5 +31,5 @@
     public void actionPerformed(ActionEvent e) {
         final Collection<Object> sel = new LinkedList<Object>();
-        new Visitor() {
+        new AbstractVisitor() {
             public void visit(Node n) {
                 if(n.id <= 0) return;
Index: trunk/src/org/openstreetmap/josm/actions/ReverseWayAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ReverseWayAction.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/ReverseWayAction.java	(revision 1523)
@@ -19,9 +19,9 @@
 import org.openstreetmap.josm.corrector.UserCancelException;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -35,5 +35,5 @@
     public void actionPerformed(ActionEvent e) {
         final Collection<Way> sel = new LinkedList<Way>();
-        new Visitor() {
+        new AbstractVisitor() {
             public void visit(Node n) {
             }
Index: trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java	(revision 1523)
@@ -6,8 +6,9 @@
 import java.awt.event.ActionEvent;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 
 import javax.swing.JFileChooser;
@@ -18,9 +19,9 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.GpxLayer;
+import org.openstreetmap.josm.io.GpxWriter;
 import org.openstreetmap.josm.io.OsmWriter;
-import org.openstreetmap.josm.io.GpxWriter;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -159,5 +160,10 @@
                     copy(file, tmpFile);
                 }
-                OsmWriter.output(new FileOutputStream(file), new OsmWriter.All(layer.data, false));
+                OsmWriter w = new OsmWriter(new PrintWriter(new FileOutputStream(file)), false, layer.data.version);
+                w.header();
+                w.writeDataSources(layer.data);
+                w.writeContent(layer.data);
+                w.footer();
+                // FIXME - how to close?
                 if (!Main.pref.getBoolean("save.keepbackup") && (tmpFile != null))
                     tmpFile.delete();
Index: trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/SplitWayAction.java	(revision 1523)
@@ -31,4 +31,5 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
@@ -74,5 +75,5 @@
         selectedNodes = null;
 
-        Visitor splitVisitor = new Visitor(){
+        Visitor splitVisitor = new AbstractVisitor() {
             public void visit(Node n) {
                 if (selectedNodes == null)
Index: trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 1523)
@@ -154,5 +154,5 @@
         PleaseWaitRunnable uploadTask = new PleaseWaitRunnable(tr("Uploading data")){
             @Override protected void realRun() throws SAXException {
-                server.uploadOsm(all);
+                server.uploadOsm(Main.ds.version, all);
             }
             @Override protected void finish() {
@@ -160,5 +160,5 @@
             }
             @Override protected void cancel() {
-                server.cancel();
+                // FIXME server.cancel();
             }
         };
Index: trunk/src/org/openstreetmap/josm/command/Command.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/Command.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/command/Command.java	(revision 1523)
@@ -12,9 +12,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
@@ -33,5 +33,5 @@
 abstract public class Command {
 
-   private static final class CloneVisitor implements Visitor {
+   private static final class CloneVisitor extends AbstractVisitor {
       public Map<OsmPrimitive, OsmPrimitive> orig = new HashMap<OsmPrimitive, OsmPrimitive>();
 
Index: trunk/src/org/openstreetmap/josm/data/osm/Changeset.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 1523)
@@ -2,13 +2,6 @@
 package org.openstreetmap.josm.data.osm;
 
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.io.XmlWriter;
-import org.openstreetmap.josm.io.XmlWriter.OsmWriterInterface;
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.HashMap;
-import java.util.Collections;
-import java.util.Collection;
+import org.openstreetmap.josm.data.osm.visitor.Visitor;
+
 
 
@@ -19,18 +12,5 @@
  *
  */
-public final class Changeset /*extends OsmPrimitive*/ implements OsmWriterInterface {
-    /**
-     * The key/value list for this primitive.
-     */
-    public Map<String, String> keys;
-
-    public long id = 0;
-
-    /**
-     * User that created this changeset, as specified by the server.
-     * Never changed by JOSM.
-     */
-    public User user = null;
-
+public final class Changeset extends OsmPrimitive {
     /**
      * Time of last modification to this object. This is not set by JOSM but
@@ -45,77 +25,13 @@
     public String start_timestamp = null;
 
-        private void addTags(PrintWriter out) {
-                if (this.keys != null) {
-                        for (Entry<String, String> e : this.keys.entrySet())
-                                out.println("    <tag k='"+ XmlWriter.encode(e.getKey()) +
-                                                "' v='"+XmlWriter.encode(e.getValue())+ "' />");
-                }
-        }
-
-    public final void header(PrintWriter out) {
-            out.print("<osm version='");
-                out.print(Main.pref.get("osm-server.version", "0.6"));
-                out.println("' generator='JOSM'>");
-    }
-    public final void write(PrintWriter out) {
-        out.print("  <changeset");
-        if (id != 0)
-            out.print(" id="+id);
-        if (this.user != null) {
-            out.print(" user='"+XmlWriter.encode(this.user.name)+"'");
-        }
-        out.println(">\n");
-        addTags( out );
-        out.println("  </changeset>");
-    }
-    public final void footer(PrintWriter out) {
-        out.println("</osm>");
+    public void visit(Visitor v) {
+        v.visit(this);
     }
 
-    /******************************************************
-     * This tag stuff is copied from OsmPrimitive. Perhaps a changeset
-     * really is a primitive, but it's not right now. Perhaps it should
-     * be...
-     ******************************************************/
-
-    /**
-     * Set the given value to the given key
-     * @param key The key, for which the value is to be set.
-     * @param value The value for the key.
-     */
-    public final void put(String key, String value) {
-        if (value == null)
-            remove(key);
-        else {
-            if (keys == null)
-                keys = new HashMap<String, String>();
-            keys.put(key, value);
-        }
+    public int compareTo(OsmPrimitive arg0) {
+        if (arg0 instanceof Changeset) return Long.valueOf(id).compareTo(arg0.id);
+        return 1;
     }
-    /**
-     * Remove the given key from the list.
-     */
-    public final void remove(String key) {
-        if (keys != null) {
-            keys.remove(key);
-            if (keys.isEmpty())
-                keys = null;
-        }
-    }
-
-    public final String get(String key) {
-        return keys == null ? null : keys.get(key);
-    }
-
-    public final Collection<Entry<String, String>> entrySet() {
-        if (keys == null)
-            return Collections.emptyList();
-        return keys.entrySet();
-    }
-
-    public final Collection<String> keySet() {
-        if (keys == null)
-            return Collections.emptyList();
-        return keys.keySet();
-    }
+    
+    
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 1523)
@@ -24,4 +24,9 @@
 public class DataSet implements Cloneable {
 
+    /**
+     * The API version that created this data set, if any.
+     */
+    public String version;
+    
     /**
      * All nodes goes here, even when included in other data (ways etc). This enables the instant
@@ -209,4 +214,5 @@
         for (DataSource source : dataSources)
             ds.dataSources.add(new DataSource(source.bounds, source.origin));
+        ds.version = version;
         return ds;
     }
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 1523)
@@ -211,7 +211,8 @@
         final int[] ret = new int[1];
         Visitor v = new Visitor(){
-            public void visit(Node n) { ret[0] = 1; }
-            public void visit(Way w) { ret[0] = 2; }
-            public void visit(Relation e) { ret[0] = 3; }
+            public void visit(Node n) { ret[0] = 0; }
+            public void visit(Way w) { ret[0] = 1; }
+            public void visit(Relation e) { ret[0] = 2; }
+            public void visit(Changeset cs) { ret[0] = 3; }
         };
         visit(v);
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/AbstractVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/AbstractVisitor.java	(revision 1523)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/AbstractVisitor.java	(revision 1523)
@@ -0,0 +1,18 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.visitor;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+
+/**
+ * This class serves as a base class for most simple visitors,
+ * blocking out the "changeset" visit so as to avoid cluttering 
+ * the visitors which are not interested.
+ * 
+ * @author fred
+ */
+public abstract class AbstractVisitor implements Visitor {
+
+    public void visit(Changeset cs) {
+        // do nothing
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/AddVisitor.java	(revision 1523)
@@ -14,5 +14,5 @@
  * @author imi
  */
-public class AddVisitor implements Visitor {
+public class AddVisitor extends AbstractVisitor {
 
     protected final DataSet ds;
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/AllNodesVisitor.java	(revision 1523)
@@ -16,5 +16,5 @@
  * @author imi
  */
-public class AllNodesVisitor implements Visitor {
+public class AllNodesVisitor extends AbstractVisitor {
 
     /**
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java	(revision 1523)
@@ -17,5 +17,5 @@
  * @author imi
  */
-public class BoundingXYVisitor implements Visitor {
+public class BoundingXYVisitor extends AbstractVisitor {
 
     public EastNorth min, max;
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java	(revision 1523)
@@ -19,5 +19,5 @@
  * @author imi
  */
-public class CollectBackReferencesVisitor implements Visitor {
+public class CollectBackReferencesVisitor extends AbstractVisitor {
 
     private final DataSet ds;
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/CreateOsmChangeVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/CreateOsmChangeVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/CreateOsmChangeVisitor.java	(revision 1523)
@@ -6,5 +6,4 @@
 import java.util.Map;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.Node;
@@ -12,4 +11,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmWriter;
 
@@ -22,70 +22,62 @@
  *
  */
-public class CreateOsmChangeVisitor implements Visitor {
+public class CreateOsmChangeVisitor extends AbstractVisitor {
 
-    StringBuffer document;
-    String currentMode;
-    Changeset changeset;
-    PrintWriter writer;
-    StringWriter swriter;
-    OsmWriter osmwriter;
+    private String currentMode;
+    private PrintWriter writer;
+    private StringWriter swriter;
+    private OsmWriter osmwriter;
+    private OsmApi api;
 
-    public CreateOsmChangeVisitor(Changeset changeset) {
+    public CreateOsmChangeVisitor(Changeset changeset, OsmApi api) {
         writer = new PrintWriter(swriter = new StringWriter());
         writer.write("<osmChange version=\"");
-        writer.write(Main.pref.get("osm-server.version", "0.6"));
+        writer.write(api.getVersion());
         writer.write("\" generator=\"JOSM\">\n");
-        this.changeset = changeset;
-        osmwriter = new OsmWriter(writer, false, changeset);
+        this.api = api;
+        // need to set osmConform = false here so that negative IDs get transmitted.
+        // this also enables unnecessary and (if the API were more strict) potentially
+        // harmful action="..." attributes. 
+        osmwriter = new OsmWriter(writer, false, api.getVersion());
+        osmwriter.setChangeset(changeset);
     }
 
+    // FIXME: This should really NOT use a visitor pattern, it looks
+    // stupid. Just have one method named "write" instead of three "visit"s.
+    
     public void visit(Node n) {
         if (n.deleted) {
             switchMode("delete");
-            writer.write("<node id=\"");
-            writer.write(Long.toString(n.id));
-            writer.write("\" version=\"");
-            writer.write(Long.toString(n.version));
-            writer.write("\" changeset=\"");
-            writer.write(Long.toString(changeset.id));
-            writer.write("\" />\n");
+            osmwriter.setWithBody(false);
+            osmwriter.visit(n);
         } else {
             switchMode((n.id == 0) ? "create" : "modify");
-            n.visit(osmwriter);
+            osmwriter.setWithBody(true);
+            osmwriter.visit(n);
         }
     }
-
     public void visit(Way w) {
         if (w.deleted) {
             switchMode("delete");
-            writer.write("<way id=\"");
-            writer.write(Long.toString(w.id));
-            writer.write("\" version=\"");
-            writer.write(Long.toString(w.version));
-            writer.write("\" changeset=\"");
-            writer.write(Long.toString(changeset.id));
-            writer.write("\" />\n");
+            osmwriter.setWithBody(false);
+            osmwriter.visit(w);
         } else {
             switchMode((w.id == 0) ? "create" : "modify");
-            w.visit(osmwriter);
+            osmwriter.setWithBody(true);
+            osmwriter.visit(w);
         }
     }
-
     public void visit(Relation r) {
         if (r.deleted) {
             switchMode("delete");
-            writer.write("<relation id=\"");
-            writer.write(Long.toString(r.id));
-            writer.write("\" version=\"");
-            writer.write(Long.toString(r.version));
-            writer.write("\" changeset=\"");
-            writer.write(Long.toString(changeset.id));
-            writer.write("\" />\n");
+            osmwriter.setWithBody(false);
+            osmwriter.visit(r);
         } else {
             switchMode((r.id == 0) ? "create" : "modify");
-            r.visit(osmwriter);
+            osmwriter.setWithBody(true);
+            osmwriter.visit(r);
         }
     }
-
+    
     private void switchMode(String newMode) {
         if ((newMode != null && !newMode.equals(currentMode))||(newMode == null && currentMode != null)) {
@@ -99,5 +91,5 @@
                 writer.write(newMode);
                 writer.write(" version=\"");
-                writer.write(Main.pref.get("osm-server.version", "0.6"));
+                writer.write(api.getVersion());
                 writer.write("\" generator=\"JOSM\">\n");
             }
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/DeleteVisitor.java	(revision 1523)
@@ -14,5 +14,5 @@
  * @author imi
  */
-public class DeleteVisitor implements Visitor {
+public class DeleteVisitor extends AbstractVisitor {
 
     private final DataSet ds;
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/MapPaintVisitor.java	(revision 1523)
@@ -16,14 +16,15 @@
 import java.awt.Stroke;
 import java.awt.geom.GeneralPath;
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.Locale;
-import java.util.Iterator;
 
 import javax.swing.ImageIcon;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
@@ -32,7 +33,4 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
-import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.gui.mappaint.AreaElemStyle;
 import org.openstreetmap.josm.gui.mappaint.ElemStyle;
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java	(revision 1523)
@@ -22,5 +22,5 @@
  * @author imi
  */
-public class MergeVisitor implements Visitor {
+public class MergeVisitor extends AbstractVisitor {
 
     /**
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/NameVisitor.java	(revision 1523)
@@ -20,5 +20,5 @@
  * @author imi
  */
-public class NameVisitor implements Visitor {
+public class NameVisitor extends AbstractVisitor {
 
     /**
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/SimplePaintVisitor.java	(revision 1523)
@@ -33,5 +33,5 @@
  * @author imi
  */
-public class SimplePaintVisitor implements Visitor {
+public class SimplePaintVisitor extends AbstractVisitor {
 
     public final static Color darkerblue = new Color(0,0,96);
Index: trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/data/osm/visitor/Visitor.java	(revision 1523)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.data.osm.visitor;
 
+import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Node;
@@ -16,3 +17,4 @@
     void visit(Way w);
     void visit(Relation e);
+    void visit(Changeset cs);
 }
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 1523)
@@ -3,5 +3,4 @@
 package org.openstreetmap.josm.gui;
 
-import org.xnap.commons.i18n.I18nFactory;
 import static org.openstreetmap.josm.tools.I18n.i18n;
 import static org.openstreetmap.josm.tools.I18n.tr;
@@ -26,4 +25,5 @@
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.xnap.commons.i18n.I18nFactory;
 
 /**
@@ -94,6 +94,6 @@
         String localeName = null; // The locale to use
 
-        //Check if passed as parameter
-        if(args.containsKey("language"))
+        // Check if passed as parameter
+        if (args.containsKey("language"))
             localeName = (String)(args.get("language").toArray()[0]);
 
@@ -104,5 +104,5 @@
             Locale l;
             Locale d = Locale.getDefault();
-            if(localeName.equals("he")) localeName = "iw_IL";
+            if (localeName.equals("he")) localeName = "iw_IL";
             int i = localeName.indexOf('_');
             if (i > 0) {
@@ -115,12 +115,9 @@
                 i18n = I18nFactory.getI18n(MainApplication.class);
             } catch (MissingResourceException ex) {
-                if(!l.getLanguage().equals("en"))
-                {
+                if (!l.getLanguage().equals("en")) {
                     System.out.println(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
                     l.getDisplayName(), d.getDisplayName()));
                     Locale.setDefault(d);
-                }
-                else
-                {
+                } else {
                     i18n = null;
                 }
@@ -165,4 +162,5 @@
         splash.setStatus(tr("Setting defaults"));
         preConstructorInit(args);
+        removeObsoletePreferences();
         splash.setStatus(tr("Creating main GUI"));
         JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
@@ -181,5 +179,5 @@
             mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
 
-        EventQueue.invokeLater(new Runnable(){
+        EventQueue.invokeLater(new Runnable() {
             public void run() {
                 main.postConstructorProcessCmdLine(args);
@@ -188,3 +186,23 @@
     }
 
-}
+    /**
+     * Removes obsolete preference settings. If you throw out a once-used preference
+     * setting, add it to the list here with an expiry date (written as comment). If you
+     * see something with an expiry date in the past, remove it from the list.
+     */
+    public static void removeObsoletePreferences() {
+        String[] obsolete = {
+           "sample.preference.that.does.not.exist", // sample comment, expiry date should go here
+           "osm-server.version", // remove this around 10/2009
+           "osm-server.additional-versions", // remove this around 10/2009
+           null
+        };
+        for (String i : obsolete) {
+            if (i == null) continue;
+            if (Main.pref.hasKey(i)) {
+                Main.pref.removeFromCollection(i, Main.pref.get(i));
+                System.out.println(tr("Preference setting {0} has been removed since it is no longer used.", i));
+            }
+        }
+    }     
+ }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictDialog.java	(revision 1523)
@@ -2,6 +2,6 @@
 package org.openstreetmap.josm.gui.dialogs;
 
+import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import java.awt.BorderLayout;
@@ -22,5 +22,4 @@
 import javax.swing.DefaultListModel;
 import javax.swing.JList;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
@@ -33,9 +32,10 @@
 import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
 import org.openstreetmap.josm.gui.ConflictResolver;
@@ -171,5 +171,5 @@
             return;
         g.setColor(preferencesColor);
-        Visitor conflictPainter = new Visitor(){
+        Visitor conflictPainter = new AbstractVisitor(){
             public void visit(Node n) {
                 Point p = nc.getPoint(n.eastNorth);
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1523)
@@ -21,4 +21,5 @@
 import java.awt.image.BufferedImage;
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -26,5 +27,4 @@
 import java.util.LinkedList;
 import java.util.Set;
-import java.util.ArrayList;
 
 import javax.swing.AbstractAction;
@@ -43,18 +43,18 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
-import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
 import org.openstreetmap.josm.data.osm.visitor.MapPaintVisitor;
 import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
 import org.openstreetmap.josm.data.osm.visitor.SimplePaintVisitor;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
-import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
@@ -73,5 +73,5 @@
 public class OsmDataLayer extends Layer {
 
-    public final static class DataCountVisitor implements Visitor {
+    public final static class DataCountVisitor extends AbstractVisitor {
         public final int[] normal = new int[3];
         public final int[] deleted = new int[3];
@@ -215,4 +215,5 @@
         tool += undeletedSize(data.nodes)+" "+trn("node", "nodes", undeletedSize(data.nodes))+", ";
         tool += undeletedSize(data.ways)+" "+trn("way", "ways", undeletedSize(data.ways));
+        if (data.version != null) tool += ", " + tr("version {0}", data.version);
         if (associatedFile != null)
             tool = "<html>"+tool+"<br>"+associatedFile.getPath()+"</html>";
@@ -236,4 +237,14 @@
         for (DataSource src : ((OsmDataLayer)from).data.dataSources)
             data.dataSources.add(src);
+        
+        // copy the merged layer's API version, downgrade if required
+        if (data.version == null) {
+            data.version = ((OsmDataLayer)from).data.version;
+        } else {
+            if ("0.5".equals(data.version) ^ "0.5".equals(((OsmDataLayer)from).data.version)) {
+                System.err.println("Warning: mixing 0.6 and 0.5 data results in version 0.5");
+                data.version = "0.5";
+            }
+        }
         fireDataChange();
         // repaint to make sure new data is displayed properly.
@@ -349,4 +360,6 @@
             p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
         }
+        p.add(new JLabel(tr("API version: {0}", (data.version != null) ? data.version : tr("unset"))));
+
         return p;
     }
@@ -447,6 +460,5 @@
     }
 
-    public boolean containsPoint(LatLon coor)
-    {
+    public boolean containsPoint(LatLon coor) {
         // we'll assume that if this has no data sources
         // that it also has no borders
Index: trunk/src/org/openstreetmap/josm/io/DiffResultReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/DiffResultReader.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/DiffResultReader.java	(revision 1523)
@@ -5,6 +5,5 @@
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.io.StringReader;
 import java.util.Collection;
 import java.util.HashMap;
@@ -18,5 +17,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
+import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
 import org.openstreetmap.josm.gui.PleaseWaitDialog;
 import org.xml.sax.Attributes;
@@ -27,5 +26,5 @@
 /**
  */
-public class DiffResultReader implements Visitor {
+public class DiffResultReader extends AbstractVisitor {
 
     /**
@@ -65,5 +64,5 @@
      * Parse the given input source and return the dataset.
      */
-    public static void parseDiffResult(InputStream source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg)
+    public static void parseDiffResult(String source, Collection<OsmPrimitive> osm, Collection<OsmPrimitive> processed, Map<OsmPrimitive,Long> newIdMap, PleaseWaitDialog pleaseWaitDlg)
     throws SAXException, IOException {
 
@@ -71,5 +70,5 @@
        drr.processed = processed;
        drr.newIdMap = newIdMap;
-       InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
+       InputSource inputSource = new InputSource(new StringReader(source));
        try {
            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, drr.new Parser());
@@ -85,8 +84,8 @@
 
        for (OsmPrimitive p : osm) {
-           System.out.println("old: "+ p);
+           //System.out.println("old: "+ p);
            p.visit(drr);
-           System.out.println("new: "+ p);
-           System.out.println("");
+           //System.out.println("new: "+ p);
+           //System.out.println("");
        }
     }
Index: trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/MirroredInputStream.java	(revision 1523)
@@ -22,60 +22,47 @@
     InputStream fs = null;
 
-    public MirroredInputStream(String name) throws IOException
-    {
+    public MirroredInputStream(String name) throws IOException {
         this(name, null, -1L);
     }
 
-    public MirroredInputStream(String name, long maxTime) throws IOException
-    {
+    public MirroredInputStream(String name, long maxTime) throws IOException {
         this(name, null, maxTime);
     }
 
-    public MirroredInputStream(String name, String destDir, long maxTime) throws IOException
-    {
+    public MirroredInputStream(String name, String destDir, long maxTime) throws IOException {
         URL url;
         File file = null;
-        try
-        {
+        try {
             url = new URL(name);
-            if(url.getProtocol().equals("file"))
-            {
+            if (url.getProtocol().equals("file")) {
                 file = new File(name.substring("file:/".length()));
-                if(!file.exists())
+                if (!file.exists())
                     file = new File(name.substring("file://".length()));
+            } else {
+                file = checkLocal(url, destDir, maxTime);
             }
-            else
-                file = checkLocal(url, destDir, maxTime);
-        }
-        catch(java.net.MalformedURLException e)
-        {
-            if(name.startsWith("resource://"))
-            {
+        } catch (java.net.MalformedURLException e) {
+            if(name.startsWith("resource://")) {
                 fs = getClass().getResourceAsStream(
                 name.substring("resource:/".length()));
                 return;
             }
-            else
-                file = new File(name);
+            file = new File(name);
         }
-        if(file == null)
+        if (file == null)
             throw new IOException();
         fs = new FileInputStream(file);
     }
 
-    private File checkLocal(URL url, String destDir, long maxTime)
-    {
+    private File checkLocal(URL url, String destDir, long maxTime) {
         String localPath = Main.pref.get("mirror." + url);
         File file = null;
-        if(localPath != null && localPath.length() > 0)
-        {
+        if (localPath != null && localPath.length() > 0) {
             String[] lp = localPath.split(";");
             file = new File(lp[1]);
-            if(maxTime <= 0)
+            if (maxTime <= 0)
                 maxTime = Main.pref.getInteger("mirror.maxtime", 7*24*60*60);
-            if(System.currentTimeMillis() - Long.parseLong(lp[0]) < maxTime*1000)
-            {
-                if(file.exists())
-                {
+            if (System.currentTimeMillis() - Long.parseLong(lp[0]) < maxTime*1000) {
+                if(file.exists()) {
                     return file;
                 }
@@ -86,5 +73,5 @@
 
         File destDirFile = new File(destDir);
-        if(!destDirFile.exists() )
+        if (!destDirFile.exists())
             destDirFile.mkdirs();
 
@@ -93,6 +80,5 @@
         BufferedOutputStream bos = null;
         BufferedInputStream bis = null;
-        try
-        {
+        try {
             URLConnection conn = url.openConnection();
             conn.setConnectTimeout(5000);
@@ -101,35 +87,22 @@
             byte[] buffer = new byte[4096];
             int length;
-            while((length = bis.read(buffer)) > -1)
+            while ((length = bis.read(buffer)) > -1)
                 bos.write(buffer, 0, length);
-        }
-        catch(IOException ioe)
-        {
-            if(file != null)
+        } catch(IOException ioe) {
+            if (file != null)
                 return file;
-            else
-                return null;
-        }
-        finally
-        {
-            if(bis != null)
-            {
-                try
-                {
+            return null;
+        } finally {
+            if (bis != null) {
+                try {
                     bis.close();
-                }
-                catch (IOException e)
-                {
+                } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
-            if(bos != null)
-            {
-                try
-                {
+            if (bos != null) {
+                try {
                     bos.close();
-                }
-                catch (IOException e)
-                {
+                } catch (IOException e) {
                     e.printStackTrace();
                 }
Index: trunk/src/org/openstreetmap/josm/io/MyHttpHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MyHttpHandler.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/MyHttpHandler.java	(revision 1523)
@@ -9,25 +9,25 @@
 // Basically a copy of sun.net.www.protocol.http.Handler
 public class MyHttpHandler extends sun.net.www.protocol.http.Handler  {
-            protected String proxy;
-            protected int proxyPort;
+    protected String proxy;
+    protected int proxyPort;
 
-            public MyHttpHandler() {
-                super();
-                proxy = null;
-                proxyPort = -1;
-            }
+    public MyHttpHandler() {
+        super();
+        proxy = null;
+        proxyPort = -1;
+    }
 
-            protected java.net.URLConnection openConnection(URL u)
-                    throws IOException {
-                return openConnection(u, (Proxy) null);
-            }
-            public MyHttpHandler(String proxy, int port) {
-                this.proxy = proxy;
-                proxyPort = port;
-            }
+    protected java.net.URLConnection openConnection(URL u)
+    throws IOException {
+        return openConnection(u, (Proxy) null);
+    }
+    public MyHttpHandler(String proxy, int port) {
+        this.proxy = proxy;
+        proxyPort = port;
+    }
 
-            protected java.net.URLConnection openConnection(URL u, Proxy p)
-                    throws IOException {
-                return new MyHttpURLConnection(u, p, this);
-            }
+    protected java.net.URLConnection openConnection(URL u, Proxy p)
+    throws IOException {
+        return new MyHttpURLConnection(u, p, this);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 1523)
+++ trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 1523)
@@ -0,0 +1,432 @@
+//License: GPL. See README for details.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Class that encapsulates the communications with the OSM API. 
+ * 
+ * All interaction with the server-side OSM API should go through this class. 
+ *
+ * It is conceivable to extract this into an interface later and create various
+ * classes implementing the interface, to be able to talk to various kinds of servers.
+ * 
+ */
+public class OsmApi extends OsmConnection {
+
+    /**
+     * Object describing current changeset
+     */
+    private Changeset changeset;
+
+    /**
+     * API version used for server communications
+     */
+    private String version = null;
+
+    /**
+     * Minimum API version accepted by server, from capabilities response
+     */
+    private String minVersion = null;
+    
+    /**
+     * Maximum API version accepted by server, from capabilities response
+     */
+    private String maxVersion = null;
+    
+    /**
+     * Maximum downloadable area from server (degrees squared), from capabilities response
+     * FIXME: make download dialog use this, instead of hard-coded default.
+     */
+    private String maxArea = null;
+    
+    /**
+     * true if successfully initialized
+     */
+    private boolean initialized = false;
+    
+    private ByteArrayOutputStream bao = new ByteArrayOutputStream();
+    private OsmWriter osmWriter = new OsmWriter(new PrintWriter(bao), true, null);
+
+    /**
+     * A parser for the "capabilities" response XML
+     */
+    private class CapabilitiesParser extends DefaultHandler {
+        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+            if (qName.equals("version")) {
+                minVersion = atts.getValue("minimum");
+                maxVersion = atts.getValue("maximum");
+            } else if (qName.equals("area"))
+                maxArea = atts.getValue("maximum");
+        }
+    }
+
+    /**
+     * Helper that returns the lower-case type name of an OsmPrimitive
+     * @param o the primitive
+     * @return "node", "way", "relation", or "changeset"
+     */
+    public static String which(OsmPrimitive o) {
+        if (o instanceof Node) return "node";
+        if (o instanceof Way) return "way";
+        if (o instanceof Relation) return "relation";
+        if (o instanceof Changeset) return "changeset";
+        return "";
+    }
+    
+    /** 
+     * Returns the OSM protocol version we use to talk to the server.
+     * @return protocol version, or null if not yet negotiated.
+     */
+    public String getVersion() {
+        return version;
+    }
+    
+    /**
+     * Returns true if the negotiated version supports changesets.
+     * @return true if the negotiated version supports changesets.
+     */
+    public boolean hasChangesetSupport() {
+        return ((version != null) && (version.compareTo("0.6")>=0));
+    }
+    
+    /**
+     * Initializes this component by negotiating a protocol version with the server.
+     */
+    public void initialize() {
+        initAuthentication();
+        try {
+            initialized = true; // note: has to be before the sendRequest or that will throw!
+            String capabilities = sendRequest("GET", "capabilities", null);
+            InputSource inputSource = new InputSource(new StringReader(capabilities));
+            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new CapabilitiesParser());
+            if (maxVersion.compareTo("0.6") >= 0) {
+                version = "0.6";
+            } else if (minVersion.compareTo("0.5") <= 0) {
+                version = "0.5";
+            } else {
+                System.err.println(tr("This version of JOSM is incompatible with the configured server."));
+                System.err.println(tr("It supports protocol versions 0.5 and 0.6, while the server says it supports {0} to {1}.", 
+                    minVersion, maxVersion));
+                initialized = false;
+            }
+            System.out.println(tr("Communications with {0} established using protocol version {1}", 
+                Main.pref.get("osm-server.url"), 
+                version));
+            osmWriter.setVersion(version);
+        } catch (Exception ex) {
+            initialized = false;
+            ex.printStackTrace();
+        }
+    }
+    
+    /**
+     * Makes an XML string from an OSM primitive. Uses the OsmWriter class.
+     * @param o the OSM primitive
+     * @param addBody true to generate the full XML, false to only generate the encapsulating tag
+     * @return XML string
+     */
+    private String toXml(OsmPrimitive o, boolean addBody) {
+        bao.reset();
+        osmWriter.setWithBody(addBody);
+        osmWriter.header();
+        o.visit(osmWriter);
+        osmWriter.footer();
+        osmWriter.out.flush();
+        return bao.toString();
+    }
+    
+    /**
+     * Helper that makes an int from the first whitespace separated token in a string.
+     * @param s the string
+     * @return the integer represenation of the first token in the string
+     * @throws OsmTransferException if the string is empty or does not represent a number
+     */
+    public static int parseInt(String s) throws OsmTransferException {
+        StringTokenizer t = new StringTokenizer(s);
+        try {
+            return Integer.parseInt(t.nextToken());
+        } catch (Exception x) {
+            throw new OsmTransferException("Cannot read numeric value from response");
+        }
+    }
+
+    /**
+     * Helper that makes a long from the first whitespace separated token in a string.
+     * @param s the string
+     * @return the long represenation of the first token in the string
+     * @throws OsmTransferException if the string is empty or does not represent a number
+     */
+    public static long parseLong(String s) throws OsmTransferException {
+        StringTokenizer t = new StringTokenizer(s);
+        try {
+            return Long.parseLong(t.nextToken());
+        } catch (Exception x) {
+            throw new OsmTransferException("Cannot read numeric value from response");
+        }
+    }    
+    
+    /**
+     * Returns the base URL for API requests, including the negotiated version number.
+     * @return base URL string
+     */
+    public String getBaseUrl() {
+        StringBuffer rv = new StringBuffer(Main.pref.get("osm-server.url"));
+        if (version != null) {
+            rv.append("/");
+            rv.append(version);
+        }
+        rv.append("/");
+        // this works around a ruby (or lighttpd) bug where two consecutive slashes in 
+        // an URL will cause a "404 not found" response.
+        int p; while ((p = rv.indexOf("//", 6)) > -1) { rv.delete(p, p + 1); }
+        return rv.toString();
+    }
+
+    /** 
+     * Creates an OSM primitive on the server. The OsmPrimitive object passed in
+     * is modified by giving it the server-assigned id.
+     * 
+     * @param osm the primitive
+     * @throws OsmTransferException if something goes wrong
+     */
+    public void createPrimitive(OsmPrimitive osm) throws OsmTransferException {
+        osm.id = parseLong(sendRequest("PUT", which(osm)+"/create", toXml(osm, true)));
+        osm.version = 1;
+    }
+    
+    /**
+     * Modifies an OSM primitive on the server. For protocols greater than 0.5,
+     * the OsmPrimitive object passed in is modified by giving it the server-assigned 
+     * version.
+     * 
+     * @param osm the primitive
+     * @throws OsmTransferException if something goes wrong
+     */
+    public void modifyPrimitive(OsmPrimitive osm) throws OsmTransferException {
+        if (version.equals("0.5")) {
+            // legacy mode does not return the new object version.
+            sendRequest("PUT", which(osm)+"/" + osm.id, toXml(osm, true));
+        } else {
+            // normal mode (0.6 and up) returns new object version.
+            osm.version = parseInt(sendRequest("PUT", which(osm)+"/" + osm.id, toXml(osm, true)));
+        }
+    }    
+    
+    /**
+     * Deletes an OSM primitive on the server.
+     * @param osm the primitive
+     * @throws OsmTransferException if something goes wrong
+     */
+    public void deletePrimitive(OsmPrimitive osm) throws OsmTransferException {
+        // legacy mode does not require payload. normal mode (0.6 and up) requires payload for version matching.
+        sendRequest("DELETE", which(osm)+"/" + osm.id, version.equals("0.5") ? null : toXml(osm, false));
+    }   
+    
+    /**
+     * Creates a new changeset on the server to use for subsequent calls.
+     * @param comment the "commit comment" for the new changeset
+     * @throws OsmTransferException signifying a non-200 return code, or connection errors
+     */
+    public void createChangeset(String comment) throws OsmTransferException {
+        changeset = new Changeset();
+        Main.pleaseWaitDlg.currentAction.setText(tr("Opening changeset..."));
+        changeset.put("created_by", "JOSM");
+        changeset.put("comment", comment);
+        createPrimitive(changeset);
+    }
+
+    /**
+     * Closes a changeset on the server.
+     * 
+     * @throws OsmTransferException if something goes wrong.
+     */
+    public void stopChangeset() throws OsmTransferException {
+        Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset..."));
+        sendRequest("PUT", "changeset" + "/" + changeset.id + "/close", null);
+        changeset = null;
+    }
+
+    /**
+     * Uploads a list of changes in "diff" form the the server. 
+     * @param list the list of changed OSM Primitives
+     * @return list of processed primitives
+     * @throws OsmTransferException if something is wrong.
+     */
+    public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list) throws OsmTransferException {
+    
+        if (changeset == null) {
+            throw new OsmTransferException(tr("No changeset present for diff upload"));
+        }
+        
+        CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset, this);
+        String diff = duv.getDocument();
+        
+        ArrayList<OsmPrimitive> processed = new ArrayList<OsmPrimitive>();
+    
+        for (OsmPrimitive osm : list) {
+            int progress = Main.pleaseWaitDlg.progress.getValue();
+            Main.pleaseWaitDlg.currentAction.setText(tr("Preparing..."));
+            if (cancel) throw new OsmTransferCancelledException();
+            osm.visit(duv);
+            Main.pleaseWaitDlg.progress.setValue(progress+1);
+        }
+    
+        Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
+        if (cancel) throw new OsmTransferCancelledException();
+    
+        String diffresult = sendRequest("POST", "changeset/" + changeset.id + "/upload", diff);  
+        try {
+            DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg);
+        } catch (Exception sxe) {
+            throw new OsmTransferException(tr("Error processing changeset upload response"), sxe);
+        }
+        return processed;
+    }
+
+    private void sleepAndListen() throws OsmTransferCancelledException {
+        // System.out.print("backing off for 10 seconds...");
+        for(int i=0; i < 10; i++) {
+            if (cancel || isAuthCancelled()) {
+                throw new OsmTransferCancelledException();
+            }
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ex) {}
+        }
+    }
+
+    /**
+     * Generic method for sending requests to the OSM API.
+     *
+     * This method will automatically re-try any requests that are answered with a 5xx
+     * error code, or that resulted in a timeout exception from the TCP layer.
+     * 
+     * @param requestMethod The http method used when talking with the server.
+     * @param urlSuffix The suffix to add at the server url, not including the version number,
+     *    but including any object ids (e.g. "/way/1234/history").
+     * @param requestBody the body of the HTTP request, if any.
+     * 
+     * @return the body of the HTTP response, if and only if the response code was "200 OK".
+     * @exception OsmTransferException if the HTTP return code was not 200 (and retries have 
+     *    been exhausted), or rewrapping a Java exception. 
+     */
+    private String sendRequest(String requestMethod, String urlSuffix,
+            String requestBody) throws OsmTransferException {
+
+        if (!initialized) throw new OsmTransferException(tr("Not initialized"));
+        
+        StringBuffer responseBody = new StringBuffer();
+        StringBuffer statusMessage = new StringBuffer();
+
+        int retries = 5; // configurable? 
+        
+        while(true) { // the retry loop
+            try {
+                URL url = new URL(new URL(getBaseUrl()), urlSuffix, new MyHttpHandler());
+                System.out.print(requestMethod + " " + url + "... ");
+                activeConnection = (HttpURLConnection)url.openConnection();
+                activeConnection.setConnectTimeout(15000);
+                activeConnection.setRequestMethod(requestMethod);
+                addAuth(activeConnection);
+
+                if (requestBody != null) {
+                    activeConnection.setDoOutput(true);
+                    OutputStream out = activeConnection.getOutputStream();
+                    BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out));
+                    bwr.write(requestBody);
+                    bwr.flush();
+                    out.close();
+                } else {
+                    // It seems that certain bits of the Ruby API are very unhappy upon
+                    // receipt of a PUT/POST message withtout a Content-length header,
+                    // even if the request has no payload.
+                    // Since Java will not generate a Content-length header unless
+                    // we use the output stream, we create one and write no data to 
+                    // it, instead of not creating one. -- fr 6-Apr-09
+                    activeConnection.setDoOutput(true);
+                    OutputStream out = activeConnection.getOutputStream();
+                    out.close();
+                }
+                activeConnection.connect();
+                System.out.println(activeConnection.getResponseMessage());
+
+                int retCode = activeConnection.getResponseCode();
+                
+                if (retCode >= 500) {
+                    if (retries-- > 0) {
+                        sleepAndListen();
+                        continue;
+                    }
+                }
+
+                // populate return fields.
+                responseBody.setLength(0);
+                BufferedReader in = new BufferedReader(new InputStreamReader(activeConnection.getInputStream()));
+                String s;
+                while((s = in.readLine()) != null) {
+                    responseBody.append(s);
+                    responseBody.append("\n");
+                }
+
+                statusMessage.setLength(0);
+                statusMessage.append (activeConnection.getResponseMessage());
+                // Look for a detailed error message from the server
+                if (activeConnection.getHeaderField("Error") != null) {
+                    statusMessage.append(": ");
+                    statusMessage.append(activeConnection.getHeaderField("Error"));
+                }
+                activeConnection.disconnect();
+                
+                if (retCode != 200) {
+                    throw new OsmTransferException(statusMessage.toString());
+                }
+                return responseBody.toString();
+            } catch (UnknownHostException e) {
+                throw new OsmTransferException(tr("Unknown host")+": "+e.getMessage(), e);
+            } catch (SocketTimeoutException e) {
+                if (retries-- > 0)
+                    continue;
+                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
+            } catch (ConnectException e) {
+                if (retries-- > 0)
+                    continue;
+                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
+            } catch (Exception e) {
+                if (e instanceof OsmTransferException) throw (OsmTransferException) e;
+                throw new OsmTransferException(e);
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmConnection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 1523)
@@ -49,5 +49,7 @@
      */
     static {
-        //TODO: refactor this crap (maybe just insert the damn auth http-header by yourself)
+        // TODO: current authentication handling is sub-optimal in that it seems to use the same authenticator for
+        // any kind of request. HTTP requests executed by plugins, e.g. to password-protected WMS servers,
+        // will use the same username/password which is undesirable.
         try {
             HttpURLConnection.setFollowRedirects(true);
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 1523)
@@ -8,8 +8,6 @@
 import java.io.InputStreamReader;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.Map;
@@ -130,9 +128,4 @@
      private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>();
 
-     /**
-      * List of protocol versions that will be accepted on reading
-      */
-     private HashSet<String> allowedVersions = new HashSet<String>();
-
      private class Parser extends DefaultHandler {
           /**
@@ -164,33 +157,13 @@
                          if (atts == null)
                               throw new SAXException(tr("Unknown version"));
-                         if (!allowedVersions.contains(atts.getValue("version")))
-                              throw new SAXException(tr("Unknown version")+": "+atts.getValue("version"));
+                         String v = atts.getValue("version");
+                         if (v == null) 
+                             throw new SAXException(tr("Version number missing from OSM data"));
+                         if (!(v.equals("0.5") || v.equals("0.6"))) 
+                             throw new SAXException(tr("Unknown version: {0}", v));
                          // save generator attribute for later use when creating DataSource objects
                          generator = atts.getValue("generator");
-
-
-                    } else if (qName.equals("bound")) {
-                         // old style bounds.
-                         // TODO: remove this around 1st October 2008.
-                         // - this is a bit of a hack; since we still write out old style bound objects,
-                         // we don't want to load them both. so when writing, we add a "note" tag the our
-                         // old-style bound, and when reading, ignore those with a "note".
-                         String note = atts.getValue("note");
-                         if (note == null) {
-                             System.out.println("Notice: old style <bound> element detected; support for these will be dropped in a future release.");
-                             String bbox = atts.getValue("box");
-                             String origin = atts.getValue("origin");
-                             if (origin == null) origin = "";
-                             if (bbox != null) {
-                                  String[] b = bbox.split(",");
-                                  Bounds bounds = new Bounds();
-                                  if (b.length == 4)
-                                       bounds = new Bounds(
-                                                 new LatLon(Double.parseDouble(b[0]),Double.parseDouble(b[1])),
-                                                 new LatLon(Double.parseDouble(b[2]),Double.parseDouble(b[3])));
-                                  DataSource src = new DataSource(bounds, origin);
-                                  ds.dataSources.add(src);
-                             }
-                         }
+                         ds.version = v;
+                         
                     } else if (qName.equals("bounds")) {
                          // new style bounds.
@@ -277,18 +250,4 @@
                return Double.parseDouble(atts.getValue(value));
           }
-     }
-
-     /**
-      * Constructor initializes list of allowed protocol versions.
-      */
-     public OsmReader() {
-          // first add the main server version
-          allowedVersions.add(Main.pref.get("osm-server.version", "0.5"));
-          // now also add all compatible versions
-          String[] additionalVersions =
-               Main.pref.get("osm-server.additional-versions", "").split("/,/");
-          if (additionalVersions.length == 1 && additionalVersions[0].length() == 0)
-               additionalVersions = new String[] {};
-          allowedVersions.addAll(Arrays.asList(additionalVersions));
      }
 
@@ -491,5 +450,4 @@
           osm.references = ref == null ? new DataSet() : ref;
 
-
           currSource = source;
 
Index: trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1523)
@@ -32,5 +32,4 @@
             Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
             final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg);
-//          String origin = Main.pref.get("osm-server.url")+"/"+Main.pref.get("osm-server.version", "0.5");
 //          Bounds bounds = new Bounds(new LatLon(lat1, lon1), new LatLon(lat2, lon2));
 //          DataSource src = new DataSource(bounds, origin);
Index: trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1523)
@@ -52,5 +52,4 @@
             final DataSet data = osm.getDs();
 
-//          String origin = Main.pref.get("osm-server.url")+"/"+Main.pref.get("osm-server.version", "0.5");
 //          Bounds bounds = new Bounds(new LatLon(lat1, lon1), new LatLon(lat2, lon2));
 //          DataSource src = new DataSource(bounds, origin);
Index: trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 1523)
@@ -27,4 +27,7 @@
  */
 public abstract class OsmServerReader extends OsmConnection {
+    
+    private OsmApi api = new OsmApi();
+    
     /**
      * Open a connection to the given url and return a reader on the input stream
@@ -35,6 +38,6 @@
      */
     protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
-        String version = Main.pref.get("osm-server.version", "0.5");
-        urlStr = Main.pref.get("osm-server.url")+"/"+version+"/" + urlStr;
+        api.initialize();
+        urlStr = api.getBaseUrl() + urlStr;
         return getInputStreamRaw(urlStr, pleaseWaitDlg);
     }
@@ -43,5 +46,4 @@
 
 //        System.out.println("download: "+urlStr);
-        initAuthentication();
         URL url = new URL(urlStr);
         activeConnection = (HttpURLConnection)url.openConnection();
Index: trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 1523)
@@ -4,34 +4,12 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.lang.Math;
-import java.net.ConnectException;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.net.SocketTimeoutException;
 import java.util.Collection;
 import java.util.LinkedList;
+
 import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.Changeset;
-import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
 import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
-import org.openstreetmap.josm.data.osm.visitor.Visitor;
-import org.xml.sax.SAXException;
-import org.openstreetmap.josm.io.XmlWriter.OsmWriterInterface;
 
 /**
@@ -41,11 +19,6 @@
  * those in deleted, which are ignored - All objects in deleted list are
  * deleted. - All remaining objects with modified flag set are updated.
- *
- * This class implements visitor and will perform the correct upload action on
- * the visited element.
- *
- * @author imi
  */
-public class OsmServerWriter extends OsmConnection implements Visitor {
+public class OsmServerWriter {
 
     /**
@@ -58,19 +31,6 @@
     public Collection<OsmPrimitive> processed;
 
-    /**
-     * Whether the operation should be aborted as soon as possible.
-     */
-    // use the inherited variable
-    // private boolean cancel = false;
-
-    /**
-     * Object describing current changeset
-     */
-    private Changeset changeset;
-
-    /**
-     * Send the dataset to the server. Ask the user first and does nothing if he
-     * does not want to send the data.
-     */
+    private OsmApi api = new OsmApi();
+    
     private static final int MSECS_PER_SECOND = 1000;
     private static final int SECONDS_PER_MINUTE = 60;
@@ -96,18 +56,23 @@
     }
 
-    public void uploadOsm(Collection<OsmPrimitive> list) throws SAXException {
+    /**
+     * Send the dataset to the server. 
+     * @param the_version version of the data set
+     * @param list list of objects to send
+     */
+    public void uploadOsm(String the_version, Collection<OsmPrimitive> list) {
         processed = new LinkedList<OsmPrimitive>();
-        initAuthentication();
+        api.initialize();
 
         Main.pleaseWaitDlg.progress.setMaximum(list.size());
         Main.pleaseWaitDlg.progress.setValue(0);
 
-        // controls whether or not we open and close a changeset. API 0.6 requires changesets.
-        boolean useChangesets = Main.pref.get("osm-server.version", "0.5").equals("0.6");
+        boolean useChangesets = api.hasChangesetSupport();
+        
+        // controls whether or not we try and upload the whole bunch in one go
+        boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload",
+            "0.6".equals(api.getVersion()));
 
-        // controls whether or not we try and uplaod the whole bunch in one go
-        boolean useDiffUploads = Main.pref.getBoolean("osm-server.atomic-upload",
-            Main.pref.get("osm-server.version", "0.5").equals("0.6"));
-
+        // solicit commit comment from user
         String comment = null;
         while (useChangesets && comment == null) {
@@ -117,14 +82,14 @@
             if (comment == null)
                 return;
-            /* Don't let people just hit enter */
-            if( comment.trim().length() >= 3 )
+            // Don't let people just hit enter
+            if (comment.trim().length() >= 3)
                 break;
             comment = null;
         }
+        
+        // create changeset if required
         try {
-            if (useChangesets && !startChangeset(10, comment))
-                return;
-        }
-        catch (OsmTransferException ex) {
+            if (useChangesets) api.createChangeset(comment);
+        } catch (OsmTransferException ex) {
             dealWithTransferException(ex);
             return;
@@ -133,39 +98,29 @@
         try {
             if (useDiffUploads) {
-                uploadDiff(10, list);
+                // all in one go
+                processed.addAll(api.uploadDiff(list));
             } else {
+                // upload changes individually (90% of code is for the status display...)
                 NameVisitor v = new NameVisitor();
                 uploadStartTime = System.currentTimeMillis();
                 for (OsmPrimitive osm : list) {
-                    if (cancel)
-                        return;
                     osm.visit(v);
                     int progress = Main.pleaseWaitDlg.progress.getValue();
                     String time_left_str = timeLeft(progress, list.size());
                     Main.pleaseWaitDlg.currentAction.setText(
-                    tr("{0}% ({1}/{2}), {3} left. Uploading {4}: {5} (id: {6})",
-                    Math.round(100.0*progress/list.size()), progress,
-                    list.size(), time_left_str, tr(v.className), v.name, osm.id));
-                    osm.visit(this);
+                            tr("{0}% ({1}/{2}), {3} left. Uploading {4}: {5} (id: {6})",
+                                    Math.round(100.0*progress/list.size()), progress,
+                                    list.size(), time_left_str, tr(v.className), v.name, osm.id));
+                    makeApiRequest(osm);
+                    processed.add(osm);
                     Main.pleaseWaitDlg.progress.setValue(progress+1);
                 }
             }
-            if (useChangesets) stopChangeset(10);
-        } catch (RuntimeException e) {
+            if (useChangesets) api.stopChangeset();
+        } catch (OsmTransferException e) {
             try {
-                if (useChangesets) stopChangeset(10);
-            }
-            catch (OsmTransferException ex) {
-                dealWithTransferException(ex);
-            }
-            e.printStackTrace();
-            throw new SAXException(tr("An error occurred: {0}",e.getMessage()));
-        }
-        catch (OsmTransferException e) {
-            try {
-                if (useChangesets) stopChangeset(10);
-            }
-            catch (OsmTransferException ex) {
-                dealWithTransferException(ex);
+                if (useChangesets) api.stopChangeset();
+            } catch (Exception ee) {
+                // ignore nested exception
             }
             dealWithTransferException(e);
@@ -173,475 +128,11 @@
     }
 
-    /* FIXME: This code is terrible, please fix it!!!! */
-
-    /* Ok, this needs some explanation: The problem is that the code for
-     * retrying requests is intertwined with the code that generates the
-     * actual request. This means that for the retry code for the
-     * changeset stuff, it's basically a copy/cut/change slightly
-     * process. What actually needs to happen is that the retrying needs
-     * to be split from the creation of the requests and the retry loop
-     * handled in one place (preferably without recursion). While at you
-     * can fix the issue where hitting cancel doesn't do anything while
-     * retrying. - Mv0 Apr 2008
-     *
-     * Cancelling has an effect now, maybe it does not always catch on. Florian Heer, Aug 08
-     */
-    private boolean startChangeset(int retries, String comment) throws OsmTransferException {
-        Main.pleaseWaitDlg.currentAction.setText(tr("Opening changeset..."));
-        changeset = new Changeset();
-        changeset.put( "created_by", "JOSM" );
-        changeset.put( "comment", comment );
-        try {
-            if (cancel)
-                return false; // assume cancel
-            String version = Main.pref.get("osm-server.version", "0.6");
-            URL url = new URL(
-                    Main.pref.get("osm-server.url") +
-                    "/" + version +
-                    "/" + "changeset" +
-                    "/" + "create");
-            System.out.print("upload to: "+url+ "..." );
-            activeConnection = (HttpURLConnection)url.openConnection();
-            activeConnection.setConnectTimeout(15000);
-            activeConnection.setRequestMethod("PUT");
-            addAuth(activeConnection);
-
-            activeConnection.setDoOutput(true);
-            OutputStream out = activeConnection.getOutputStream();
-            OsmWriter.output(out, changeset);
-            out.close();
-
-            activeConnection.connect();
-            System.out.println("connected");
-
-            int retCode = activeConnection.getResponseCode();
-            if (retCode == 200)
-                changeset.id = readId(activeConnection.getInputStream());
-            System.out.println("got return: "+retCode+" with id "+changeset.id);
-            String retMsg = activeConnection.getResponseMessage();
-            activeConnection.disconnect();
-            if (retCode == 404)    {
-                throw new OsmTransferException(tr("Server does not support changesets"));
-            }
-            if (retCode != 200 && retCode != 412) {
-                if (retries >= 0) {
-                    retries--;
-                    if(sleepAndListen()) return false;
-                    System.out.println("retrying ("+retries+" left)");
-                    return startChangeset(retries, comment);
-                }
-
-                // Look for a detailed error message from the server
-                retMsg += "\n" + readString(activeConnection.getInputStream());
-
-                // Report our error
-                ByteArrayOutputStream o = new ByteArrayOutputStream();
-                OsmWriter.output(o, changeset);
-                System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-                throw new OsmTransferException (retCode + " " + retMsg);
-            }
-        } catch (UnknownHostException e) {
-            throw new OsmTransferException(tr("Unknown host")+": "+e.getMessage(), e);
-        } catch(SocketTimeoutException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return false; // assume cancel
-            if (retries-- > 0)
-                startChangeset(retries, comment);
-            else
-                throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-        catch (ConnectException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return false; // assume cancel
-            if (retries-- > 0)
-                startChangeset(retries, comment);
-            else
-                throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-
-        catch (Exception e) {
-            if (cancel)
-                return false; // assume cancel
-            if (e instanceof OsmTransferException)
-                throw (OsmTransferException)e;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-        return true;
-    }
-
-    private void uploadDiff(int retries, Collection<OsmPrimitive> list) throws OsmTransferException {
-
-        CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset);
-
-        for (OsmPrimitive osm : list) {
-            int progress = Main.pleaseWaitDlg.progress.getValue();
-            Main.pleaseWaitDlg.currentAction.setText(tr("Preparing..."));
-            if (cancel)
-                return;
-            osm.visit(duv);
-            Main.pleaseWaitDlg.progress.setValue(progress+1);
-        }
-        System.out.println("the document:\n");
-        String diff = duv.getDocument();
-        System.out.println(diff);
-
-        Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
-        try {
-            if (cancel)
-                return; // assume cancel
-            String version = Main.pref.get("osm-server.version", "0.6");
-            URL url = new URL(
-                    Main.pref.get("osm-server.url") +
-                    "/" + version +
-                    "/" + "changeset" +
-                    "/" + changeset.id +
-                    "/upload" );
-            System.out.print("upload to: "+url+ "..." );
-            activeConnection = (HttpURLConnection)url.openConnection();
-            activeConnection.setConnectTimeout(15000);
-            activeConnection.setRequestMethod("POST");
-            addAuth(activeConnection);
-
-            activeConnection.setDoOutput(true);
-            PrintWriter out;
-            try {
-                out = new PrintWriter(new OutputStreamWriter(activeConnection.getOutputStream(), "UTF-8"));
-            } catch (UnsupportedEncodingException e) {
-                throw new RuntimeException(e);
-            }
-            out.print(diff);
-            out.close();
-
-            activeConnection.connect();
-            System.out.println("connected");
-
-            int retCode = activeConnection.getResponseCode();
-            String retMsg = "";
-
-            if (retCode == 200) {
-                DiffResultReader.parseDiffResult(activeConnection.getInputStream(), list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg);
-            } else if (retCode != 200 && retCode != 412) {
-                if (retries >= 0) {
-                    retries--;
-                    if(sleepAndListen()) return;
-                    System.out.println("retrying ("+retries+" left)");
-                    stopChangeset(retries);
-                } else {
-                    // Look for a detailed error message from the server
-                    retMsg += "\n" + readString(activeConnection.getInputStream());
-
-                    // Report our error
-                    ByteArrayOutputStream o = new ByteArrayOutputStream();
-                    OsmWriter.output(o, changeset);
-                    System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-                    throw new OsmTransferException(retCode+" "+retMsg);
-                }
-            }
-        } catch (UnknownHostException e) {
-            throw new OsmTransferException(tr("Unknown host")+": "+e.getMessage(), e);
-        } catch(SocketTimeoutException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                stopChangeset(retries);
-            else
-                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch(ConnectException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                stopChangeset(retries);
-            else
-                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch (Exception e) {
-            if (cancel)
-                return; // assume cancel
-            if (e instanceof OsmTransferException)
-                throw (OsmTransferException)e;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-    }
-
-
-    private void stopChangeset(int retries) throws OsmTransferException {
-        Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset..."));
-        try {
-            if (cancel)
-                return; // assume cancel
-            String version = Main.pref.get("osm-server.version", "0.6");
-            URL url = new URL(
-                    Main.pref.get("osm-server.url") +
-                    "/" + version +
-                    "/" + "changeset" +
-                    "/" + changeset.id +
-                    "/close" );
-            System.out.print("upload to: "+url+ "..." );
-            activeConnection = (HttpURLConnection)url.openConnection();
-            activeConnection.setConnectTimeout(15000);
-            activeConnection.setRequestMethod("PUT");
-            addAuth(activeConnection);
-
-            activeConnection.setDoOutput(true);
-            OutputStream out = activeConnection.getOutputStream();
-            OsmWriter.output(out, changeset);
-            out.close();
-
-            activeConnection.connect();
-            System.out.println("connected");
-
-            int retCode = activeConnection.getResponseCode();
-            System.out.println("got return: "+retCode);
-            String retMsg = activeConnection.getResponseMessage();
-            activeConnection.disconnect();
-            if (retCode == 404)    {
-                System.out.println("Server does not support changesets, or the changeset could not be found, continuing");
-                return;
-            }
-            if (retCode != 200 && retCode != 412) {
-                if (retries >= 0) {
-                    retries--;
-                    if(sleepAndListen()) return;
-                    System.out.println("retrying ("+retries+" left)");
-                    stopChangeset(retries);
-                } else {
-                    // Look for a detailed error message from the server
-                    retMsg += readString(activeConnection.getInputStream());
-
-                    // Report our error
-                    ByteArrayOutputStream o = new ByteArrayOutputStream();
-                    OsmWriter.output(o, changeset);
-                    System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-                    throw new OsmTransferException(retCode+" "+retMsg);
-                }
-            }
-        } catch (UnknownHostException e) {
-            throw new OsmTransferException(tr("Unknown host")+": "+e.getMessage(), e);
-        } catch(SocketTimeoutException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                stopChangeset(retries);
-            else
-                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch(ConnectException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                stopChangeset(retries);
-            else
-                throw new OsmTransferException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch (Exception e) {
-            if (cancel)
-                return; // assume cancel
-            if (e instanceof OsmTransferException)
-                throw (OsmTransferException)e;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-    }
-
-    private boolean sleepAndListen() {
-        // System.out.print("backing off for 10 seconds...");
-        for(int i=0; i < 10; i++) {
-            if(cancel || isAuthCancelled()) {
-                if(!cancel) cancel();
-                return true;
-            }
-            try {
-                Thread.sleep(1000);
-            } catch (InterruptedException ex) {}
-        }
-        return false;
-    }
-
-    /**
-     * Upload a single node.
-     */
-    public void visit(Node n) {
-        if (n.deleted) {
-            sendRequest("DELETE", "node", n, true);
+    void makeApiRequest(OsmPrimitive osm) throws OsmTransferException {
+        if (osm.deleted) {
+            api.deletePrimitive(osm);
+        } else if (osm.id == 0) {
+            api.createPrimitive(osm);
         } else {
-            sendRequest("PUT", "node", n, true);
-        }
-        processed.add(n);
-    }
-
-    /**
-     * Upload a whole way with the complete node id list.
-     */
-    public void visit(Way w) {
-        if (w.deleted) {
-            sendRequest("DELETE", "way", w, true);
-        } else {
-            sendRequest("PUT", "way", w, true);
-        }
-        processed.add(w);
-    }
-
-    /**
-     * Upload an relation with all members.
-     */
-    public void visit(Relation e) {
-        if (e.deleted) {
-            sendRequest("DELETE", "relation", e, true);
-        } else {
-            sendRequest("PUT", "relation", e, true);
-        }
-        processed.add(e);
-    }
-
-    /**
-     * Read a long from the input stream and return it.
-     */
-    private long readId(InputStream inputStream) throws IOException {
-        BufferedReader in = new BufferedReader(new InputStreamReader(
-                inputStream));
-        String s = in.readLine();
-        if (s == null)
-            return 0;
-        try {
-            return Long.parseLong(s);
-        } catch (NumberFormatException e) {
-            return 0;
-        }
-    }
-
-    /**
-     * Consume the input stream and return it as a string.
-     */
-    private String readString(InputStream inputStream) throws IOException {
-        BufferedReader in = new BufferedReader(new InputStreamReader(
-                inputStream));
-        StringBuffer sb = new StringBuffer();
-        String s;
-        while((s = in.readLine()) != null) {
-            sb.append(s);
-            sb.append("\n");
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Send the request. The objects id will be replaced if it was 0 before
-     * (on add requests).
-     *
-     * @param requestMethod The http method used when talking with the server.
-     * @param urlSuffix The suffix to add at the server url.
-     * @param osm The primitive to encode to the server.
-     * @param body the body to be sent
-     */
-    private void sendRequestRetry(String requestMethod, String urlSuffix,
-            OsmPrimitive osm, OsmWriterInterface body, int retries) throws OsmTransferException {
-        try {
-            if (cancel)
-                return; // assume cancel
-            String version = Main.pref.get("osm-server.version", "0.5");
-            URL url = new URL(
-                    new URL(Main.pref.get("osm-server.url") +
-                    "/" + version + "/"),
-                    urlSuffix +
-                    "/" + (osm.id==0 ? "create" : osm.id),
-                    new MyHttpHandler());
-            System.out.print("upload to: "+url+ "..." );
-            activeConnection = (HttpURLConnection)url.openConnection();
-            activeConnection.setConnectTimeout(15000);
-            activeConnection.setRequestMethod(requestMethod);
-            addAuth(activeConnection);
-            if (body != null) {
-                activeConnection.setDoOutput(true);
-                OutputStream out = activeConnection.getOutputStream();
-                OsmWriter.output(out, body);
-                out.close();
-            }
-            activeConnection.connect();
-            System.out.println("connected");
-
-            int retCode = activeConnection.getResponseCode();
-            /* When creating new, the returned value is the new id, otherwise it is the new version */
-            if (retCode == 200)    {
-                if (osm.id == 0) {
-                    osm.id = readId(activeConnection.getInputStream());
-                    osm.version = 1;
-                } else {
-                    int read_version = (int)readId(activeConnection.getInputStream());
-                    if (read_version > 0)
-                        osm.version = read_version;
-                }
-            } else {
-                System.out.println("got return: "+retCode+" with id "+osm.id);
-            }
-            activeConnection.disconnect();
-            if (retCode == 410 && requestMethod.equals("DELETE"))
-                return; // everything fine.. was already deleted.
-            else if (retCode != 200) {
-                if (retries >= 0 && retCode != 412)    {
-                    retries--;
-                    if(sleepAndListen()) return;
-                    System.out.println("retrying ("+retries+" left)");
-                    sendRequestRetry(requestMethod, urlSuffix, osm, body, retries);
-                } else {
-                    String retMsg = activeConnection.getResponseMessage();
-                    // Look for a detailed error message from the server
-                    if (activeConnection.getHeaderField("Error") != null)
-                        retMsg += "\n" + activeConnection.getHeaderField("Error");
-
-                    // Report our error
-                    ByteArrayOutputStream o = new ByteArrayOutputStream();
-                    OsmWriter.output(o, body);
-                    System.out.println(new String(o.toByteArray(), "UTF-8").toString());
-                    throw new OsmTransferException(retCode+" "+retMsg);
-                }
-            }
-        } catch (UnknownHostException e) {
-            throw new OsmTransferException(tr("Unknown host")+": "+e.getMessage(), e);
-        } catch(SocketTimeoutException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                sendRequestRetry(requestMethod, urlSuffix, osm, body, retries);
-            else
-                throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch(ConnectException e) {
-            System.out.println(" timed out, retries left: " + retries);
-            if (cancel)
-                return; // assume cancel
-            if (retries-- > 0)
-                sendRequestRetry(requestMethod, urlSuffix, osm, body, retries);
-            else
-                throw new OsmTransferException (e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        } catch (Exception e) {
-            if (cancel)
-                return; // assume cancel
-            if (e instanceof OsmTransferException)
-                throw (OsmTransferException)e;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e.getMessage()+ " " + e.getClass().getCanonicalName(), e);
-        }
-    }
-
-    private void sendRequest(String requestMethod, String urlSuffix,
-            OsmPrimitive osm, boolean addBody)  {
-        XmlWriter.OsmWriterInterface body = null;
-        if (addBody) {
-                body = new OsmWriter.Single(osm, true, changeset);
-        }
-        try {
-            sendRequestRetry(requestMethod, urlSuffix, osm, body, 10);
-        }
-        catch (OsmTransferException e) {
-            dealWithTransferException (e);
+            api.modifyPrimitive(osm);
         }
     }
@@ -649,5 +140,4 @@
     private void dealWithTransferException (OsmTransferException e) {
         Main.pleaseWaitDlg.currentAction.setText(tr("Transfer aborted due to error (will wait for 5 seconds):") + e.getMessage());
-        cancel = true;
         try {
             Thread.sleep(5000);
Index: trunk/src/org/openstreetmap/josm/io/OsmTransferCancelledException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmTransferCancelledException.java	(revision 1523)
+++ trunk/src/org/openstreetmap/josm/io/OsmTransferCancelledException.java	(revision 1523)
@@ -0,0 +1,6 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+public class OsmTransferCancelledException extends OsmTransferException {
+
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1523)
@@ -6,12 +6,11 @@
 import java.util.Map.Entry;
 
-import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
@@ -26,7 +25,8 @@
 
     /**
-     * The counter for new created objects. Starting at -1 and goes down.
+     * The counter for newly created objects. Starts at -1 and goes down.
      */
     private long newIdCounter = -1;
+ 
     /**
      * All newly created ids and their primitive that uses it. This is a back reference
@@ -35,98 +35,60 @@
     public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>();
 
-    private final boolean osmConform;
-    private final Changeset changeset;
-
-    public abstract static class Osm implements OsmWriterInterface {
-        public void header(PrintWriter out) {
-            out.print("<osm version='");
-            out.print(Main.pref.get("osm-server.version", "0.5"));
-            out.println("' generator='JOSM'>");
-        }
-        public void footer(PrintWriter out) {
-            out.println("</osm>");
-        }
-    }
-
-    // simple helper to write the object's class to the out stream
-    private Visitor typeWriteVisitor = new Visitor() {
-        public void visit(Node n) { out.print("node"); }
-        public void visit(Way w) { out.print("way"); }
-        public void visit(Relation e) { out.print("relation"); }
-    };
-
-    /**
-     * An output writer for function output that writes everything of the given dataset into
-     * the xml
-     */
-    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, null);
-            for (Node n : ds.nodes)
-                if (shouldWrite(n))
-                    writer.visit(n);
-            for (Way w : ds.ways)
-                if (shouldWrite(w))
-                    writer.visit(w);
-            for (Relation e : ds.relations)
-                if (shouldWrite(e))
-                    writer.visit(e);
-        }
-
-        private boolean shouldWrite(OsmPrimitive osm) {
-            return osm.id != 0 || !osm.deleted;
-        }
-
-        @Override public void header(PrintWriter out) {
-            super.header(out);
-            for (DataSource s : ds.dataSources) {
-                out.println("  <bounds minlat='"
-                + s.bounds.min.lat()+"' minlon='"
-                + s.bounds.min.lon()+"' maxlat='"
-                + s.bounds.max.lat()+"' maxlon='"
-                + s.bounds.max.lon()
-                +"' origin='"+XmlWriter.encode(s.origin)+"' />");
-            }
-        }
-    }
-
-    /**
-     * 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;
-        private final Changeset changeset;
-
-        public Single(OsmPrimitive osm, boolean osmConform, Changeset changeset) {
-            this.osm = osm;
-            this.osmConform = osmConform;
-            this.changeset = changeset;
-        }
-
-        public void write(PrintWriter out) {
-            osm.visit(new OsmWriter(out, osmConform, changeset));
-        }
-    }
-
-    public OsmWriter(PrintWriter out, boolean osmConform, Changeset changeset) {
+    private boolean osmConform;
+    private boolean withBody = true;
+    private String version;
+    private Changeset changeset;
+    
+    public OsmWriter(PrintWriter out, boolean osmConform, String version) {
         super(out);
         this.osmConform = osmConform;
-        this.changeset = changeset;
+        this.version = version;
+    }
+    
+    public void setWithBody(boolean wb) {
+        this.withBody = wb;
+    }
+    public void setChangeset(Changeset cs) {
+        this.changeset = cs;
+    }
+    public void setVersion(String v) {
+        this.version = v;
+    }
+    
+    public void header() {
+        out.println("<?xml version='1.0' encoding='UTF-8'?>");
+        out.print("<osm version='");
+        out.print(version);
+        out.println("' generator='JOSM'>");
+    }
+    public void footer() {
+        out.println("</osm>");
+    }
+
+    public void writeContent(DataSet ds) {
+        for (Node n : ds.nodes)
+            if (shouldWrite(n))
+                visit(n);
+        for (Way w : ds.ways)
+            if (shouldWrite(w))
+                visit(w);
+        for (Relation e : ds.relations)
+            if (shouldWrite(e))
+                visit(e);
+    }
+
+    private boolean shouldWrite(OsmPrimitive osm) {
+        return osm.id != 0 || !osm.deleted;
+    }
+
+    public void writeDataSources(DataSet ds) {
+        for (DataSource s : ds.dataSources) {
+            out.println("  <bounds minlat='"
+                    + s.bounds.min.lat()+"' minlon='"
+                    + s.bounds.min.lon()+"' maxlat='"
+                    + s.bounds.max.lat()+"' maxlon='"
+                    + s.bounds.max.lon()
+                    +"' origin='"+XmlWriter.encode(s.origin)+"' />");
+        }
     }
 
@@ -135,5 +97,9 @@
         addCommon(n, "node");
         out.print(" lat='"+n.coor.lat()+"' lon='"+n.coor.lon()+"'");
-        addTags(n, "node", true);
+        if (!withBody) {
+            out.println("/>");  
+        } else {
+            addTags(n, "node", true);
+        }
     }
 
@@ -141,8 +107,12 @@
         if (w.incomplete) return;
         addCommon(w, "way");
-        out.println(">");
-        for (Node n : w.nodes)
-            out.println("    <nd ref='"+getUsedId(n)+"' />");
-        addTags(w, "way", false);
+        if (!withBody) {
+            out.println("/>");  
+        } else {
+            out.println(">");
+            for (Node n : w.nodes)
+                out.println("    <nd ref='"+getUsedId(n)+"' />");
+            addTags(w, "way", false);
+        }
     }
 
@@ -150,14 +120,27 @@
         if (e.incomplete) return;
         addCommon(e, "relation");
-        out.println(">");
-        for (RelationMember em : e.members) {
-            out.print("    <member type='");
-            em.member.visit(typeWriteVisitor);
-            out.println("' ref='"+getUsedId(em.member)+"' role='" +
-                XmlWriter.encode(em.role) + "' />");
-        }
-        addTags(e, "relation", false);
-    }
-
+        if (!withBody) {
+            out.println("/>");  
+        } else {
+            out.println(">");
+            for (RelationMember em : e.members) {
+                out.print("    <member type='");
+                out.print(OsmApi.which(em.member));
+                out.println("' ref='"+getUsedId(em.member)+"' role='" +
+                        XmlWriter.encode(em.role) + "' />");
+            }
+            addTags(e, "relation", false);
+        }
+    }
+
+    public void visit(Changeset cs) {
+        addCommon(cs, "changeset");
+        out.println(">\n");
+        addTags(cs, "changeset", false);
+    }
+
+    public final void footer(PrintWriter out) {
+        out.println("</osm>");
+    }
 
     /**
@@ -192,5 +175,9 @@
      */
     private void addCommon(OsmPrimitive osm, String tagname) {
-        out.print("  <"+tagname+" id='"+getUsedId(osm)+"'");
+        long id = getUsedId(osm);
+        out.print("  <"+tagname);
+        if (id != 0) {
+             out.print(" id='"+getUsedId(osm)+"'");
+        }
         if (!osmConform) {
             String action = null;
Index: trunk/src/org/openstreetmap/josm/io/XmlWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/XmlWriter.java	(revision 1520)
+++ trunk/src/org/openstreetmap/josm/io/XmlWriter.java	(revision 1523)
@@ -2,8 +2,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;
 
@@ -15,19 +12,10 @@
 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) {
+    protected PrintWriter out;
+    
+    public XmlWriter(PrintWriter out) {
         this.out = out;
     }
-
+    
     /**
      * Encode the given string in XML1.0 format.
@@ -49,26 +37,6 @@
 
     /**
-     * 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 {
