Index: trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 2115)
@@ -0,0 +1,156 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.UserInfo;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.io.CloseChangesetDialog;
+import org.openstreetmap.josm.gui.io.CloseChangesetTask;
+import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.io.OsmServerChangesetReader;
+import org.openstreetmap.josm.io.OsmServerUserInfoReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.xml.sax.SAXException;
+
+public class CloseChangesetAction extends JosmAction{
+
+    public CloseChangesetAction() {
+        super(tr("Close open changesets"),
+                "closechangeset",
+                tr("Closes open changesets"),
+                Shortcut.registerShortcut(
+                        "system:closechangeset",
+                        tr("File: {0}", tr("Closes open changesets")),
+                        KeyEvent.VK_Q,
+                        Shortcut.GROUP_HOTKEY + Shortcut.GROUPS_ALT2
+                ),
+                true
+        );
+
+    }
+    public void actionPerformed(ActionEvent e) {
+        Main.worker.submit(new DownloadOpenChangesetsTask());
+    }
+
+
+    protected void onPostDownloadOpenChangesets(DownloadOpenChangesetsTask task) {
+        if (task.isCancelled() || task.getLastException() != null) return;
+
+        List<Changeset> openChangesets = task.getChangesets();
+        if (openChangesets.isEmpty()) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("There are no open changesets"),
+                    tr("No open changesets"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+            return;
+        }
+
+        CloseChangesetDialog dialog = new CloseChangesetDialog();
+        dialog.setChangesets(openChangesets);
+        dialog.setVisible(true);
+        if (dialog.isCanceled())
+            return;
+
+        Collection<Changeset> changesetsToClose = dialog.getSelectedChangesets();
+        CloseChangesetTask closeChangesetTask = new CloseChangesetTask(changesetsToClose);
+        Main.worker.submit(closeChangesetTask);
+    }
+
+
+    private class DownloadOpenChangesetsTask extends PleaseWaitRunnable {
+
+        private boolean cancelled;
+        private OsmServerChangesetReader reader;
+        private List<Changeset> changesets;
+        private Exception lastException;
+        private UserInfo userInfo;
+
+        /**
+         * 
+         * @param model provides the user id of the current user and accepts the changesets
+         * after download
+         */
+        public DownloadOpenChangesetsTask() {
+            super(tr("Downloading open changesets ...", false /* don't ignore exceptions */));
+        }
+
+        @Override
+        protected void cancel() {
+            this.cancelled = true;
+            reader.cancel();
+        }
+
+        @Override
+        protected void finish() {
+            SwingUtilities.invokeLater(
+                    new Runnable() {
+                        public void run() {
+                            if (lastException != null) {
+                                ExceptionDialogUtil.explainException(lastException);
+                            }
+                            onPostDownloadOpenChangesets(DownloadOpenChangesetsTask.this);
+                        }
+                    }
+            );
+        }
+
+        /**
+         * Fetch the user info from the server. This is necessary if we don't know
+         * the users id yet
+         * 
+         * @return the user info
+         * @throws OsmTransferException thrown in case of any communication exception
+         */
+        protected UserInfo fetchUserInfo() throws OsmTransferException {
+            OsmServerUserInfoReader reader = new OsmServerUserInfoReader();
+            return reader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false));
+        }
+
+        @Override
+        protected void realRun() throws SAXException, IOException, OsmTransferException {
+            try {
+                userInfo = fetchUserInfo();
+                if (cancelled)
+                    return;
+                reader = new OsmServerChangesetReader();
+                ChangesetQuery query = new ChangesetQuery().forUser(userInfo.getId()).beingOpen();
+                changesets = reader.queryChangesets(
+                        query,
+                        getProgressMonitor().createSubTaskMonitor(1, false /* not internal */)
+                );
+            } catch(Exception e) {
+                if (cancelled)
+                    return;
+                lastException = e;
+            }
+        }
+
+        public boolean isCancelled() {
+            return cancelled;
+        }
+
+        public List<Changeset> getChangesets() {
+            return changesets;
+        }
+
+        public Exception getLastException() {
+            return lastException;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/actions/StopChangesetAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/StopChangesetAction.java	(revision 2114)
+++ 	(revision )
@@ -1,89 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.actions;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.io.IOException;
-
-import javax.swing.JOptionPane;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.ExceptionDialogUtil;
-import org.openstreetmap.josm.gui.PleaseWaitRunnable;
-import org.openstreetmap.josm.io.ChangesetProcessingType;
-import org.openstreetmap.josm.io.OsmApi;
-import org.openstreetmap.josm.io.OsmTransferException;
-import org.openstreetmap.josm.tools.Shortcut;
-import org.xml.sax.SAXException;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-public class StopChangesetAction extends JosmAction{
-
-    public StopChangesetAction() {
-        super(tr("Close current changeset"),
-                "closechangeset",
-                tr("Close the current changeset ..."),
-                Shortcut.registerShortcut(
-                        "system:closechangeset",
-                        tr("File: {0}", tr("Close the current changeset ...")),
-                        KeyEvent.VK_Q,
-                        Shortcut.GROUP_HOTKEY + Shortcut.GROUPS_ALT2
-                ),
-                true
-        );
-
-    }
-    public void actionPerformed(ActionEvent e) {
-        if (OsmApi.getOsmApi().getCurrentChangeset() == null) {
-            JOptionPane.showMessageDialog(
-                    Main.parent,
-                    tr("There is currently no changeset open."),
-                    tr("No open changeset"),
-                    JOptionPane.INFORMATION_MESSAGE
-            );
-            return;
-        }
-        Main.worker.submit(new StopChangesetActionTask());
-    }
-
-    @Override
-    protected void updateEnabledState() {
-        setEnabled(Main.map != null && OsmApi.getOsmApi().getCurrentChangeset() != null);
-    }
-
-    static class StopChangesetActionTask extends PleaseWaitRunnable {
-        private boolean cancelled;
-        private Exception lastException;
-
-        public StopChangesetActionTask() {
-            super(tr("Closing changeset"), false /* don't ignore exceptions */);
-        }
-        @Override
-        protected void cancel() {
-            this.cancelled = true;
-            OsmApi.getOsmApi().cancel();
-
-        }
-
-        @Override
-        protected void finish() {
-            if (cancelled)
-                return;
-            if (lastException != null) {
-                ExceptionDialogUtil.explainException(lastException);
-            }
-        }
-
-        @Override
-        protected void realRun() throws SAXException, IOException, OsmTransferException {
-            try {
-                OsmApi.getOsmApi().stopChangeset(ChangesetProcessingType.USE_EXISTING_AND_CLOSE, getProgressMonitor().createSubTaskMonitor(1, false));
-            } catch(Exception e) {
-                if (cancelled)
-                    return;
-                lastException = e;
-            }
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2115)
@@ -28,5 +28,4 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.ChangesetProcessingType;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiException;
@@ -176,6 +175,6 @@
                         Main.map.mapView.getEditLayer(),
                         apiData.getPrimitives(),
-                        UploadConfirmationHook.getUploadDialog().getChangeset(),
-                        UploadConfirmationHook.getUploadDialog().getChangesetProcessingType()
+                        UploadDialog.getUploadDialog().getChangeset(),
+                        UploadDialog.getUploadDialog().isDoCloseAfterUpload()
                 )
         );
@@ -244,11 +243,11 @@
         );
         switch(ret) {
-        case JOptionPane.CLOSED_OPTION: return;
-        case JOptionPane.CANCEL_OPTION: return;
-        case 0: synchronizePrimitive(primitiveType, id); break;
-        case 1: synchronizeDataSet(); break;
-        default:
-            // should not happen
-            throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
+            case JOptionPane.CLOSED_OPTION: return;
+            case JOptionPane.CANCEL_OPTION: return;
+            case 0: synchronizePrimitive(primitiveType, id); break;
+            case 1: synchronizeDataSet(); break;
+            default:
+                // should not happen
+                throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
         }
     }
@@ -284,10 +283,10 @@
         );
         switch(ret) {
-        case JOptionPane.CLOSED_OPTION: return;
-        case 1: return;
-        case 0: synchronizeDataSet(); break;
-        default:
-            // should not happen
-            throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
+            case JOptionPane.CLOSED_OPTION: return;
+            case 1: return;
+            case 0: synchronizeDataSet(); break;
+            default:
+                // should not happen
+                throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
         }
     }
@@ -457,15 +456,7 @@
 
     static public class UploadConfirmationHook implements UploadHook {
-        static private UploadDialog uploadDialog;
-
-        static public UploadDialog getUploadDialog() {
-            if (uploadDialog == null) {
-                uploadDialog = new UploadDialog();
-            }
-            return uploadDialog;
-        }
 
         public boolean checkUpload(APIDataSet apiData) {
-            final UploadDialog dialog = getUploadDialog();
+            final UploadDialog dialog = UploadDialog.getUploadDialog();
             dialog.setUploadedPrimitives(apiData.getPrimitivesToAdd(),apiData.getPrimitivesToUpdate(), apiData.getPrimitivesToDelete());
             dialog.setVisible(true);
@@ -477,8 +468,12 @@
     }
 
-    public UploadDiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, ChangesetProcessingType changesetProcessingType) {
-        return new UploadDiffTask(layer, toUpload, changeset, changesetProcessingType);
-    }
-
+    public UploadDiffTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
+        return new UploadDiffTask(layer, toUpload, changeset, closeChangesetAfterUpload);
+    }
+
+    /**
+     * The task for uploading a collection of primitives
+     *
+     */
     public class UploadDiffTask extends  PleaseWaitRunnable {
         private boolean uploadCancelled = false;
@@ -488,12 +483,19 @@
         private OsmDataLayer layer;
         private Changeset changeset;
-        private ChangesetProcessingType changesetProcessingType;
-
-        private UploadDiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, ChangesetProcessingType changesetProcessingType) {
+        private boolean closeChangesetAfterUpload;
+
+        /**
+         * 
+         * @param layer  the OSM data layer for which data is uploaded
+         * @param toUpload the collection of primitives to upload
+         * @param changeset the changeset to use for uploading
+         * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading
+         */
+        private UploadDiffTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
             super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
             this.toUpload = toUpload;
             this.layer = layer;
             this.changeset = changeset;
-            this.changesetProcessingType = changesetProcessingType == null ? ChangesetProcessingType.USE_NEW_AND_CLOSE : changesetProcessingType;
+            this.closeChangesetAfterUpload = closeChangesetAfterUpload;
         }
 
@@ -502,5 +504,5 @@
             try {
                 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
-                writer.uploadOsm(layer.data.version, toUpload, changeset,changesetProcessingType, monitor);
+                writer.uploadOsm(layer.data.version, toUpload, changeset,closeChangesetAfterUpload, monitor);
             } catch (Exception sxe) {
                 if (uploadCancelled) {
@@ -525,5 +527,10 @@
                 handleFailedUpload(lastException);
             } else {
+                // run post upload action on the layer
+                //
                 layer.onPostUploadToServer();
+                // refresh changeset dialog with the updated changeset
+                //
+                UploadDialog.getUploadDialog().setOrUpdateChangeset(changeset);
             }
         }
Index: trunk/src/org/openstreetmap/josm/data/osm/Changeset.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/data/osm/Changeset.java	(revision 2115)
@@ -4,6 +4,10 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import javax.print.attribute.standard.MediaSize.Other;
-
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
 
@@ -13,41 +17,83 @@
  *
  */
-public final class Changeset extends OsmPrimitive {
+public final class Changeset implements Tagged {
+    /** the changeset id */
+    private long id;
+    /** the user who owns the changeset */
+    private User user;
+    /** date this changeset was created at */
+    private Date createdAt;
+    /** the date this changeset was closed at*/
+    private Date closedAt;
+    /** indicates whether this changeset is still open or not */
+    private boolean open;
+    /** the min. coordinates of the bounding box of this changeset */
+    private LatLon min;
+    /** the max. coordinates of the bounding box of this changeset */
+    private LatLon max;
+    /** the map of tags */
+    private Map<String,String> tags;
+    /** indicates whether this changeset is incomplete. For an
+     * incomplete changeset we only know its id
+     */
+    private boolean incomplete;
+
+
     /**
-     * Time of last modification to this object. This is not set by JOSM but
-     * read from the server and delivered back to the server unmodified.
+     * Creates a new changeset with id 0.
      */
-    public String end_timestamp = null;
+    public Changeset() {
+        this.id = 0;
+        this.tags = new HashMap<String, String>();
+    }
 
     /**
-     * Time of first modification to this object. This is not set by JOSM but
-     * read from the server and delivered back to the server unmodified.
+     * Creates a changeset with id <code>id</code>. If id > 0, sets incomplete to true.
+     * 
+     * @param id the id
      */
-    public String start_timestamp = null;
-
-    public Changeset() {
-        super(0);
-    }
-
     public Changeset(long id) {
-        super(id);
-    }
-
-    public Changeset(Changeset clone){
-        super(clone.getId());
-        cloneFrom(clone);
-    }
-
-    @Override
+        this.id = id;
+        this.incomplete = id > 0;
+        this.tags = new HashMap<String, String>();
+    }
+
+    /**
+     * Creates a clone of <code>other</code>
+     * 
+     * @param other the other changeset. If null, creates a new changeset with id 0.
+     */
+    public Changeset(Changeset other) {
+        if (other == null) {
+            this.id = 0;
+            this.tags = new HashMap<String, String>();
+        } else if (other.isIncomplete()) {
+            setId(other.getId());
+            this.incomplete = true;
+        } else {
+            cloneFrom(other);
+            this.incomplete = false;
+        }
+    }
+
+    public void cloneFrom(Changeset other) {
+        setId(other.getId());
+        setUser(other.getUser());
+        setCreatedAt(other.getCreatedAt());
+        setClosedAt(other.getClosedAt());
+        setMin(other.getMin());
+        setMax(other.getMax());
+        setKeys(other.getKeys());
+        setOpen(other.isOpen());
+    }
+
     public void visit(Visitor v) {
         v.visit(this);
     }
 
-    public int compareTo(OsmPrimitive other) {
-        if (other instanceof Changeset) return Long.valueOf(getId()).compareTo(other.getId());
-        return 1;
-    }
-
-    @Override
+    public int compareTo(Changeset other) {
+        return Long.valueOf(getId()).compareTo(other.getId());
+    }
+
     public String getName() {
         // no translation
@@ -55,17 +101,207 @@
     }
 
-    @Override
     public String getLocalName(){
         return tr("Changeset {0}",getId());
     }
 
-    @Override
     public String getDisplayName(NameFormatter formatter) {
         return formatter.format(this);
     }
 
-
-    @Override public void cloneFrom(OsmPrimitive osm) {
-        super.cloneFrom(osm);
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Date getCreatedAt() {
+        return createdAt;
+    }
+
+    public void setCreatedAt(Date createdAt) {
+        this.createdAt = createdAt;
+    }
+
+    public Date getClosedAt() {
+        return closedAt;
+    }
+
+    public void setClosedAt(Date closedAt) {
+        this.closedAt = closedAt;
+    }
+
+    public boolean isOpen() {
+        return open;
+    }
+
+    public void setOpen(boolean open) {
+        this.open = open;
+    }
+
+    public LatLon getMin() {
+        return min;
+    }
+
+    public void setMin(LatLon min) {
+        this.min = min;
+    }
+
+    public LatLon getMax() {
+        return max;
+    }
+
+    public void setMax(LatLon max) {
+        this.max = max;
+    }
+
+    public Map<String, String> getKeys() {
+        return tags;
+    }
+
+    public void setKeys(Map<String, String> keys) {
+        this.tags = keys;
+    }
+
+    public boolean isIncomplete() {
+        return incomplete;
+    }
+
+    public void setIncomplete(boolean incomplete) {
+        this.incomplete = incomplete;
+    }
+
+    public void put(String key, String value) {
+        this.tags.put(key, value);
+    }
+
+    public String get(String key) {
+        return this.tags.get(key);
+    }
+
+    public void remove(String key) {
+        this.tags.remove(key);
+    }
+
+    public boolean hasEqualSemanticAttributes(Changeset other) {
+        if (other == null)
+            return false;
+        if (closedAt == null) {
+            if (other.closedAt != null)
+                return false;
+        } else if (!closedAt.equals(other.closedAt))
+            return false;
+        if (createdAt == null) {
+            if (other.createdAt != null)
+                return false;
+        } else if (!createdAt.equals(other.createdAt))
+            return false;
+        if (id != other.id)
+            return false;
+        if (max == null) {
+            if (other.max != null)
+                return false;
+        } else if (!max.equals(other.max))
+            return false;
+        if (min == null) {
+            if (other.min != null)
+                return false;
+        } else if (!min.equals(other.min))
+            return false;
+        if (open != other.open)
+            return false;
+        if (tags == null) {
+            if (other.tags != null)
+                return false;
+        } else if (!tags.equals(other.tags))
+            return false;
+        if (user == null) {
+            if (other.user != null)
+                return false;
+        } else if (!user.equals(other.user))
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (id ^ (id >>> 32));
+        if (id > 0)
+            return prime * result + getClass().hashCode();
+        result = prime * result + ((closedAt == null) ? 0 : closedAt.hashCode());
+        result = prime * result + ((createdAt == null) ? 0 : createdAt.hashCode());
+        result = prime * result + ((max == null) ? 0 : max.hashCode());
+        result = prime * result + ((min == null) ? 0 : min.hashCode());
+        result = prime * result + (open ? 1231 : 1237);
+        result = prime * result + ((tags == null) ? 0 : tags.hashCode());
+        result = prime * result + ((user == null) ? 0 : user.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Changeset other = (Changeset) obj;
+        if (this.id > 0 && other.id == this.id)
+            return true;
+        if (closedAt == null) {
+            if (other.closedAt != null)
+                return false;
+        } else if (!closedAt.equals(other.closedAt))
+            return false;
+        if (createdAt == null) {
+            if (other.createdAt != null)
+                return false;
+        } else if (!createdAt.equals(other.createdAt))
+            return false;
+        if (id != other.id)
+            return false;
+        if (max == null) {
+            if (other.max != null)
+                return false;
+        } else if (!max.equals(other.max))
+            return false;
+        if (min == null) {
+            if (other.min != null)
+                return false;
+        } else if (!min.equals(other.min))
+            return false;
+        if (open != other.open)
+            return false;
+        if (tags == null) {
+            if (other.tags != null)
+                return false;
+        } else if (!tags.equals(other.tags))
+            return false;
+        if (user == null) {
+            if (other.user != null)
+                return false;
+        } else if (!user.equals(other.user))
+            return false;
+        return true;
+    }
+
+    public boolean hasKeys() {
+        return !tags.keySet().isEmpty();
+    }
+
+    public Collection<String> keySet() {
+        return tags.keySet();
     }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 2115)
@@ -326,8 +326,7 @@
         Collection<? extends OsmPrimitive> primitives = null;
         switch(type) {
-        case NODE: primitives = nodes; break;
-        case WAY: primitives = ways; break;
-        case RELATION: primitives = relations; break;
-        case CHANGESET: throw new IllegalArgumentException(tr("unsupported value ''{0}'' or parameter ''{1}''", type, "type"));
+            case NODE: primitives = nodes; break;
+            case WAY: primitives = ways; break;
+            case RELATION: primitives = relations; break;
         }
         for (OsmPrimitive primitive : primitives) {
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java	(revision 2115)
@@ -33,5 +33,5 @@
  * @author imi
  */
-abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {
+abstract public class OsmPrimitive implements Comparable<OsmPrimitive>, Tagged {
 
     static public <T extends OsmPrimitive>  List<T> getFilteredList(Collection<OsmPrimitive> list, Class<T> type) {
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java	(revision 2115)
@@ -7,6 +7,5 @@
     NODE ("node"),
     WAY  ("way"),
-    RELATION ("relation"),
-    CHANGESET ("changeset");
+    RELATION ("relation");
 
     private String apiTypeName;
@@ -35,5 +34,4 @@
         if (cls.equals(Way.class)) return WAY;
         if (cls.equals(Relation.class)) return RELATION;
-        if (cls.equals(Changeset.class)) return CHANGESET;
         throw new IllegalArgumentException(tr("parameter ''{0}'' is not an acceptable class, got ''{1}''", "cls", cls.toString()));
     }
Index: trunk/src/org/openstreetmap/josm/data/osm/Tagged.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Tagged.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/data/osm/Tagged.java	(revision 2115)
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Collection;
+import java.util.Map;
+/**
+ * Objects implement Tagged if they provide a map of key/value pairs.
+ * 
+ * 
+ */
+// FIXME: better naming? setTags(), getTags(), getKeys() instead of keySet() ?
+//
+public interface Tagged {
+    /**
+     * Sets the map of key/value pairs
+     * 
+     * @param keys the map of key value pairs. If null, reset to the empty map.
+     */
+    void setKeys(Map<String,String> keys);
+
+    /**
+     * Replies the map of key/value pairs. Never null, but may be the empty map.
+     * 
+     * @return the map of key/value pairs
+     */
+    Map<String,String> getKeys();
+
+    /**
+     * Sets a key/value pairs
+     * 
+     * @param key the key
+     * @param value the value. If null, removes the key/value pair.
+     */
+    void put(String key, String value);
+
+    /**
+     * Replies the value of the given key; null, if there is no value for this key
+     * 
+     * @param key the key
+     * @return the value
+     */
+    String get(String key);
+
+    /**
+     * Removes a given key/value pair
+     * 
+     * @param key the key
+     */
+    void remove(String key);
+
+    /**
+     * Replies true, if there is at least one key/value pair; false, otherwise
+     * 
+     * @return true, if there is at least one key/value pair; false, otherwise
+     */
+    boolean hasKeys();
+
+    /**
+     * Replies the set of keys
+     * 
+     * @return the set of keys
+     */
+    Collection<String> keySet();
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/UserInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/UserInfo.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/data/osm/UserInfo.java	(revision 2115)
@@ -0,0 +1,73 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.Date;
+import java.util.List;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+
+public class UserInfo {
+    /** the user id */
+    private long id;
+    /** the display name */
+    private String displayName;
+    /** the date this user was created */
+    private Date accountCreated;
+    /** the home location */
+    private LatLon home;
+    /** the zoom level for the home location */
+    private int homeZoom;
+    /** the profile description */
+    private String description;
+    /** the list of preferred languages */
+    private List<String> languages;
+
+    public UserInfo() {
+        id = 0;
+    }
+
+    public long getId() {
+        return id;
+    }
+    public void setId(long id) {
+        this.id = id;
+    }
+    public String getDisplayName() {
+        return displayName;
+    }
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+    public Date getAccountCreated() {
+        return accountCreated;
+    }
+    public void setAccountCreated(Date accountCreated) {
+        this.accountCreated = accountCreated;
+    }
+    public LatLon getHome() {
+        return home;
+    }
+    public void setHome(LatLon home) {
+        this.home = home;
+    }
+    public String getDescription() {
+        return description;
+    }
+    public void setDescription(String description) {
+        this.description = description;
+    }
+    public List<String> getLanguages() {
+        return languages;
+    }
+    public void setLanguages(List<String> languages) {
+        this.languages = languages;
+    }
+
+    public int getHomeZoom() {
+        return homeZoom;
+    }
+
+    public void setHomeZoom(int homeZoom) {
+        this.homeZoom = homeZoom;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 2115)
@@ -58,5 +58,5 @@
 import org.openstreetmap.josm.actions.ShowStatusReportAction;
 import org.openstreetmap.josm.actions.SplitWayAction;
-import org.openstreetmap.josm.actions.StopChangesetAction;
+import org.openstreetmap.josm.actions.CloseChangesetAction;
 import org.openstreetmap.josm.actions.ToggleGPXLinesAction;
 import org.openstreetmap.josm.actions.UnGlueAction;
@@ -100,5 +100,5 @@
     public final DownloadAction download = new DownloadAction();
     public final DownloadReferrersAction downloadReferrers = new DownloadReferrersAction();
-    public final StopChangesetAction stopChangesetAction = new StopChangesetAction();
+    public final CloseChangesetAction closeChangesetAction = new CloseChangesetAction();
     public final JosmAction update = new UpdateDataAction();
     public final JosmAction updateSelection = new UpdateSelectionAction();
@@ -208,5 +208,5 @@
         add(fileMenu, update);
         add(fileMenu, updateSelection);
-        add(fileMenu, stopChangesetAction);
+        add(fileMenu, closeChangesetAction);
         fileMenu.addSeparator();
         add(fileMenu, exit);
Index: trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java	(revision 2115)
@@ -80,7 +80,4 @@
         resolvers = new HashMap<OsmPrimitiveType, TagConflictResolver>();
         for (OsmPrimitiveType type: OsmPrimitiveType.values()) {
-            if (type.equals(OsmPrimitiveType.CHANGESET)) {
-                continue;
-            }
             resolvers.put(type, new TagConflictResolver());
             resolvers.get(type).getModel().addPropertyChangeListener(this);
@@ -148,7 +145,7 @@
         String msg = "";
         switch(type) {
-        case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;
-        case WAY: msg= trn("{0} way", "{0} ways", count, count); break;
-        case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;
+            case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;
+            case WAY: msg= trn("{0} way", "{0} ways", count, count); break;
+            case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;
         }
         return msg;
@@ -446,7 +443,7 @@
                 String msg = "";
                 switch(type) {
-                case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
-                case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
-                case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
+                    case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
+                    case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
+                    case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
                 }
                 text = text.equals("") ? msg : text + ", " + msg;
@@ -472,7 +469,7 @@
                 String msg = "";
                 switch(type) {
-                case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
-                case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
-                case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
+                    case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
+                    case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
+                    case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
                 }
                 text = text.equals("") ? msg : text + ", " + msg;
@@ -491,7 +488,7 @@
 
                 switch(column) {
-                case 0: renderNumTags(info); break;
-                case 1: renderFrom(info); break;
-                case 2: renderTo(info); break;
+                    case 0: renderNumTags(info); break;
+                    case 1: renderFrom(info); break;
+                    case 2: renderTo(info); break;
                 }
             }
Index: trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/gui/io/ChangesetCellRenderer.java	(revision 2115)
@@ -0,0 +1,71 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.text.SimpleDateFormat;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * A {@see ListCellRenderer} for the list of changesets in the upload dialog.
+ * 
+ *
+ */
+public class ChangesetCellRenderer extends JLabel implements ListCellRenderer {
+    private ImageIcon icon ;
+    public ChangesetCellRenderer() {
+        icon = ImageProvider.get("data", "changeset");
+        setOpaque(true);
+    }
+
+    protected String buildToolTipText(Changeset cs) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<html>");
+        sb.append("<strong>").append(tr("Changeset id:")).append("</strong>").append(cs.getId()).append("<br>");
+        if (cs.getCreatedAt() != null) {
+            SimpleDateFormat df  =new SimpleDateFormat();
+            sb.append("<strong>").append(tr("Created at:")).append("</strong>").append(df.format(cs.getCreatedAt())).append("<br>");
+        }
+        if (cs.get("comment") != null) {
+            sb.append("<strong>").append(tr("Changeset comment:")).append("</strong>").append(cs.get("comment")).append("<br>");
+        }
+        return sb.toString();
+    }
+
+    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+            boolean cellHasFocus) {
+        Changeset cs = (Changeset)value;
+        if (isSelected) {
+            setForeground(UIManager.getColor("List.selectionForeground"));
+            setBackground(UIManager.getColor("List.selectionBackground"));
+        } else {
+            setForeground(UIManager.getColor("List.foreground"));
+            setBackground(UIManager.getColor("List.background"));
+        }
+        if (cs != null) {
+            setIcon(icon);
+            StringBuffer sb = new StringBuffer();
+            if (cs.get("comment") != null) {
+                sb.append(cs.getId()).append(" - ").append(cs.get("comment"));
+            } else if (cs.get("name") != null) {
+                sb.append(cs.getId()).append(" - ").append(cs.get("name"));
+            } else {
+                sb.append(tr("Changeset {0}", cs.getId()));
+            }
+            setText(sb.toString());
+            setToolTipText(buildToolTipText(cs));
+        } else {
+            setText(tr("No open changeset"));
+        }
+        return this;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetDialog.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetDialog.java	(revision 2115)
@@ -0,0 +1,153 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListModel;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.gui.SideButton;
+import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.WindowGeometry;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class CloseChangesetDialog extends JDialog {
+
+    private JList lstOpenChangesets;
+    private boolean canceled;
+    private DefaultListModel model;
+
+    protected JPanel buildTopPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+        pnl.setLayout(new BorderLayout());
+        pnl.add(new JLabel(tr("<html>Please select the changesets you want to close</html>")), BorderLayout.CENTER);
+        return pnl;
+    }
+
+    protected JPanel buildCenterPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new BorderLayout());
+        model = new DefaultListModel();
+        pnl.add(lstOpenChangesets = new JList(model), BorderLayout.CENTER);
+        lstOpenChangesets.setCellRenderer(new ChangesetCellRenderer());
+        return pnl;
+    }
+
+    protected JPanel buildSouthPanel() {
+        JPanel pnl = new JPanel();
+        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
+
+        // -- close action
+        CloseAction closeAction = new CloseAction();
+        lstOpenChangesets.addListSelectionListener(closeAction);
+        pnl.add(new SideButton(closeAction));
+        pnl.add(new SideButton(new CancelAction()));
+        return pnl;
+    }
+
+    protected void build() {
+        setTitle(tr("Open changesets"));
+        getContentPane().setLayout(new BorderLayout());
+        getContentPane().add(buildTopPanel(), BorderLayout.NORTH);
+        getContentPane().add(buildCenterPanel(), BorderLayout.CENTER);
+        getContentPane().add(buildSouthPanel(), BorderLayout.SOUTH);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            new WindowGeometry(
+                    getClass().getName() + ".geometry",
+                    WindowGeometry.centerInWindow(Main.parent, new Dimension(300,300))
+            ).apply(this);
+        } else {
+            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
+        }
+        super.setVisible(visible);
+    }
+
+    public CloseChangesetDialog() {
+        super(JOptionPane.getFrameForComponent(Main.parent), true /* modal */);
+        build();
+    }
+
+    class CloseAction extends AbstractAction implements ListSelectionListener {
+        public CloseAction() {
+            putValue(NAME, tr("Close changesets"));
+            //putValue(SMALL_ICON, ImageProvider.get("cancel"));
+            putValue(SHORT_DESCRIPTION, tr("Close the selected open changesets"));
+            refreshEnabledState();
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(false);
+            setVisible(false);
+        }
+
+        protected void refreshEnabledState() {
+            setEnabled(lstOpenChangesets.getSelectedValues() != null && lstOpenChangesets.getSelectedValues().length > 0);
+        }
+
+        public void valueChanged(ListSelectionEvent e) {
+            refreshEnabledState();
+        }
+    }
+
+    class CancelAction extends AbstractAction {
+
+        public CancelAction() {
+            putValue(NAME, tr("Cancel"));
+            putValue(SMALL_ICON, ImageProvider.get("cancel"));
+            putValue(SHORT_DESCRIPTION, tr("Cancel closeing of changesets"));
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            setCanceled(true);
+            setVisible(false);
+        }
+    }
+
+    public boolean isCanceled() {
+        return canceled;
+    }
+
+    protected void setCanceled(boolean canceled) {
+        this.canceled = canceled;
+    }
+
+    public void setChangesets(Collection<Changeset> changesets) {
+        if (changesets == null) {
+            changesets = new ArrayList<Changeset>();
+        }
+        model.removeAllElements();
+        for (Changeset cs: changesets) {
+            model.addElement(cs);
+        }
+    }
+
+    public Collection<Changeset> getSelectedChangesets() {
+        Object [] sel = lstOpenChangesets.getSelectedValues();
+        ArrayList<Changeset> ret = new ArrayList<Changeset>();
+        for (Object o: sel) {
+            ret.add((Changeset)o);
+        }
+        return ret;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetTask.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/gui/io/CloseChangesetTask.java	(revision 2115)
@@ -0,0 +1,87 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+/**
+ * A task for closing a collection of changesets.
+ * 
+ */
+public class CloseChangesetTask extends PleaseWaitRunnable {
+    private boolean cancelled;
+    private Exception lastException;
+    private Collection<Changeset> changesets;
+    private ArrayList<Changeset> closedChangesets;
+
+    /**
+     * Closes all changesets in <code>changesets</code> if they are not null, if they
+     * are still open and if they have an id > 0. Other changesets in the collection
+     * are ignored.
+     * 
+     * @param changesets  the collection of changesets. Empty collection assumes, if null.
+     */
+    public CloseChangesetTask(Collection<Changeset> changesets) {
+        super(tr("Closing changeset"), false /* don't ignore exceptions */);
+        if (changesets == null) {
+            changesets = new ArrayList<Changeset>();
+        }
+        this.changesets = changesets;
+        this.closedChangesets = new ArrayList<Changeset>();
+    }
+
+    @Override
+    protected void cancel() {
+        this.cancelled = true;
+        OsmApi.getOsmApi().cancel();
+    }
+
+    @Override
+    protected void finish() {
+        if (cancelled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+        }
+        SwingUtilities.invokeLater(
+                new Runnable() {
+                    public void run() {
+                        for (Changeset cs: closedChangesets) {
+                            UploadDialog.getUploadDialog().setOrUpdateChangeset(cs);
+                        }
+                    }
+                }
+        );
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            for (Changeset cs: changesets) {
+                if (cancelled) return;
+                if (cs == null || cs.getId() <= 0 || ! cs.isOpen()) {
+                    continue;
+                }
+                getProgressMonitor().subTask(tr("Closing changeset {0}", cs.getId()));
+                OsmApi.getOsmApi().closeChangeset(cs, getProgressMonitor().createSubTaskMonitor(1, false));
+                closedChangesets.add(cs);
+            }
+        } catch(Exception e) {
+            if (cancelled)
+                return;
+            lastException = e;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTask.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTask.java	(revision 2115)
@@ -0,0 +1,116 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.UserInfo;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.io.UploadDialog.OpenChangesetModel;
+import org.openstreetmap.josm.io.ChangesetQuery;
+import org.openstreetmap.josm.io.OsmServerChangesetReader;
+import org.openstreetmap.josm.io.OsmServerUserInfoReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * This is a task for downloading the open changesets of the current user
+ * from the OSM server.
+
+ *
+ */
+public class DownloadOpenChangesetsTask extends PleaseWaitRunnable {
+
+    private boolean cancelled;
+    private OsmServerChangesetReader reader;
+    private List<Changeset> changesets;
+    private OpenChangesetModel model;
+    private Exception lastException;
+    private UserInfo userInfo;
+
+    /**
+     * 
+     * @param model provides the user id of the current user and accepts the changesets
+     * after download
+     */
+    public DownloadOpenChangesetsTask(OpenChangesetModel model) {
+        super(tr("Downloading open changesets ...", false /* don't ignore exceptions */));
+        this.model = model;
+    }
+
+    @Override
+    protected void cancel() {
+        this.cancelled = true;
+        reader.cancel();
+    }
+
+    @Override
+    protected void finish() {
+        if (cancelled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+            return;
+        }
+        if (changesets.isEmpty()) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("There are no open changesets"),
+                    tr("No open changesets"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+        }
+        SwingUtilities.invokeLater(
+                new Runnable() {
+                    public void run() {
+                        model.addOrUpdate(changesets);
+                    }
+                }
+        );
+    }
+
+    /**
+     * Fetch the user info from the server. This is necessary if we don't know
+     * the users id yet
+     * 
+     * @return the user info
+     * @throws OsmTransferException thrown in case of any communication exception
+     */
+    protected UserInfo fetchUserInfo() throws OsmTransferException {
+        OsmServerUserInfoReader reader = new OsmServerUserInfoReader();
+        return reader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false));
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        try {
+            if (model.getUserId()== 0) {
+                userInfo = fetchUserInfo();
+                model.setUserId(userInfo.getId());
+            }
+            if (cancelled)
+                return;
+            reader = new OsmServerChangesetReader();
+            ChangesetQuery query = new ChangesetQuery().forUser(model.getUserId()).beingOpen();
+            changesets = reader.queryChangesets(
+                    query,
+                    getProgressMonitor().createSubTaskMonitor(1, false /* not internal */)
+            );
+        } catch(Exception e) {
+            if (cancelled)
+                return;
+            lastException = e;
+        }
+    }
+
+    public boolean isCancelled() {
+        return cancelled;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java	(revision 2115)
@@ -33,5 +33,4 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.UploadAction;
-import org.openstreetmap.josm.actions.UploadAction.UploadConfirmationHook;
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
 import org.openstreetmap.josm.gui.SideButton;
@@ -299,6 +298,6 @@
         public void cancel() {
             switch(model.getMode()) {
-            case EDITING_DATA: cancelWhenInEditingModel(); break;
-            case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;
+                case EDITING_DATA: cancelWhenInEditingModel(); break;
+                case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;
             }
         }
@@ -334,6 +333,6 @@
                 Mode mode = (Mode)evt.getNewValue();
                 switch(mode) {
-                case EDITING_DATA: setEnabled(true); break;
-                case UPLOADING_AND_SAVING: setEnabled(false); break;
+                    case EDITING_DATA: setEnabled(true); break;
+                    case UPLOADING_AND_SAVING: setEnabled(false); break;
                 }
             }
@@ -368,6 +367,6 @@
                 SaveLayersModel.Mode mode = (SaveLayersModel.Mode)evt.getNewValue();
                 switch(mode) {
-                case EDITING_DATA: setEnabled(true); break;
-                case UPLOADING_AND_SAVING: setEnabled(false); break;
+                    case EDITING_DATA: setEnabled(true); break;
+                    case UPLOADING_AND_SAVING: setEnabled(false); break;
                 }
             }
@@ -411,6 +410,6 @@
                         layerInfo.getLayer(),
                         monitor,
-                        UploadAction.UploadConfirmationHook.getUploadDialog().getChangeset(),
-                        UploadAction.UploadConfirmationHook.getUploadDialog().getChangesetProcessingType()
+                        UploadDialog.getUploadDialog().getChangeset(),
+                        UploadDialog.getUploadDialog().isDoCloseAfterUpload()
                 );
                 currentFuture = worker.submit(currentTask);
Index: trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 2115)
@@ -6,4 +6,5 @@
 
 import java.awt.BorderLayout;
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
@@ -12,18 +13,24 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 import javax.swing.AbstractAction;
 import javax.swing.AbstractListModel;
 import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
 import javax.swing.ButtonGroup;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
 import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
@@ -34,6 +41,10 @@
 import javax.swing.JScrollPane;
 import javax.swing.JTabbedPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.UIManager;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
 
 import org.openstreetmap.josm.Main;
@@ -46,5 +57,4 @@
 import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
 import org.openstreetmap.josm.gui.tagging.TagModel;
-import org.openstreetmap.josm.io.ChangesetProcessingType;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.GBC;
@@ -60,5 +70,21 @@
 public class UploadDialog extends JDialog {
 
+
     public static final String HISTORY_KEY = "upload.comment.history";
+
+    /**  the unique instance of the upload dialog */
+    static private UploadDialog uploadDialog;
+
+    /**
+     * Replies the unique instance of the upload dialog
+     * 
+     * @return the unique instance of the upload dialog
+     */
+    static public UploadDialog getUploadDialog() {
+        if (uploadDialog == null) {
+            uploadDialog = new UploadDialog();
+        }
+        return uploadDialog;
+    }
 
     /** the list with the added primitives */
@@ -77,15 +103,9 @@
     private JPanel pnlLists;
     /** checkbox for selecting whether an atomic upload is to be used  */
-    private JCheckBox cbUseAtomicUpload;
-    /** input field for changeset comment */
-    private SuggestingJHistoryComboBox cmt;
-    /** ui component for editing changeset tags */
     private TagEditorPanel tagEditorPanel;
     /** the tabbed pane used below of the list of primitives  */
     private JTabbedPane southTabbedPane;
-    /** the button group with the changeset processing types */
-    private ButtonGroup bgChangesetHandlingOptions;
-    /** radio buttons for selecting a changeset processing type */
-    private Map<ChangesetProcessingType, JRadioButton> rbChangesetHandlingOptions;
+
+    private ChangesetSelectionPanel pnlChangesetSelection;
 
     private boolean canceled = false;
@@ -104,93 +124,4 @@
     }
 
-    /**
-     * builds the panel with the ui components for controlling how the changeset
-     * should be processed (opening/closing a changeset)
-     * 
-     * @return the panel with the ui components for controlling how the changeset
-     * should be processed
-     */
-    protected JPanel buildChangesetHandlingControlPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));
-        bgChangesetHandlingOptions = new ButtonGroup();
-        rbChangesetHandlingOptions = new HashMap<ChangesetProcessingType, JRadioButton>();
-        ChangesetProcessingTypeChangedAction a = new ChangesetProcessingTypeChangedAction();
-        for(ChangesetProcessingType type: ChangesetProcessingType.values()) {
-            rbChangesetHandlingOptions.put(type, new JRadioButton());
-            rbChangesetHandlingOptions.get(type).addActionListener(a);
-        }
-        JRadioButton rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_CLOSE);
-        rb.setText(tr("Use a new changeset and close it"));
-        rb.setToolTipText(tr("Select to upload the data using a new changeset and to close the changeset after the upload"));
-
-        rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_LEAVE_OPEN);
-        rb.setText(tr("Use a new changeset and leave it open"));
-        rb.setToolTipText(tr("Select to upload the data using a new changeset and to leave the changeset open after the upload"));
-
-        pnl.add(new JLabel(tr("Upload to a new or to an existing changeset?")));
-        pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_CLOSE));
-        pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_NEW_AND_LEAVE_OPEN));
-        pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_CLOSE));
-        pnl.add(rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_LEAVE_OPEN));
-
-        for(ChangesetProcessingType type: ChangesetProcessingType.values()) {
-            rbChangesetHandlingOptions.get(type).setVisible(false);
-            bgChangesetHandlingOptions.add(rbChangesetHandlingOptions.get(type));
-        }
-        return pnl;
-    }
-
-    /**
-     * build the panel with the widgets for controlling how the changeset should be processed
-     * (atomic upload or not, comment, opening/closing changeset)
-     * 
-     * @return
-     */
-    protected JPanel buildChangesetControlPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new BoxLayout(pnl, BoxLayout.Y_AXIS));
-        pnl.add(cbUseAtomicUpload = new JCheckBox(tr("upload all changes in one request")));
-        cbUseAtomicUpload.setToolTipText(tr("Enable to upload all changes in one request, disable to use one request per changed primitive"));
-        boolean useAtomicUpload = Main.pref.getBoolean("osm-server.atomic-upload", true);
-        cbUseAtomicUpload.setSelected(useAtomicUpload);
-        cbUseAtomicUpload.setEnabled(OsmApi.getOsmApi().hasSupportForDiffUploads());
-
-        pnl.add(buildChangesetHandlingControlPanel());
-        return pnl;
-    }
-
-    /**
-     * builds the upload control panel
-     * 
-     * @return
-     */
-    protected JPanel buildUploadControlPanel() {
-        JPanel pnl = new JPanel();
-        pnl.setLayout(new GridBagLayout());
-        pnl.add(new JLabel(tr("Provide a brief comment for the changes you are uploading:")), GBC.eol().insets(0, 5, 10, 3));
-        cmt = new SuggestingJHistoryComboBox();
-        List<String> cmtHistory = new LinkedList<String>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>()));
-        cmt.setHistory(cmtHistory);
-        cmt.getEditor().addActionListener(
-                new ActionListener() {
-                    public void actionPerformed(ActionEvent e) {
-                        TagModel tm = tagEditorPanel.getModel().get("comment");
-                        if (tm == null) {
-                            tagEditorPanel.getModel().add(new TagModel("comment", cmt.getText()));
-                        } else {
-                            tm.setValue(cmt.getText());
-                        }
-                        tagEditorPanel.getModel().fireTableDataChanged();
-                    }
-                }
-        );
-        pnl.add(cmt, GBC.eol().fill(GBC.HORIZONTAL));
-
-        // configuration options for atomic upload
-        //
-        pnl.add(buildChangesetControlPanel(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
-        return pnl;
-    }
 
     protected JPanel buildContentPanel() {
@@ -211,7 +142,8 @@
         //
         southTabbedPane = new JTabbedPane();
-        southTabbedPane.add(buildUploadControlPanel());
+        southTabbedPane.add(new JPanel());
         tagEditorPanel = new TagEditorPanel();
         southTabbedPane.add(tagEditorPanel);
+        southTabbedPane.setComponentAt(0, pnlChangesetSelection = new ChangesetSelectionPanel());
         southTabbedPane.setTitleAt(0, tr("Settings"));
         southTabbedPane.setTitleAt(1, tr("Tags of new changeset"));
@@ -340,22 +272,8 @@
 
     /**
-     * Replies true if a valid changeset comment has been entered in this dialog
-     * 
-     * @return true if a valid changeset comment has been entered in this dialog
-     */
-    public boolean hasChangesetComment() {
-        if (!getChangesetProcessingType().isUseNew())
-            return true;
-        return cmt.getText().trim().length() >= 3;
-    }
-
-    /**
      * Remembers the user input in the preference settings
      */
     public void rememberUserInput() {
-        // store the history of comments
-        cmt.addCurrentItemToHistory();
-        Main.pref.putCollection(HISTORY_KEY, cmt.getHistory());
-        Main.pref.put("osm-server.atomic-upload", cbUseAtomicUpload.isSelected());
+        pnlChangesetSelection.rememberUserInput();
     }
 
@@ -365,25 +283,5 @@
     public void startUserInput() {
         tagEditorPanel.initAutoCompletion(Main.main.getEditLayer());
-        initChangesetProcessingType();
-        cmt.getEditor().selectAll();
-        cmt.requestFocus();
-    }
-
-    /**
-     * Replies the current changeset processing type
-     * 
-     * @return the current changeset processing type
-     */
-    public ChangesetProcessingType getChangesetProcessingType() {
-        ChangesetProcessingType changesetProcessingType = null;
-        for (ChangesetProcessingType type: ChangesetProcessingType.values()) {
-            if (rbChangesetHandlingOptions.get(type).isSelected()) {
-                changesetProcessingType = type;
-                break;
-            }
-        }
-        return changesetProcessingType == null ?
-                ChangesetProcessingType.USE_NEW_AND_CLOSE :
-                    changesetProcessingType;
+        pnlChangesetSelection.startUserInput();
     }
 
@@ -394,55 +292,26 @@
      */
     public Changeset getChangeset() {
-        Changeset changeset = new Changeset();
-        tagEditorPanel.getModel().applyToPrimitive(changeset);
-        changeset.put("comment", cmt.getText());
-        return changeset;
-    }
-
-    /**
-     * initializes the panel depending on the possible changeset processing
-     * types
-     */
-    protected void initChangesetProcessingType() {
-        for (ChangesetProcessingType type: ChangesetProcessingType.values()) {
-            // show options for new changeset, disable others
-            //
-            rbChangesetHandlingOptions.get(type).setVisible(type.isUseNew());
-        }
-        if (OsmApi.getOsmApi().getCurrentChangeset() != null) {
-            Changeset cs = OsmApi.getOsmApi().getCurrentChangeset();
-            for (ChangesetProcessingType type: ChangesetProcessingType.values()) {
-                // show options for using existing changeset
-                //
-                if (!type.isUseNew()) {
-                    rbChangesetHandlingOptions.get(type).setVisible(true);
-                }
-            }
-            JRadioButton rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_CLOSE);
-            rb.setText(tr("Use the existing changeset {0} and close it after upload",cs.getId()));
-            rb.setToolTipText(tr("Select to upload to the existing changeset {0} and to close the changeset after this upload",cs.getId()));
-
-            rb = rbChangesetHandlingOptions.get(ChangesetProcessingType.USE_EXISTING_AND_LEAVE_OPEN);
-            rb.setText(tr("Use the existing changeset {0} and leave it open",cs.getId()));
-            rb.setToolTipText(tr("Select to upload to the existing changeset {0} and to leave the changeset open for further uploads",cs.getId()));
-
-            rbChangesetHandlingOptions.get(getChangesetProcessingType()).setSelected(true);
-
-        } else {
-            ChangesetProcessingType type = getChangesetProcessingType();
-            if (!type.isUseNew()) {
-                type = ChangesetProcessingType.USE_NEW_AND_CLOSE;
-            }
-            rbChangesetHandlingOptions.get(type).setSelected(true);
-        }
-        ChangesetProcessingType type = getChangesetProcessingType();
-        if (type.isUseNew() || (! type.isUseNew() && OsmApi.getOsmApi().getCurrentChangeset() == null)) {
-            Changeset cs = new Changeset();
-            cs.put("created_by", getDefaultCreatedBy());
-            tagEditorPanel.getModel().initFromPrimitive(cs);
-        } else {
-            Changeset cs = OsmApi.getOsmApi().getCurrentChangeset();
-            tagEditorPanel.getModel().initFromPrimitive(cs);
-        }
+        Changeset cs = pnlChangesetSelection.getChangeset();
+        tagEditorPanel.getModel().applyToPrimitive(cs);
+        cs.put("comment", getUploadComment());
+        return cs;
+    }
+
+    /**
+     * Sets or updates the changeset cs.
+     * If cs is null, does nothing.
+     * If cs.getId() == 0 does nothing.
+     * If cs.getId() > 0 and cs is open, adds it to the list of open
+     * changesets. If it is closed, removes it from the list of open
+     * changesets.
+     * 
+     * @param cs the changeset
+     */
+    public void setOrUpdateChangeset(Changeset cs) {
+        pnlChangesetSelection.setOrUpdateChangeset(cs);
+    }
+
+    public boolean isDoCloseAfterUpload() {
+        return pnlChangesetSelection.isCloseAfterUpload();
     }
 
@@ -457,38 +326,11 @@
     }
 
-    /**
-     * refreshes  the panel depending on a changeset processing type
-     * 
-     * @param type the changeset processing type
-     */
-    protected void switchToProcessingType(ChangesetProcessingType type) {
-        if (type.isUseNew()) {
-            southTabbedPane.setTitleAt(1, tr("Tags of new changeset"));
-            // init a new changeset from the currently edited tags
-            // and the comment field
-            //
-            Changeset cs = new Changeset(getChangeset());
-            if (cs.get("created_by") == null) {
-                cs.put("created_by", getDefaultCreatedBy());
-            }
-            cs.put("comment", this.cmt.getText());
-            tagEditorPanel.getModel().initFromPrimitive(cs);
-        } else {
-            Changeset cs = OsmApi.getOsmApi().getCurrentChangeset();
-            if (cs != null) {
-                cs.put("comment", this.cmt.getText());
-                cs.setKeys(getChangeset().getKeys());
-                southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
-                tagEditorPanel.getModel().initFromPrimitive(cs);
-            }
-        }
-    }
-
-    public String getUploadComment() {
+    protected String getUploadComment() {
         switch(southTabbedPane.getSelectedIndex()) {
-        case 0: return cmt.getText();
-        case 1:
-            TagModel tm = tagEditorPanel.getModel().get("comment");
-            return tm == null? "" : tm.getValue();
+            case 0:
+                pnlChangesetSelection.getUploadComment();
+            case 1:
+                TagModel tm = tagEditorPanel.getModel().get("comment");
+                return tm == null? "" : tm.getValue();
         }
         return "";
@@ -509,5 +351,5 @@
                     getClass().getName() + ".geometry",
                     WindowGeometry.centerInWindow(
-                            JOptionPane.getFrameForComponent(Main.parent),
+                            Main.parent,
                             new Dimension(400,600)
                     )
@@ -520,12 +362,12 @@
     }
 
-    class ChangesetProcessingTypeChangedAction implements ActionListener {
-        public void actionPerformed(ActionEvent e) {
-            ChangesetProcessingType type = getChangesetProcessingType();
-            switchToProcessingType(type);
-        }
-    }
-
-
+    /**
+     * This change listener is triggered when current tab in the tabbed pane in
+     * the lower half of the dialog is changed.
+     * 
+     * It's main purpose is to keep the content in the text field for the changeset
+     * comment in sync with the changeset tag "comment".
+     *
+     */
     class TabbedPaneChangeLister implements ChangeListener {
 
@@ -560,18 +402,15 @@
             TagModel tm = getOrCreateCommentTag();
             tm.setName("comment");
-            tm.setValue(cmt.getText().trim());
-            if (cmt.getText().trim().equals("")) {
+            tm.setValue(pnlChangesetSelection.getUploadComment().trim());
+            if (pnlChangesetSelection.getUploadComment().trim().equals("")) {
                 removeCommentTag();
             }
             tagEditorPanel.getModel().fireTableDataChanged();
         }
-
 
         public void stateChanged(ChangeEvent e) {
             if (southTabbedPane.getSelectedIndex() ==0) {
                 TagModel tm = tagEditorPanel.getModel().get("comment");
-                cmt.setText(tm == null ? "" : tm.getValue());
-                cmt.getEditor().selectAll();
-                cmt.requestFocus();
+                pnlChangesetSelection.initEditingOfUploadComment(tm == null ? "" : tm.getValue());
             } else if (southTabbedPane.getSelectedIndex() == 1) {
                 refreshCommentTag();
@@ -580,4 +419,8 @@
     }
 
+    /**
+     * Handles an upload
+     *
+     */
     class UploadAction extends AbstractAction {
         public UploadAction() {
@@ -599,6 +442,6 @@
             if (getUploadComment().trim().length() < 3) {
                 warnIllegalUploadComment();
-                cmt.getEditor().selectAll();
-                cmt.requestFocus();
+                southTabbedPane.setSelectedIndex(0);
+                pnlChangesetSelection.initEditingOfUploadComment(getUploadComment());
                 return;
             }
@@ -609,4 +452,8 @@
     }
 
+    /**
+     * Action for canceling the dialog
+     *
+     */
     class CancelAction extends AbstractAction {
         public CancelAction() {
@@ -623,4 +470,8 @@
     }
 
+    /**
+     * A simple list of OSM primitives.
+     *
+     */
     class PrimitiveList extends JList {
         public PrimitiveList() {
@@ -633,4 +484,8 @@
     }
 
+    /**
+     * A list model for a list of OSM primitives.
+     *
+     */
     class PrimitiveListModel extends AbstractListModel{
         private List<OsmPrimitive> primitives;
@@ -664,4 +519,8 @@
     }
 
+    /**
+     * Listens to window closing events and processes them as cancel events
+     *
+     */
     class WindowClosingAdapter extends WindowAdapter {
         @Override
@@ -670,3 +529,487 @@
         }
     }
+
+    /**
+     * The panel which provides various UI widgets for controlling how to use
+     * changesets during upload.
+     *
+     */
+    class ChangesetSelectionPanel extends JPanel implements ListDataListener{
+
+        private ButtonGroup bgUseNewOrExisting;
+        private JRadioButton rbUseNew;
+        private JRadioButton rbExisting;
+        private JComboBox cbOpenChangesets;
+        private JButton btnRefresh;
+        private JButton btnClose;
+        private JCheckBox cbCloseAfterUpload;
+        private OpenChangesetModel model;
+        private SuggestingJHistoryComboBox cmt;
+        private JCheckBox cbUseAtomicUpload;
+
+        /**
+         * build the panel with the widgets for controlling whether an atomic upload
+         * should be used or not
+         * 
+         * @return the panel
+         */
+        protected JPanel buildAtomicUploadControlPanel() {
+            JPanel pnl = new JPanel();
+            pnl.setLayout(new GridBagLayout());
+            GridBagConstraints gc = new GridBagConstraints();
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            gc.anchor = GridBagConstraints.FIRST_LINE_START;
+            pnl.add(cbUseAtomicUpload = new JCheckBox(tr("Upload all changes in one request")), gc);
+            cbUseAtomicUpload.setToolTipText(tr("Enable to upload all changes in one request, disable to use one request per changed primitive"));
+            boolean useAtomicUpload = Main.pref.getBoolean("osm-server.atomic-upload", true);
+            cbUseAtomicUpload.setSelected(useAtomicUpload);
+            cbUseAtomicUpload.setEnabled(OsmApi.getOsmApi().hasSupportForDiffUploads());
+            return pnl;
+        }
+
+        protected JPanel buildUploadCommentPanel() {
+            JPanel pnl = new JPanel();
+            pnl.setLayout(new GridBagLayout());
+            pnl.add(new JLabel(tr("Provide a brief comment for the changes you are uploading:")), GBC.eol().insets(0, 5, 10, 3));
+            cmt = new SuggestingJHistoryComboBox();
+            List<String> cmtHistory = new LinkedList<String>(Main.pref.getCollection(HISTORY_KEY, new LinkedList<String>()));
+            cmt.setHistory(cmtHistory);
+            cmt.getEditor().addActionListener(
+                    new ActionListener() {
+                        public void actionPerformed(ActionEvent e) {
+                            TagModel tm = tagEditorPanel.getModel().get("comment");
+                            if (tm == null) {
+                                tagEditorPanel.getModel().add(new TagModel("comment", cmt.getText()));
+                            } else {
+                                tm.setValue(cmt.getText());
+                            }
+                            tagEditorPanel.getModel().fireTableDataChanged();
+                        }
+                    }
+            );
+            pnl.add(cmt, GBC.eol().fill(GBC.HORIZONTAL));
+            return pnl;
+        }
+
+        protected void build() {
+            setLayout(new GridBagLayout());
+            GridBagConstraints gc = new GridBagConstraints();
+
+            bgUseNewOrExisting = new ButtonGroup();
+
+            // -- atomic upload
+            gc.gridwidth = 4;
+            gc.gridy = 0;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            gc.anchor = GridBagConstraints.FIRST_LINE_START;
+            add(buildAtomicUploadControlPanel(), gc);
+
+            // -- changeset command
+            gc.gridwidth = 4;
+            gc.gridy = 1;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 1.0;
+            gc.anchor = GridBagConstraints.FIRST_LINE_START;
+            add(buildUploadCommentPanel(), gc);
+
+            gc.gridwidth = 4;
+            gc.gridy = 2;
+            gc.fill = GridBagConstraints.HORIZONTAL;
+            gc.weightx = 0.0;
+            gc.anchor = GridBagConstraints.FIRST_LINE_START;
+            rbUseNew = new JRadioButton(tr("Open a new changeset"));
+            rbUseNew.setToolTipText(tr("Open a new changeset and use it in the next upload"));
+            bgUseNewOrExisting.add(rbUseNew);
+            add(rbUseNew, gc);
+
+            gc.gridx = 0;
+            gc.gridy = 3;
+            gc.gridwidth = 1;
+            rbExisting = new JRadioButton(tr("Use an open changeset"));
+            rbExisting.setToolTipText(tr("Upload data to an already opened changeset"));
+            bgUseNewOrExisting.add(rbExisting);
+            add(rbExisting, gc);
+
+            gc.gridx = 1;
+            gc.gridy = 3;
+            gc.gridwidth = 1;
+            gc.weightx = 1.0;
+            model = new OpenChangesetModel();
+            cbOpenChangesets = new JComboBox(model);
+            cbOpenChangesets.setToolTipText("Select an open changeset");
+            cbOpenChangesets.setRenderer(new ChangesetCellRenderer());
+            cbOpenChangesets.addItemListener(new ChangesetListItemStateListener());
+            Dimension d = cbOpenChangesets.getPreferredSize();
+            d.width = 200;
+            cbOpenChangesets.setPreferredSize(d);
+            d.width = 100;
+            cbOpenChangesets.setMinimumSize(d);
+            model.addListDataListener(this);
+            add(cbOpenChangesets, gc);
+
+            gc.gridx = 3;
+            gc.gridy = 3;
+            gc.gridwidth = 1;
+            gc.weightx = 0.0;
+            btnRefresh = new JButton(new RefreshAction());
+            add(btnRefresh, gc);
+
+            gc.gridx = 4;
+            gc.gridy = 3;
+            gc.gridwidth = 1;
+            gc.weightx = 0.0;
+            CloseChangesetAction closeChangesetAction = new CloseChangesetAction();
+            btnClose = new JButton(closeChangesetAction);
+            cbOpenChangesets.addItemListener(closeChangesetAction);
+            add(btnClose, gc);
+
+            gc.gridx = 0;
+            gc.gridy = 4;
+            gc.gridwidth = 4;
+            cbCloseAfterUpload = new JCheckBox(tr("Close changeset after upload"));
+            cbCloseAfterUpload.setToolTipText(tr("Select to close the changeset after the next upload"));
+            add(cbCloseAfterUpload, gc);
+            cbCloseAfterUpload.setSelected(true);
+
+            rbUseNew.getModel().addItemListener(new RadioButtonHandler());
+            rbExisting.getModel().addItemListener(new RadioButtonHandler());
+
+            refreshGUI();
+        }
+
+        public ChangesetSelectionPanel() {
+            build();
+        }
+
+        /**
+         * Remembers the user input in the preference settings
+         */
+        public void rememberUserInput() {
+            // store the history of comments
+            cmt.addCurrentItemToHistory();
+            Main.pref.putCollection(HISTORY_KEY, cmt.getHistory());
+            Main.pref.put("osm-server.atomic-upload", cbUseAtomicUpload.isSelected());
+        }
+
+        /**
+         * Initializes the panel for user input
+         */
+        public void startUserInput() {
+            cmt.getEditor().selectAll();
+            cmt.requestFocus();
+        }
+
+        /**
+         * Replies the current upload comment
+         * 
+         * @return
+         */
+        public String getUploadComment() {
+            return cmt.getText();
+        }
+
+        /**
+         * Replies the current upload comment
+         * 
+         * @return
+         */
+        public void setUploadComment(String uploadComment) {
+            cmt.setText(uploadComment);
+        }
+
+        public void initEditingOfUploadComment(String comment) {
+            setUploadComment(comment);
+            cmt.getEditor().selectAll();
+            cmt.requestFocus();
+        }
+
+        protected void refreshGUI() {
+            rbExisting.setEnabled(model.getSize() > 0);
+            if (model.getSize() == 0) {
+                if (!rbUseNew.isSelected()) {
+                    rbUseNew.setSelected(true);
+                }
+            }
+            cbOpenChangesets.setEnabled(model.getSize() > 0 && rbExisting.isSelected());
+        }
+
+        public void contentsChanged(ListDataEvent e) {
+            refreshGUI();
+        }
+
+        public void intervalAdded(ListDataEvent e) {
+            refreshGUI();
+        }
+
+        public void intervalRemoved(ListDataEvent e) {
+            refreshGUI();
+        }
+
+        public Changeset getChangeset() {
+            if (rbUseNew.isSelected() || cbOpenChangesets.getSelectedItem() == null)
+                return new Changeset();
+            Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem();
+            if (cs == null)
+                return new Changeset();
+            return cs;
+        }
+
+        public void setOrUpdateChangeset(Changeset cs) {
+            if (cs == null) {
+                tagEditorPanel.getModel().clear();
+                tagEditorPanel.getModel().add("created_by", getDefaultCreatedBy());
+                tagEditorPanel.getModel().appendNewTag();
+                rbUseNew.setSelected(true);
+            } else if (cs.getId() == 0) {
+                if (cs.get("created_by") == null) {
+                    cs.put("created_by", getDefaultCreatedBy());
+                }
+                tagEditorPanel.getModel().initFromPrimitive(cs);
+                tagEditorPanel.getModel().appendNewTag();
+                rbUseNew.setSelected(true);
+            } else if (cs.getId() > 0 && cs.isOpen()){
+                if (cs.get("created_by") == null) {
+                    cs.put("created_by", getDefaultCreatedBy());
+                }
+                tagEditorPanel.getModel().initFromPrimitive(cs);
+                model.addOrUpdate(cs);
+                cs = model.getChangesetById(cs.getId());
+                cbOpenChangesets.setSelectedItem(cs);
+            } else if (cs.getId() > 0 && !cs.isOpen()){
+                if (cs.get("created_by") == null) {
+                    cs.put("created_by", getDefaultCreatedBy());
+                }
+                tagEditorPanel.getModel().initFromPrimitive(cs);
+                model.removeChangeset(cs);
+                if (model.getSize() == 0) {
+                    rbUseNew.setSelected(true);
+                    model.setSelectedItem(null);
+                    southTabbedPane.setTitleAt(1, tr("Tags of new changeset"));
+                }
+            }
+        }
+
+        public void setUseNew() {
+            rbUseNew.setSelected(true);
+        }
+
+        public void setUseExisting() {
+            rbExisting.setSelected(true);
+            if (cbOpenChangesets.getSelectedItem() == null && model.getSize() > 0) {
+                cbOpenChangesets.setSelectedItem(model.getElementAt(0));
+            }
+        }
+
+        public boolean isCloseAfterUpload() {
+            return cbCloseAfterUpload.isSelected();
+        }
+
+        class RadioButtonHandler implements ItemListener {
+            public void itemStateChanged(ItemEvent e) {
+                if (rbUseNew.isSelected()) {
+                    southTabbedPane.setTitleAt(1, tr("Tags of new changeset"));
+                    // init a new changeset from the currently edited tags
+                    // and the comment field
+                    //
+                    Changeset cs = new Changeset();
+                    tagEditorPanel.getModel().applyToPrimitive(cs);
+                    if (cs.get("created_by") == null) {
+                        cs.put("created_by", getDefaultCreatedBy());
+                    }
+                    cs.put("comment", cmt.getText());
+                    tagEditorPanel.getModel().initFromPrimitive(cs);
+                } else {
+                    if (cbOpenChangesets.getSelectedItem() == null) {
+                        model.selectFirstChangeset();
+                    }
+                    Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem();
+                    if (cs != null) {
+                        cs.put("comment", cmt.getText());
+                        southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
+                        tagEditorPanel.getModel().initFromPrimitive(cs);
+                    }
+                }
+                refreshGUI();
+            }
+        }
+
+        class ChangesetListItemStateListener implements ItemListener {
+            public void itemStateChanged(ItemEvent e) {
+
+                Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem();
+                if (cs == null) {
+                    southTabbedPane.setTitleAt(1, tr("Tags of new changeset"));
+                    // init a new changeset from the currently edited tags
+                    // and the comment field
+                    //
+                    cs = new Changeset();
+                    tagEditorPanel.getModel().applyToPrimitive(cs);
+                    if (cs.get("created_by") == null) {
+                        cs.put("created_by", getDefaultCreatedBy());
+                    }
+                    cs.put("comment", cmt.getText());
+                    tagEditorPanel.getModel().initFromPrimitive(cs);
+                } else {
+                    southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
+                    cs.put("comment", cmt.getText());
+                    southTabbedPane.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
+                    if (cs.get("created_by") == null) {
+                        cs.put("created_by", getDefaultCreatedBy());
+                    }
+                    tagEditorPanel.getModel().initFromPrimitive(cs);
+                }
+            }
+        }
+
+        class RefreshAction extends AbstractAction {
+            public RefreshAction() {
+                //putValue(NAME, tr("Reload"));
+                putValue(SHORT_DESCRIPTION, tr("Load the list of your open changesets from the server"));
+                putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
+            }
+
+            public void actionPerformed(ActionEvent e) {
+                DownloadOpenChangesetsTask task = new DownloadOpenChangesetsTask(model);
+                Main.worker.submit(task);
+            }
+        }
+
+        class CloseChangesetAction extends AbstractAction implements ItemListener{
+            public CloseChangesetAction() {
+                putValue(NAME, tr("Close"));
+                putValue(SHORT_DESCRIPTION, tr("Close the currently selected open changeset"));
+                refreshEnabledState();
+            }
+
+            public void actionPerformed(ActionEvent e) {
+                Changeset cs = (Changeset)cbOpenChangesets.getSelectedItem();
+                if (cs == null) return;
+                CloseChangesetTask task = new CloseChangesetTask(Collections.singletonList(cs));
+                Main.worker.submit(task);
+            }
+
+            protected void refreshEnabledState() {
+                setEnabled(cbOpenChangesets.getModel().getSize() > 0 && cbOpenChangesets.getSelectedItem() != null);
+            }
+
+            public void itemStateChanged(ItemEvent e) {
+                refreshEnabledState();
+            }
+        }
+    }
+
+    public class OpenChangesetModel extends DefaultComboBoxModel {
+        private List<Changeset> changesets;
+        private long uid;
+        private Changeset selectedChangeset = null;
+
+        protected Changeset getChangesetById(long id) {
+            for (Changeset cs : changesets) {
+                if (cs.getId() == id) return cs;
+            }
+            return null;
+        }
+
+        public OpenChangesetModel() {
+            this.changesets = new ArrayList<Changeset>();
+        }
+
+        protected void internalAddOrUpdate(Changeset cs) {
+            Changeset other = getChangesetById(cs.getId());
+            if (other != null) {
+                cs.cloneFrom(other);
+            } else {
+                changesets.add(cs);
+            }
+        }
+
+        public void addOrUpdate(Changeset cs) {
+            if (cs.getId() <= 0 )
+                throw new IllegalArgumentException(tr("changeset id > 0 expected. Got {1}", "id", cs.getId()));
+            internalAddOrUpdate(cs);
+            fireContentsChanged(this, 0, getSize());
+        }
+
+        public void remove(long id) {
+            Changeset cs = getChangesetById(id);
+            if (cs != null) {
+                changesets.remove(cs);
+            }
+            fireContentsChanged(this, 0, getSize());
+        }
+
+        public void addOrUpdate(Collection<Changeset> changesets) {
+            for (Changeset cs: changesets) {
+                internalAddOrUpdate(cs);
+            }
+            fireContentsChanged(this, 0, getSize());
+            if (getSelectedItem() == null && !this.changesets.isEmpty()) {
+                setSelectedItem(this.changesets.get(0));
+            } else {
+                setSelectedItem(null);
+            }
+        }
+
+        public void setUserId(long uid) {
+            this.uid = uid;
+        }
+
+        public long getUserId() {
+            return uid;
+        }
+
+        public void selectFirstChangeset() {
+            if (changesets == null || changesets.isEmpty()) return;
+            setSelectedItem(changesets.get(0));
+        }
+
+        public void removeChangeset(Changeset cs) {
+            if (cs == null) return;
+            changesets.remove(cs);
+            if (selectedChangeset == cs) {
+                selectFirstChangeset();
+            }
+            fireContentsChanged(this, 0, getSize());
+        }
+        /* ------------------------------------------------------------------------------------ */
+        /* ComboBoxModel                                                                        */
+        /* ------------------------------------------------------------------------------------ */
+        @Override
+        public Object getElementAt(int index) {
+            return changesets.get(index);
+        }
+
+        @Override
+        public int getIndexOf(Object anObject) {
+            return changesets.indexOf(anObject);
+        }
+
+        @Override
+        public int getSize() {
+            return changesets.size();
+        }
+
+        @Override
+        public Object getSelectedItem() {
+            return selectedChangeset;
+        }
+
+        @Override
+        public void setSelectedItem(Object anObject) {
+            if (anObject == null) {
+                this.selectedChangeset = null;
+                super.setSelectedItem(null);
+                return;
+            }
+            if (! (anObject instanceof Changeset)) return;
+            Changeset cs = (Changeset)anObject;
+            if (cs.getId() == 0 || ! cs.isOpen()) return;
+            Changeset candidate = getChangesetById(cs.getId());
+            if (candidate == null) return;
+            this.selectedChangeset = candidate;
+            super.setSelectedItem(selectedChangeset);
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java	(revision 2115)
@@ -36,5 +36,5 @@
     private ProgressMonitor monitor;
     private Changeset changeset;
-    private ChangesetProcessingType changesetProcessingType;
+    private boolean closeChangesetAfterUpload;
 
     /**
@@ -44,8 +44,8 @@
      * @param changeset the changeset to be used if <code>changesetProcessingType</code> indicates that a new
      *   changeset is to be used
-     * @param changesetProcessingType how we handle changesets
+     * @param closeChangesetAfterUpload true, if the changeset should be closed after the upload
      * @throws IllegalArgumentException thrown, if layer is null
      */
-    public UploadLayerTask(OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, ChangesetProcessingType changesetProcessingType) {
+    public UploadLayerTask(OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, boolean closeChangesetAfterUpload) {
         if (layer == null)
             throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", layer));
@@ -56,5 +56,5 @@
         this.monitor = monitor;
         this.changeset = changeset;
-        this.changesetProcessingType = changesetProcessingType == null ? ChangesetProcessingType.USE_NEW_AND_CLOSE : changesetProcessingType;
+        this.closeChangesetAfterUpload = closeChangesetAfterUpload;
     }
 
@@ -69,5 +69,5 @@
             ProgressMonitor m = monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
             if (isCancelled()) return;
-            writer.uploadOsm(layer.data.version, toUpload, changeset, changesetProcessingType, m);
+            writer.uploadOsm(layer.data.version, toUpload, changeset, closeChangesetAfterUpload, m);
         } catch (Exception sxe) {
             if (isCancelled()) {
Index: trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java	(revision 2115)
@@ -9,6 +9,8 @@
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.logging.Logger;
 
@@ -19,4 +21,5 @@
 import org.openstreetmap.josm.command.SequenceCommand;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Tagged;
 
 
@@ -83,9 +86,9 @@
         TagModel tag = tags.get(rowIndex);
         switch(columnIndex) {
-        case 0:
-        case 1: return tag;
-
-        default:
-            throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex);
+            case 0:
+            case 1: return tag;
+
+            default:
+                throw new IndexOutOfBoundsException("unexpected columnIndex: columnIndex=" + columnIndex);
         }
     }
@@ -280,5 +283,5 @@
      * @param primitive the OSM primitive
      */
-    public void initFromPrimitive(OsmPrimitive primitive) {
+    public void initFromPrimitive(Tagged primitive) {
         clear();
         for (String key : primitive.keySet()) {
@@ -293,4 +296,21 @@
 
     /**
+     * initializes the model with the tags of an OSM primitive
+     *
+     * @param primitive the OSM primitive
+     */
+    public void initFromTags(Map<String,String> tags) {
+        clear();
+        for (String key : tags.keySet()) {
+            String value = tags.get(key);
+            add(key,value);
+        }
+        TagModel tag = new TagModel();
+        sort();
+        this.tags.add(tag);
+        setDirty(false);
+    }
+
+    /**
      * applies the current state of the tag editor model to a primitive
      *
@@ -298,7 +318,19 @@
      *
      */
-    public void applyToPrimitive(OsmPrimitive primitive) {
-        primitive.removeAll();
-        for (TagModel tag: tags) {
+    public void applyToPrimitive(Tagged primitive) {
+        Map<String,String> tags = primitive.getKeys();
+        applyToTags(tags);
+        primitive.setKeys(tags);
+    }
+
+    /**
+     * applies the current state of the tag editor model to a map of tags
+     *
+     * @param tags the map of key/value pairs
+     *
+     */
+    public void applyToTags(Map<String, String> tags) {
+        tags.clear();
+        for (TagModel tag: this.tags) {
             // tag still holds an unchanged list of different values for the same key.
             // no property change command required
@@ -312,6 +344,12 @@
                 continue;
             }
-            primitive.put(tag.getName(), tag.getValue());
-        }
+            tags.put(tag.getName(), tag.getValue());
+        }
+    }
+
+    public Map<String,String> getTags() {
+        Map<String,String> tags = new HashMap<String, String>();
+        applyToTags(tags);
+        return tags;
     }
 
Index: trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java	(revision 2115)
@@ -0,0 +1,106 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.util.Date;
+
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.DateUtils;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class ChangesetQuery {
+    private Long user = null;
+    private LatLon min = null;
+    private LatLon max = null;
+    private Date closedAfter = null;
+    private Date createdBefore = null;
+    private Boolean open = null;
+    private Boolean closed = null;
+
+    public ChangesetQuery() {}
+
+    public ChangesetQuery forUser(long uid) {
+        if (uid <= 0)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' > 0 expected. Got {1}", "uid", uid));
+        this.user = uid;
+        return this;
+    }
+
+    public ChangesetQuery inBbox(double minLon, double minLat, double maxLon, double maxLat) {
+        return inBbox(new LatLon(minLon, minLat), new LatLon(maxLon, maxLat));
+    }
+
+    public ChangesetQuery inBbox(LatLon min, LatLon max) {
+        this.min = min;
+        this.max = max;
+        return this;
+    }
+
+    public ChangesetQuery closedAfter(Date d) {
+        this.closedAfter = d;
+        return this;
+    }
+
+    public ChangesetQuery between(Date closedAfter, Date createdBefore ) {
+        this.closedAfter = closedAfter;
+        this.createdBefore = createdBefore;
+        return this;
+    }
+
+    public ChangesetQuery beingOpen() {
+        this.open =  true;
+        this.closed = null;
+        return this;
+    }
+
+    public ChangesetQuery beingClosed() {
+        this.open =  null;
+        this.closed = true;
+        return this;
+    }
+
+    public String getQueryString() {
+        StringBuffer sb = new StringBuffer();
+        if (user != null) {
+            sb.append("user").append("=").append(user);
+        }
+        if (min!=null && max != null) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append("min_lon").append("=").append(min.lonToString(CoordinateFormat.DECIMAL_DEGREES));
+            sb.append("&");
+            sb.append("min_lat").append("=").append(min.latToString(CoordinateFormat.DECIMAL_DEGREES));
+            sb.append("&");
+            sb.append("max_lon").append("=").append(max.lonToString(CoordinateFormat.DECIMAL_DEGREES));
+            sb.append("&");
+            sb.append("max_lat").append("=").append(max.latToString(CoordinateFormat.DECIMAL_DEGREES));
+        }
+        if (closedAfter != null && createdBefore != null) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append("time").append("=").append(DateUtils.fromDate(closedAfter))
+            .append(",").append(DateUtils.fromDate(createdBefore));
+        } else if (closedAfter != null) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append("time").append("=").append(DateUtils.fromDate(closedAfter));
+        }
+
+        if (open != null) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append("open=true");
+        } else if (closed != null) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append("closed=true");
+        }
+        return sb.toString();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 2115)
@@ -23,5 +23,4 @@
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Properties;
 
 import javax.xml.parsers.SAXParserFactory;
@@ -32,4 +31,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
+import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.xml.sax.Attributes;
@@ -205,4 +205,19 @@
 
     /**
+     * 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(Changeset s) {
+        swriter.getBuffer().setLength(0);
+        osmWriter.header();
+        s.visit(osmWriter);
+        osmWriter.footer();
+        osmWriter.out.flush();
+        return swriter.toString();
+    }
+
+    /**
      * Returns the base URL for API requests, including the negotiated version number.
      * @return base URL string
@@ -229,7 +244,8 @@
      */
     public void createPrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
-        initialize(monitor);
         String ret = "";
         try {
+            ensureValidChangeset();
+            initialize(monitor);
             ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/create", toXml(osm, true),monitor);
             osm.setOsmId(Long.parseLong(ret.trim()), 1);
@@ -244,21 +260,22 @@
      * version.
      *
-     * @param osm the primitive
+     * @param osm the primitive. Must not be null
      * @throws OsmTransferException if something goes wrong
      */
     public void modifyPrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
-        initialize(monitor);
-        if (version.equals("0.5")) {
-            // legacy mode does not return the new object version.
-            sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true),monitor);
-        } else {
-            String ret = null;
-            // normal mode (0.6 and up) returns new object version.
-            try {
+        String ret = null;
+        try {
+            ensureValidChangeset();
+            initialize(monitor);
+            if (version.equals("0.5")) {
+                // legacy mode does not return the new object version.
+                sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true),monitor);
+            } else {
+                // normal mode (0.6 and up) returns new object version.
                 ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true), monitor);
                 osm.setOsmId(osm.getId(), Integer.parseInt(ret.trim()));
-            } catch(NumberFormatException e) {
-                throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.getId(), ret));
             }
+        } catch(NumberFormatException e) {
+            throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.getId(), ret));
         }
     }
@@ -270,4 +287,5 @@
      */
     public void deletePrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
+        ensureValidChangeset();
         initialize(monitor);
         // can't use a the individual DELETE method in the 0.6 API. Java doesn't allow
@@ -280,16 +298,30 @@
 
     /**
-     * Creates a new changeset based on the keys in <code>changeset</code>
-     * 
-     * @param changeset the changeset to be used for uploading
+     * Creates a new changeset based on the keys in <code>changeset</code>. If this
+     * method succeeds, changeset.getId() replies the id the server assigned to the new
+     * changeset
+     * 
+     * The changeset must not be null, but its key/value-pairs may be empty.
+     * 
+     * @param changeset the changeset toe be created. Must not be null.
      * @param progressMonitor the progress monitor
      * @throws OsmTransferException signifying a non-200 return code, or connection errors
-     */
-    public void createChangeset(Changeset changeset, ProgressMonitor progressMonitor) throws OsmTransferException {
+     * @throws IllegalArgumentException thrown if changeset is null
+     */
+    public void openChangeset(Changeset changeset, ProgressMonitor progressMonitor) throws OsmTransferException {
+        if (changeset == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset"));
         try {
             progressMonitor.beginTask((tr("Creating changeset...")));
-            createPrimitive(changeset, progressMonitor);
-            this.changeset = changeset;
-            progressMonitor.setCustomText((tr("Successfully opened changeset {0}",this.changeset.getId())));
+            initialize(progressMonitor);
+            String ret = "";
+            try {
+                ret = sendRequest("PUT", "changeset/create", toXml(changeset),progressMonitor);
+                changeset.setId(Long.parseLong(ret.trim()));
+                changeset.setOpen(true);
+            } catch(NumberFormatException e){
+                throw new OsmTransferException(tr("unexpected format of id replied by the server, got ''{0}''", ret));
+            }
+            progressMonitor.setCustomText((tr("Successfully opened changeset {0}",changeset.getId())));
         } finally {
             progressMonitor.finishTask();
@@ -298,61 +330,65 @@
 
     /**
-     * Updates the current changeset with the keys in  <code>changesetUpdate</code>.
-     *
-     * @param changesetUpdate the changeset to update
-     * @param progressMonitor the progress monitor
+     * Updates a changeset with the keys in  <code>changesetUpdate</code>. The changeset must not
+     * be null and id > 0 must be true.
+     *
+     * @param changeset the changeset to update. Must not be null.
+     * @param monitor the progress monitor. If null, uses the {@see NullProgressMonitor#INSTANCE}.
      * 
      * @throws OsmTransferException if something goes wrong.
-     */
-    public void updateChangeset(Changeset changesetUpdate, ProgressMonitor progressMonitor) throws OsmTransferException {
+     * @throws IllegalArgumentException if changeset is null
+     * @throws IllegalArgumentException if changeset.getId() == 0
+     * 
+     */
+    public void updateChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
+        if (changeset == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset"));
+        if (monitor == null) {
+            monitor = NullProgressMonitor.INSTANCE;
+        }
+        if (changeset.getId() <= 0)
+            throw new IllegalArgumentException(tr("id of changeset > 0 required. Got {0}", changeset.getId()));
         try {
-            progressMonitor.beginTask(tr("Updating changeset..."));
-            initialize(progressMonitor);
-            if (this.changeset != null && this.changeset.getId() > 0) {
-                if (this.changeset.hasEqualSemanticAttributes(changesetUpdate)) {
-                    progressMonitor.setCustomText(tr("Changeset {0} is unchanged. Skipping update.", changesetUpdate.getId()));
-                    return;
-                }
-                this.changeset.setKeys(changesetUpdate.getKeys());
-                progressMonitor.setCustomText(tr("Updating changeset {0}...", this.changeset.getId()));
-                sendRequest(
-                        "PUT",
-                        OsmPrimitiveType.from(changesetUpdate).getAPIName() + "/" + this.changeset.getId(),
-                        toXml(this.changeset, true),
-                        progressMonitor
-                );
-            } else
-                throw new OsmTransferException(tr("Failed to update changeset. Either there is no current changeset or the id of the current changeset is 0"));
+            monitor.beginTask(tr("Updating changeset..."));
+            initialize(monitor);
+            monitor.setCustomText(tr("Updating changeset {0}...", changeset.getId()));
+            sendRequest(
+                    "PUT",
+                    "changeset/" + this.changeset.getId(),
+                    toXml(this.changeset),
+                    monitor
+            );
         } finally {
-            progressMonitor.finishTask();
-        }
-    }
-
-    /**
-     * Closes a changeset on the server.
-     *
-     * @param changesetProcessingType how changesets are currently handled
-     * @param progressMonitor the progress monitor
+            monitor.finishTask();
+        }
+    }
+
+
+    /**
+     * Closes a changeset on the server. Sets changeset.setOpen(false) if this operation
+     * succeeds.
+     *
+     * @param changeset the changeset to be closed. Must not be null. changeset.getId() > 0 required.
+     * @param monitor the progress monitor. If null, uses {@see NullProgressMonitor#INSTANCE}
      * 
      * @throws OsmTransferException if something goes wrong.
-     */
-    public void stopChangeset(ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException {
-        if (changesetProcessingType == null) {
-            changesetProcessingType = ChangesetProcessingType.USE_NEW_AND_CLOSE;
-        }
+     * @throws IllegalArgumentException thrown if changeset is null
+     * @throws IllegalArgumentException thrown if changeset.getId() <= 0
+     */
+    public void closeChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
+        if (changeset == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "changeset"));
+        if (monitor == null) {
+            monitor = NullProgressMonitor.INSTANCE;
+        }
+        if (changeset.getId() <= 0)
+            throw new IllegalArgumentException(tr("id of changeset > 0 required. Got {0}", changeset.getId()));
         try {
-            progressMonitor.beginTask(tr("Closing changeset..."));
-            initialize(progressMonitor);
-            if (changesetProcessingType.isCloseAfterUpload()) {
-                progressMonitor.setCustomText(tr("Closing changeset {0}...", changeset.getId()));
-                if (this.changeset != null && this.changeset.getId() > 0) {
-                    sendRequest("PUT", "changeset" + "/" + changeset.getId() + "/close", null, progressMonitor);
-                    changeset = null;
-                }
-            } else {
-                progressMonitor.setCustomText(tr("Leaving changeset {0} open...", changeset.getId()));
-            }
+            monitor.beginTask(tr("Closing changeset..."));
+            initialize(monitor);
+            sendRequest("PUT", "changeset" + "/" + changeset.getId() + "/close", null, monitor);
+            changeset.setOpen(false);
         } finally {
-            progressMonitor.finishTask();
+            monitor.finishTask();
         }
     }
@@ -365,8 +401,7 @@
      * @throws OsmTransferException if something is wrong
      */
-    public Collection<OsmPrimitive> uploadDiff(final Collection<OsmPrimitive> list, ProgressMonitor progressMonitor) throws OsmTransferException {
-
-        progressMonitor.beginTask("", list.size() * 2);
+    public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list, ProgressMonitor progressMonitor) throws OsmTransferException {
         try {
+            progressMonitor.beginTask("", list.size() * 2);
             if (changeset == null)
                 throw new OsmTransferException(tr("No changeset present for diff upload"));
@@ -385,15 +420,12 @@
 
             String diff = duv.getDocument();
-            try {
-                String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff,progressMonitor);
-                DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(),
-                        progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
-            } catch(OsmTransferException e) {
-                throw e;
-            } catch(Exception e) {
-                throw new OsmTransferException(e);
-            }
-
+            String diffresult = sendRequest("POST", "changeset/" + changeset.getId() + "/upload", diff,progressMonitor);
+            DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(),
+                    progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
             return processed;
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(Exception e) {
+            throw new OsmTransferException(e);
         } finally {
             progressMonitor.finishTask();
@@ -536,6 +568,7 @@
                 }
                 throw new OsmTransferException(e);
+            } catch(OsmTransferException e) {
+                throw e;
             } catch (Exception e) {
-                if (e instanceof OsmTransferException) throw (OsmTransferException) e;
                 throw new OsmTransferException(e);
             }
@@ -552,11 +585,46 @@
     }
 
-    /**
-     * Replies the current changeset
-     * 
-     * @return the current changeset
-     */
-    public Changeset getCurrentChangeset() {
+
+    /**
+     * Ensures that the current changeset can be used for uploading data
+     * 
+     * @throws OsmTransferException thrown if the current changeset can't be used for
+     * uploading data
+     */
+    protected void ensureValidChangeset() throws OsmTransferException {
+        if (changeset == null)
+            throw new OsmTransferException(tr("current changeset is null. Can't upload data."));
+        if (changeset.getId() <= 0)
+            throw new OsmTransferException(tr("id of current changeset > required. Current id is {0}", changeset.getId()));
+    }
+    /**
+     * Replies the changeset data uploads are currently directed to
+     * 
+     * @return the changeset data uploads are currently directed to
+     */
+    public Changeset getChangeset() {
         return changeset;
     }
+
+    /**
+     * Sets the changesets to which further data uploads are directed. The changeset
+     * can be null. If it isn't null it must have been created, i.e. id > 0 is required. Furthermore,
+     * it must be open.
+     * 
+     * @param changeset the changeset
+     * @throws IllegalArgumentException thrown if changeset.getId() <= 0
+     * @throws IllegalArgumentException thrown if !changeset.isOpen()
+     */
+    public void setChangeset(Changeset changeset) {
+        if (changeset == null) {
+            this.changeset = null;
+            return;
+        }
+        if (changeset.getId() <= 0)
+            throw new IllegalArgumentException(tr("Changeset id > 0 expected. Got {0}", changeset.getId()));
+        if (!changeset.isOpen())
+            throw new IllegalArgumentException(tr("Open changeset expected. Got closed changeset with id {0}", changeset.getId()));
+        this.changeset = changeset;
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java	(revision 2115)
@@ -0,0 +1,229 @@
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.User;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Parser for a list of changesets, encapsulated in an OSM data set structure.
+ * Example:
+ * <pre>
+ * &lt;osm version="0.6" generator="OpenStreetMap server"&gt;
+ *     &lt;changeset id="143" user="guggis" uid="1" created_at="2009-09-08T20:35:39Z" closed_at="2009-09-08T21:36:12Z" open="false" min_lon="7.380925" min_lat="46.9215164" max_lon="7.3984718" max_lat="46.9226502"&gt;
+ *         &lt;tag k="asdfasdf" v="asdfasdf"/&gt;
+ *         &lt;tag k="created_by" v="JOSM/1.5 (UNKNOWN de)"/&gt;
+ *         &lt;tag k="comment" v="1234"/&gt;
+ *     &lt;/changeset&gt;
+ * &lt;/osm&gt;
+ * </pre>
+ *
+ */
+public class OsmChangesetParser {
+    static private final Logger logger = Logger.getLogger(OsmChangesetParser.class.getName());
+
+    private List<Changeset> changesets;
+
+    private OsmChangesetParser() {
+        changesets = new LinkedList<Changeset>();
+    }
+
+    public List<Changeset> getChangesets() {
+        return changesets;
+    }
+
+    private class Parser extends DefaultHandler {
+        private Locator locator;
+
+        @Override
+        public void setDocumentLocator(Locator locator) {
+            this.locator = locator;
+        }
+
+        protected void throwException(String msg) throws OsmDataParsingException{
+            throw new OsmDataParsingException(msg).rememberLocation(locator);
+        }
+        /**
+         * The current changeset
+         */
+        private Changeset current = null;
+
+        protected void parseChangesetAttributes(Changeset cs, Attributes atts) throws OsmDataParsingException {
+            // -- id
+            String value = atts.getValue("id");
+            if (value == null) {
+                throwException(tr("missing mandatory attribute ''{0}''", "id"));
+            }
+            long id = 0;
+            try {
+                id = Long.parseLong(value);
+            } catch(NumberFormatException e) {
+                throwException(tr("illegal value for attribute ''{0}''. Got ''{1}''", "id", value));
+            }
+            if (id <= 0) {
+                throwException(tr("illegal nummeric value for attribute ''{0}''. Got ''{1}''", "id", id));
+            }
+            current.setId(id);
+
+            // -- user
+            String user = atts.getValue("user");
+            String uid = atts.getValue("uid");
+            current.setUser(createUser(uid, user));
+
+            // -- created_at
+            value = atts.getValue("created_at");
+            if (value == null) {
+                current.setCreatedAt(null);
+            } else {
+                current.setCreatedAt(DateUtils.fromString(value));
+            }
+
+            // -- closed_at
+            value = atts.getValue("closed_at");
+            if (value == null) {
+                current.setClosedAt(null);
+            } else {
+                current.setClosedAt(DateUtils.fromString(value));
+            }
+
+            //  -- open
+            value = atts.getValue("open");
+            if (value == null) {
+                throwException(tr("missing mandatory attribute ''{0}''", "open"));
+            } else if (value.equals("true")) {
+                current.setOpen(true);
+            } else if (value.equals("false")) {
+                current.setOpen(false);
+            } else {
+                throwException(tr("illegal boolean value for attribute ''{0}''. Got ''{1}''", "open", value));
+            }
+
+            // -- min_lon and min_lat
+            String min_lon = atts.getValue("min_lon");
+            String min_lat = atts.getValue("min_lat");
+            String max_lon = atts.getValue("max_lon");
+            String max_lat = atts.getValue("max_lat");
+            if (min_lon != null && min_lat != null && max_lon != null && max_lat != null) {
+                double minLon = 0;
+                try {
+                    minLon = Double.parseDouble(min_lon);
+                } catch(NumberFormatException e) {
+                    throwException(tr("illegal value for attribute ''{0}''. Got ''{1}''", "min_lon", min_lon));
+                }
+                double minLat = 0;
+                try {
+                    minLat = Double.parseDouble(min_lat);
+                } catch(NumberFormatException e) {
+                    throwException(tr("illegal value for attribute ''{0}''. Got ''{1}''", "min_lat", min_lat));
+                }
+                current.setMin(new LatLon(minLat, minLon));
+
+                // -- max_lon and max_lat
+
+                double maxLon = 0;
+                try {
+                    maxLon = Double.parseDouble(max_lon);
+                } catch(NumberFormatException e) {
+                    throwException(tr("illegal value for attribute ''{0}''. Got ''{1}''", "max_lon", max_lon));
+                }
+                double maxLat = 0;
+                try {
+                    maxLat = Double.parseDouble(max_lat);
+                } catch(NumberFormatException e) {
+                    throwException(tr("illegal value for attribute ''{0}''. Got ''{1}''", "max_lat", max_lat));
+                }
+                current.setMax(new LatLon(maxLon, maxLat));
+            }
+        }
+
+        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+            if (qName.equals("osm")) {
+                if (atts == null) {
+                    throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}", "version", "osm"));
+                }
+                String v = atts.getValue("version");
+                if (v == null) {
+                    throwException(tr("Missing mandatory attribute ''{0}''", "version"));
+                }
+                if (!(v.equals("0.6"))) {
+                    throwException(tr("Unsupported version: {0}", v));
+                }
+            } else if (qName.equals("changeset")) {
+                current = new Changeset();
+                parseChangesetAttributes(current, atts);
+            } else if (qName.equals("tag")) {
+                String key = atts.getValue("k");
+                String value = atts.getValue("v");
+                current.put(key, value);
+            } else {
+                throwException(tr("Undefined element ''{0}'' found in input stream. Aborting.", qName));
+            }
+        }
+
+        @Override
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            if (qName.equals("changeset")) {
+                changesets.add(current);
+            }
+        }
+
+        protected User createUser(String uid, String name) throws OsmDataParsingException {
+            if (uid == null) {
+                if (name == null)
+                    return null;
+                return User.createLocalUser(name);
+            }
+            try {
+                long id = Long.parseLong(uid);
+                return User.createOsmUser(id, name);
+            } catch(NumberFormatException e) {
+                throwException(tr("Illegal value for attribute ''uid''. Got ''{0}''", uid));
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Parse the given input source and return the list of changesets
+     * 
+     * @param source the source input stream
+     * @param progressMonitor  the progress monitor
+     * 
+     * @return the list of changesets
+     * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
+     */
+    public static List<Changeset> parse(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
+        OsmChangesetParser parser = new OsmChangesetParser();
+        try {
+            progressMonitor.beginTask(tr("Parsing list of changesets...", 1));
+            InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
+            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser.new Parser());
+            return parser.getChangesets();
+        } catch(ParserConfigurationException e) {
+            throw new IllegalDataException(e.getMessage(), e);
+        } catch(SAXException e) {
+            throw new IllegalDataException(e.getMessage(), e);
+        } catch(Exception e) {
+            throw new IllegalDataException(e);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmDataParsingException.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmDataParsingException.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/io/OsmDataParsingException.java	(revision 2115)
@@ -7,11 +7,4 @@
 import org.xml.sax.SAXException;
 
-/**
- * Represents a parsing error in an OSM data file.
- * 
- * Use {@see #getColumnNumber()} and {@see #getLineNumber()} to locate
- * the position in the file where the parsing error occured.
- * 
- */
 public class OsmDataParsingException extends SAXException {
     private int columnNumber;
Index: trunk/src/org/openstreetmap/josm/io/OsmReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/io/OsmReader.java	(revision 2115)
@@ -36,5 +36,5 @@
 
 /**
- * Parser for the Osm Api. Read from an input stream and constructs a dataset out of it.
+ * Parser for the Osm Api. Read from an input stream and construct a dataset out of it.
  *
  */
@@ -80,4 +80,55 @@
     }
 
+    private static class OsmPrimitiveData {
+        public long id = 0;
+        public boolean modified = false;
+        public boolean deleted = false;
+        public Date timestamp = new Date();
+        public User user = null;
+        public boolean visible = true;
+        public int version = 0;
+        public LatLon latlon = new LatLon(0,0);
+        private OsmPrimitive primitive;
+
+        public void copyTo(OsmPrimitive osm) {
+            osm.setModified(modified);
+            osm.setDeleted(deleted);
+            //  id < 0 possible if read from a file
+            if (id <= 0) {
+                osm.clearOsmId();
+            } else {
+                osm.setOsmId(id, version);
+            }
+            osm.setTimestamp(timestamp);
+            osm.user = user;
+            osm.setVisible(visible);
+            osm.mappaintStyle = null;
+        }
+
+        public Node createNode() {
+            Node node = new Node();
+            node.setCoor(latlon);
+            copyTo(node);
+            primitive = node;
+            return node;
+        }
+
+        public Way createWay() {
+            Way way = new Way();
+            copyTo(way);
+            primitive = way;
+            return way;
+        }
+        public Relation createRelation() {
+            Relation relation= new Relation();
+            copyTo(relation);
+            primitive = relation;
+            return relation;
+        }
+
+        public void rememberTag(String key, String value) {
+            primitive.put(key, value);
+        }
+    }
 
     /**
@@ -100,5 +151,4 @@
      */
     private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
-
 
     private class Parser extends DefaultHandler {
@@ -241,5 +291,5 @@
                 current.rememberTag(key, value);
             } else {
-                System.out.println(tr("Warning: Undefined element ''{0}'' found in input stream. Skipping.", qName));
+                throwException(tr("Undefined element ''{0}'' found in input stream. Aborting.", qName));
             }
         }
@@ -503,59 +553,3 @@
         }
     }
-
-    /**
-     * Temporarily holds data for a parsed {@see OsmPrimitive} and provides
-     * methods for creating an {@see OsmPrimitive} based on this data.
-     */
-    private static class OsmPrimitiveData {
-        public long id = 0;
-        public boolean modified = false;
-        public boolean deleted = false;
-        public Date timestamp = new Date();
-        public User user = null;
-        public boolean visible = true;
-        public int version = 0;
-        public LatLon latlon = new LatLon(0,0);
-        private OsmPrimitive primitive;
-
-        public void copyTo(OsmPrimitive osm) {
-            //  id < 0 possible if read from a file
-            if (id <= 0) {
-                osm.clearOsmId();
-            } else {
-                osm.setOsmId(id, version);
-            }
-            osm.setDeleted(deleted);
-            osm.setModified(modified);
-            osm.setTimestamp(timestamp);
-            osm.user = user;
-            osm.setVisible(visible);
-            osm.mappaintStyle = null;
-        }
-
-        public Node createNode() {
-            Node node = new Node();
-            node.setCoor(latlon);
-            copyTo(node);
-            primitive = node;
-            return node;
-        }
-
-        public Way createWay() {
-            Way way = new Way();
-            copyTo(way);
-            primitive = way;
-            return way;
-        }
-        public Relation createRelation() {
-            Relation relation= new Relation();
-            copyTo(relation);
-            primitive = relation;
-            return relation;
-        }
-
-        public void rememberTag(String key, String value) {
-            primitive.put(key, value);
-        }
-    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 2115)
@@ -0,0 +1,81 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Reads the history of an {@see OsmPrimitive} from the OSM API server.
+ *
+ */
+public class OsmServerChangesetReader extends OsmServerReader {
+
+    /**
+     * constructor
+     *
+     */
+    public OsmServerChangesetReader(){
+    }
+
+    /**
+     * don't use - not implemented!
+     *
+     */
+    @Override
+    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
+        return null;
+    }
+
+
+    public List<Changeset> queryChangesets(ChangesetQuery query, ProgressMonitor monitor) throws OsmTransferException {
+        try {
+            monitor.beginTask(tr("Reading changesetss..."));
+            StringBuffer sb = new StringBuffer();
+            sb.append("changesets?").append(query.getQueryString());
+            InputStream in = getInputStream(sb.toString(), monitor.createSubTaskMonitor(1, true));
+            if (in == null)
+                return null;
+            monitor.indeterminateSubTask(tr("Downloading changesets ..."));
+            List<Changeset> changesets = OsmChangesetParser.parse(in, monitor.createSubTaskMonitor(1, true));
+            return changesets;
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(IllegalDataException e) {
+            throw new OsmTransferException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
+
+    public Changeset readChangeset(long id, ProgressMonitor monitor) throws OsmTransferException {
+        try {
+            monitor.beginTask(tr("Reading changeset {0} ...",id));
+            StringBuffer sb = new StringBuffer();
+            sb.append("changeset/").append(id);
+            InputStream in = getInputStream(sb.toString(), monitor.createSubTaskMonitor(1, true));
+            if (in == null)
+                return null;
+            monitor.indeterminateSubTask(tr("Downloading changeset ..."));
+            List<Changeset> changesets = OsmChangesetParser.parse(in, monitor.createSubTaskMonitor(1, true));
+            if (changesets == null || changesets.isEmpty())
+                return null;
+            return changesets.get(0);
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(IllegalDataException e) {
+            throw new OsmTransferException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
+
+    public Changeset downloadChangeset(long id, ProgressMonitor monitor) throws OsmTransferException {
+        return null;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 2115)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java	(revision 2115)
@@ -0,0 +1,134 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathException;
+import javax.xml.xpath.XPathFactory;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.UserInfo;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.tools.DateUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class OsmServerUserInfoReader extends OsmServerReader {
+
+    @Override
+    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
+        // not implemented
+        return null;
+    }
+
+    protected String getAttribute(Node node, String name) {
+        return node.getAttributes().getNamedItem(name).getNodeValue();
+    }
+
+    protected UserInfo buildFromXML(Document document) throws OsmDataParsingException{
+        try {
+            XPathFactory factory = XPathFactory.newInstance();
+            XPath xpath = factory.newXPath();
+            UserInfo userInfo = new UserInfo();
+            Node xmlNode = (Node)xpath.compile("/osm/user[1]").evaluate(document, XPathConstants.NODE);
+            if ( xmlNode== null)
+                throw new OsmDataParsingException(tr("XML tag <user> is missing"));
+
+            // -- id
+            String v = getAttribute(xmlNode, "id");
+            if (v == null)
+                throw new OsmDataParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''", "id", "user"));
+            try {
+                userInfo.setId(Long.parseLong(v));
+            } catch(NumberFormatException e) {
+                throw new OsmDataParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}", "id", "user", v));
+            }
+            // -- display name
+            v = getAttribute(xmlNode, "display_name");
+            userInfo.setDisplayName(v);
+            // -- account_created
+            v = getAttribute(xmlNode, "account_created");
+            if (v!=null) {
+                userInfo.setAccountCreated(DateUtils.fromString(v));
+            }
+            // -- description
+            xmlNode = (Node)xpath.compile("/osm/user[1]/description[1]/text()").evaluate(document, XPathConstants.NODE);
+            if (xmlNode != null) {
+                userInfo.setDescription(xmlNode.getNodeValue());
+            }
+            // -- home
+            xmlNode = (Node)xpath.compile("/osm/user[1]/home").evaluate(document, XPathConstants.NODE);
+            if (xmlNode != null) {
+                v = getAttribute(xmlNode, "lat");
+                if (v == null)
+                    throw new OsmDataParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''", "lat", "home"));
+                double lat;
+                try {
+                    lat = Double.parseDouble(v);
+                } catch(NumberFormatException e) {
+                    throw new OsmDataParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}", "lat", "home", v));
+                }
+
+                v = getAttribute(xmlNode, "lon");
+                if (v == null)
+                    throw new OsmDataParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''", "lon", "home"));
+                double lon;
+                try {
+                    lon = Double.parseDouble(v);
+                } catch(NumberFormatException e) {
+                    throw new OsmDataParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}", "lon", "home", v));
+                }
+
+                v = getAttribute(xmlNode, "zoom");
+                if (v == null)
+                    throw new OsmDataParsingException(tr("Missing attribute ''{0}'' on XML tag ''{1}''", "zoom", "home"));
+                int zoom;
+                try {
+                    zoom = Integer.parseInt(v);
+                } catch(NumberFormatException e) {
+                    throw new OsmDataParsingException(tr("Illegal value for attribute ''{0}'' on XML tag ''{1}''. Got {2}", "zoom", "home", v));
+                }
+                userInfo.setHome(new LatLon(lat,lon));
+                userInfo.setHomeZoom(zoom);
+            }
+
+            // -- language list
+            NodeList xmlNodeList = (NodeList)xpath.compile("/osm/user[1]/languages[1]/lang").evaluate(document, XPathConstants.NODESET);
+            if (xmlNodeList != null) {
+                List<String> languages = new LinkedList<String>();
+                for (int i=0; i < xmlNodeList.getLength(); i++) {
+                    languages.add(xmlNodeList.item(i).getNodeValue());
+                }
+                userInfo.setLanguages(languages);
+            }
+            return userInfo;
+        } catch(XPathException e) {
+            throw new OsmDataParsingException(e);
+        }
+    }
+
+    public UserInfo fetchUserInfo(ProgressMonitor monitor) throws OsmTransferException {
+        try {
+            monitor.beginTask("Reading user info ...");
+            InputStream in = getInputStream("user/details", monitor.createSubTaskMonitor(1, true));
+            return buildFromXML(
+                    DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in)
+            );
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(Exception e) {
+            throw new OsmTransferException(e);
+        } finally {
+            monitor.finishTask();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 2115)
@@ -65,19 +65,11 @@
      * 
      * @param primitives the collection of primitives to upload
-     * @param changeset the changeset to be used if <code>changesetProcessingType</code> indicates that
-     *   a new changeset should be opened
-     * @param changesetProcessingType how we handle changesets
      * @param progressMonitor the progress monitor
      * @throws OsmTransferException thrown if an exception occurs
      */
-    protected void uploadChangesIndividually(Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException {
+    protected void uploadChangesIndividually(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException {
         try {
             progressMonitor.beginTask(tr("Starting to upload with one request per primitive ..."));
             progressMonitor.setTicksCount(primitives.size());
-            if (changesetProcessingType.isUseNew()) {
-                api.createChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
-            } else {
-                api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
-            }
             uploadStartTime = System.currentTimeMillis();
             for (OsmPrimitive osm : primitives) {
@@ -86,7 +78,7 @@
                 String msg = "";
                 switch(OsmPrimitiveType.from(osm)) {
-                case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break;
-                case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break;
-                case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break;
+                    case NODE: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading node ''{4}'' (id: {5})"); break;
+                    case WAY: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading way ''{4}'' (id: {5})"); break;
+                    case RELATION: msg = marktr("{0}% ({1}/{2}), {3} left. Uploading relation ''{4}'' (id: {5})"); break;
                 }
                 progressMonitor.subTask(
@@ -107,19 +99,5 @@
             throw new OsmTransferException(e);
         } finally {
-            try {
-                // starting the changeset may have failed, for instance because the user
-                // cancelled the upload task. Only close the changeset if we currently have
-                // an open changeset
-
-                if (api.getCurrentChangeset() != null && api.getCurrentChangeset().getId() > 0) {
-                    api.stopChangeset(changesetProcessingType, progressMonitor.createSubTaskMonitor(0, false));
-                }
-            } catch(Exception e) {
-                OsmChangesetCloseException closeException = new OsmChangesetCloseException(e);
-                closeException.setChangeset(api.getCurrentChangeset());
-                throw closeException;
-            } finally {
-                progressMonitor.finishTask();
-            }
+            progressMonitor.finishTask();
         }
     }
@@ -132,14 +110,9 @@
      * @throws OsmTransferException thrown if an exception occurs
      */
-    protected void uploadChangesAsDiffUpload(Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException {
+    protected void uploadChangesAsDiffUpload(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor) throws OsmTransferException {
         // upload everything in one changeset
         //
         try {
             progressMonitor.beginTask(tr("Starting to upload in one request ..."));
-            if (changesetProcessingType.isUseNew()) {
-                api.createChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
-            } else {
-                api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
-            }
             processed.addAll(api.uploadDiff(primitives, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
         } catch(OsmTransferException e) {
@@ -148,14 +121,5 @@
             throw new OsmTransferException(e);
         } finally {
-            try {
-                api.stopChangeset(changesetProcessingType, progressMonitor.createSubTaskMonitor(0, false));
-            } catch (Exception ee) {
-                OsmChangesetCloseException closeException = new OsmChangesetCloseException(ee);
-                closeException.setChangeset(api.getCurrentChangeset());
-                throw closeException;
-            } finally {
-                progressMonitor.finishTask();
-            }
-
+            progressMonitor.finishTask();
         }
     }
@@ -167,5 +131,5 @@
      * @param primitives list of objects to send
      */
-    public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, ChangesetProcessingType changesetProcessingType, ProgressMonitor progressMonitor) throws OsmTransferException {
+    public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, boolean closeChangesetAfterUpload, ProgressMonitor progressMonitor) throws OsmTransferException {
         processed = new LinkedList<OsmPrimitive>();
         progressMonitor.beginTask(tr("Uploading data ..."));
@@ -184,12 +148,35 @@
                 useDiffUpload = false;
             }
-
+            if (changeset == null) {
+                changeset = new Changeset();
+            }
+            if (changeset.getId() == 0) {
+                api.openChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
+            } else {
+                api.updateChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
+            }
+            api.setChangeset(changeset);
             if (useDiffUpload) {
-                uploadChangesAsDiffUpload(primitives,changeset, changesetProcessingType, progressMonitor.createSubTaskMonitor(0,false));
+                uploadChangesAsDiffUpload(primitives,progressMonitor.createSubTaskMonitor(0,false));
             } else {
-                uploadChangesIndividually(primitives,changeset,changesetProcessingType,  progressMonitor.createSubTaskMonitor(0,false));
-            }
+                uploadChangesIndividually(primitives,progressMonitor.createSubTaskMonitor(0,false));
+            }
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(Exception e) {
+            throw new OsmTransferException(e);
         } finally {
-            progressMonitor.finishTask();
+            try {
+                if (closeChangesetAfterUpload && api.getChangeset() != null && api.getChangeset().getId() > 0) {
+                    api.closeChangeset(changeset,progressMonitor.createSubTaskMonitor(0, false));
+                    api.setChangeset(null);
+                }
+            } catch (Exception ee) {
+                OsmChangesetCloseException closeException = new OsmChangesetCloseException(ee);
+                closeException.setChangeset(api.getChangeset());
+                throw closeException;
+            } finally {
+                progressMonitor.finishTask();
+            }
         }
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 2114)
+++ trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 2115)
@@ -6,4 +6,5 @@
 import java.util.Map.Entry;
 
+import org.openstreetmap.josm.data.coor.CoordinateFormat;
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -14,5 +15,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
-import org.openstreetmap.josm.data.osm.User;
+import org.openstreetmap.josm.data.osm.Tagged;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
@@ -143,11 +144,27 @@
 
     public void visit(Changeset cs) {
-        addCommon(cs, "changeset");
-        out.println(">\n");
-        addTags(cs, "changeset", false);
-    }
-
-    public final void footer(PrintWriter out) {
-        out.println("</osm>");
+        out.print("  <changeset ");
+        out.print(" id='"+cs.getId()+"'");
+        if (cs.getUser() != null) {
+            out.print(" user='"+cs.getUser().getName() +"'");
+            out.print(" uid='"+cs.getUser().getId() +"'");
+        }
+        if (cs.getCreatedAt() != null) {
+            out.print(" created_at='"+DateUtils.fromDate(cs.getCreatedAt()) +"'");
+        }
+        if (cs.getClosedAt() != null) {
+            out.print(" closed_at='"+DateUtils.fromDate(cs.getClosedAt()) +"'");
+        }
+        out.print(" open='"+ (cs.isOpen() ? "true" : "false") +"'");
+        if (cs.getMin() != null) {
+            out.print(" min_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
+            out.print(" min_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
+        }
+        if (cs.getMax() != null) {
+            out.print(" max_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
+            out.print(" max_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
+        }
+        out.println(">");
+        addTags(cs, "changeset", false); // also writes closing </changeset>
     }
 
@@ -164,10 +181,10 @@
     }
 
-    private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) {
+    private void addTags(Tagged osm, String tagname, boolean tagOpen) {
         if (osm.hasKeys()) {
             if (tagOpen) {
                 out.println(">");
             }
-            for (Entry<String, String> e : osm.entrySet()) {
+            for (Entry<String, String> e : osm.getKeys().entrySet()) {
                 if ((osm instanceof Changeset) || !("created_by".equals(e.getKey()))) {
                     out.println("    <tag k='"+ XmlWriter.encode(e.getKey()) +
