Index: trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/actions/UpdateDataAction.java	(revision 1670)
@@ -15,5 +15,4 @@
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTaskList;
 import org.openstreetmap.josm.data.osm.DataSource;
-import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -26,6 +25,6 @@
                         tr("Update Data"),
                         KeyEvent.VK_U,
-                        Shortcut.GROUP_NONE),
-                true);
+                        Shortcut.GROUP_HOTKEY),
+                        true);
     }
 
@@ -33,15 +32,7 @@
         int bboxCount = 0;
         List<Area> areas = new ArrayList<Area>();
-        for(DataSource ds : Main.main.editLayer().data.dataSources)
+        for(DataSource ds : Main.main.editLayer().data.dataSources) {
             areas.add(new Area(ds.bounds.asRect()));
-
-        // This would loop over all DataLayers but download all data to the currently
-        // selected one
-        /*for(Layer l : Main.map.mapView.getAllLayers()) {
-            if(!(l instanceof OsmDataLayer)) continue;
-
-            for(DataSource ds : ((OsmDataLayer)l).data.dataSources)
-                areas.add(new Area(ds.bounds.asRect()));
-        }*/
+        }
 
         // The next two blocks removes every intersection from every DataSource Area
@@ -62,6 +53,7 @@
 
         for(Area a : areas) {
-            if(a.isEmpty())
+            if(a.isEmpty()) {
                 continue;
+            }
             bboxCount++;
         }
@@ -69,20 +61,9 @@
         if(bboxCount == 0) {
             JOptionPane.showMessageDialog(Main.parent,
-                        tr("No data to update found. Have you already opened or downloaded a data layer?"));
-                return;
+                    tr("No data to update found. Have you already opened or downloaded a data layer?"));
+            return;
         }
-
-        int result = new ExtendedDialog(Main.parent,
-                tr("Update Data"),
-                tr("This action will require {0} individual download requests. "
-                        + "Do you wish to continue?", bboxCount),
-                new String[] { tr("Update Data"), tr("Cancel") },
-                new String[] { "updatedata.png", "cancel.png" }).getValue();
-
-        if(result != 1)
-            return;
 
         new DownloadOsmTaskList().download(false, areas);
     }
-
 }
Index: trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 1670)
@@ -0,0 +1,487 @@
+// 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.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.PurgePrimitivesCommand;
+import org.openstreetmap.josm.command.UndeletePrimitivesCommand;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmApiException;
+import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.Shortcut;
+import org.xml.sax.SAXException;
+
+/**
+ * This action synchronizes a set of primitives with their state on the server.
+ * 
+ *
+ */
+public class UpdateSelectionAction extends JosmAction {
+
+    static public int DEFAULT_MAX_SIZE_UPDATE_SELECTION = 50;
+
+    /**
+     * Undelete a node which is already deleted on the server. The API
+     * doesn't offer a call for "undeleting" a node. We therefore create
+     * a clone of the node which we flag as new. On the next upload the
+     * server will assign the node a new id.
+     * 
+     * @param node the node to undelete
+     */
+    protected void  undeleteNode(Node node) {
+        UndeletePrimitivesCommand cmd = new UndeletePrimitivesCommand(node);
+        Main.main.undoRedo.add(cmd);
+    }
+
+    /**
+     * Undelete a way which is already deleted on the server.
+     * 
+     * This method also checks whether there are additional nodes referred to by
+     * this way which are deleted on the server too.
+     * 
+     * @param way the way to undelete
+     * @see #undeleteNode(Node)
+     */
+    protected void undeleteWay(final Way way) {
+        class NodeGoneChecker extends PleaseWaitRunnable {
+
+            UndeletePrimitivesCommand cmd = null;
+
+            public NodeGoneChecker() {
+                super(tr("Undeleting Way..."), false);
+            }
+
+            @Override
+            protected void cancel() {
+                OsmApi.getOsmApi().cancel();
+            }
+
+            @Override
+            protected void finish() {
+                if (cmd != null) {
+                    Main.main.undoRedo.add(cmd);
+                }
+            }
+
+            /**
+             * replies the subset of the node list which already
+             * have an assigned id
+             * 
+             * @param way  the way
+             * @return the node list
+             */
+            protected ArrayList<Node> getCandidateNodes(Way way) {
+                ArrayList<Node> candidates = new ArrayList<Node>();
+                for (Node n : way.nodes) {
+                    if (n.id > 0 && ! candidates.contains(n)) {
+                        candidates.add(n);
+                    }
+                }
+                return candidates;
+            }
+
+            /**
+             * checks whether a specific node is deleted on the server
+             * 
+             * @param n the node
+             * @return true, if the node is deleted; false, otherwise
+             * @throws OsmTransferException thrown, if an error occurs while communicating with the API
+             */
+            protected boolean isGone(Node n) throws OsmTransferException {
+                OsmServerObjectReader reader = new OsmServerObjectReader(n.id, OsmPrimitiveType.from(n), true);
+                try {
+                    reader.parseOsm();
+                } catch(OsmApiException e) {
+                    if (e.getResponseCode() == HttpURLConnection.HTTP_GONE)
+                        return true;
+                    throw e;
+                } catch(OsmTransferException e) {
+                    throw e;
+                }
+                return false;
+            }
+
+            /**
+             * displays a confirmation message. The user has to confirm that additional dependent
+             * nodes should be undeleted too.
+             * 
+             * @param way  the way
+             * @param dependent a list of dependent nodes which have to be undelete too
+             * @return true, if the user confirms; false, otherwise
+             */
+            protected boolean confirmUndeleteDependentPrimitives(Way way, ArrayList<OsmPrimitive> dependent) {
+                String [] options = {
+                        tr("Yes, undelete them too"),
+                        tr("No, cancel operation")
+                };
+                int ret = JOptionPane.showOptionDialog(
+                        Main.parent,
+                        tr("<html>There are {0} additional nodes used by way {1}<br>"
+                                + "which are deleted on the server.<br>"
+                                + "<br>"
+                                + "Do you want to undelete these nodes too?</html>",
+                                Long.toString(dependent.size()), Long.toString(way.id)),
+                                tr("Undelete additional nodes?"),
+                                JOptionPane.YES_NO_OPTION,
+                                JOptionPane.QUESTION_MESSAGE,
+                                null,
+                                options,
+                                options[0]
+                );
+
+                switch(ret) {
+                case JOptionPane.CLOSED_OPTION: return false;
+                case JOptionPane.YES_OPTION: return true;
+                case JOptionPane.NO_OPTION: return false;
+                }
+                return false;
+
+            }
+
+            @Override
+            protected void realRun() throws SAXException, IOException, OsmTransferException {
+                ArrayList<Node> candidate = getCandidateNodes(way);
+                ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
+                Main.pleaseWaitDlg.progress.setMinimum(0);
+                Main.pleaseWaitDlg.progress.setMaximum(candidate.size());
+
+                for (int i=0; i<candidate.size();i++) {
+                    Node n = candidate.get(i);
+                    Main.pleaseWaitDlg.progress.setValue(i);
+                    Main.pleaseWaitDlg.currentAction.setText(tr("Checking whether node {0} is gone ...", n.id));
+                    if (isGone(n)) {
+                        toDelete.add(n);
+                    }
+                }
+                if (toDelete.size() > 0) {
+                    if (!confirmUndeleteDependentPrimitives(way, toDelete))
+                        return;
+                }
+
+                toDelete.add(way);
+                cmd = new UndeletePrimitivesCommand(toDelete);
+            }
+        }
+
+        Main.worker.submit(new NodeGoneChecker());
+    }
+
+    /**
+     * Undelete a relation which is already deleted on the server.
+     * 
+     * This method  checks whether there are additional primitives referred to by
+     * this relation which are already deleted on the server.
+     * 
+     * @param r the relation
+     * @see #undeleteNode(Node)
+     */
+    protected void undeleteRelation(final Relation r) {
+        class RelationMemberGoneChecker extends PleaseWaitRunnable {
+
+            UndeletePrimitivesCommand cmd = null;
+
+            public RelationMemberGoneChecker() {
+                super(tr("Undeleting relation..."), false);
+            }
+
+            @Override
+            protected void cancel() {
+                OsmApi.getOsmApi().cancel();
+            }
+
+            @Override
+            protected void finish() {
+                if (cmd != null) {
+                    Main.main.undoRedo.add(cmd);
+                }
+            }
+
+            protected ArrayList<OsmPrimitive> getCandidateRelationMembers(Relation r) {
+                ArrayList<OsmPrimitive> candidates = new ArrayList<OsmPrimitive>();
+                for (RelationMember m : r.members) {
+                    if (m.member.id > 0 && !candidates.contains(m.member)) {
+                        candidates.add(m.member);
+                    }
+                }
+                return candidates;
+            }
+
+            protected boolean isGone(OsmPrimitive primitive) throws OsmTransferException {
+                OsmServerObjectReader reader = new OsmServerObjectReader(
+                        primitive.id,
+                        OsmPrimitiveType.from(primitive),
+                        true);
+                try {
+                    reader.parseOsm();
+                } catch(OsmApiException e) {
+                    if (e.getResponseCode() == HttpURLConnection.HTTP_GONE)
+                        return true;
+                    throw e;
+                } catch(OsmTransferException e) {
+                    throw e;
+                }
+                return false;
+            }
+
+            protected boolean confirmUndeleteDependentPrimitives(Relation r, ArrayList<OsmPrimitive> dependent) {
+                String [] options = {
+                        tr("Yes, undelete them too"),
+                        tr("No, cancel operation")
+                };
+                int ret = JOptionPane.showOptionDialog(
+                        Main.parent,
+                        tr("<html>There are {0} additional primitives referred to by relation {1}<br>"
+                                + "which are deleted on the server.<br>"
+                                + "<br>"
+                                + "Do you want to undelete them too?</html>",
+                                Long.toString(dependent.size()), Long.toString(r.id)),
+                                tr("Undelete dependent primitives?"),
+                                JOptionPane.YES_NO_OPTION,
+                                JOptionPane.QUESTION_MESSAGE,
+                                null,
+                                options,
+                                options[0]
+                );
+
+                switch(ret) {
+                case JOptionPane.CLOSED_OPTION: return false;
+                case JOptionPane.YES_OPTION: return true;
+                case JOptionPane.NO_OPTION: return false;
+                }
+                return false;
+
+            }
+
+            @Override
+            protected void realRun() throws SAXException, IOException, OsmTransferException {
+                ArrayList<OsmPrimitive> candidate = getCandidateRelationMembers(r);
+                ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
+                Main.pleaseWaitDlg.progress.setMinimum(0);
+                Main.pleaseWaitDlg.progress.setMaximum(candidate.size());
+
+                for (int i=0; i<candidate.size();i++) {
+                    OsmPrimitive primitive = candidate.get(i);
+                    Main.pleaseWaitDlg.progress.setValue(i);
+                    Main.pleaseWaitDlg.currentAction.setText(tr("Checking whether primitive {0} is gone ...", primitive.id));
+                    if (isGone(primitive)) {
+                        toDelete.add(primitive);
+                    }
+                }
+                if (toDelete.size() > 0) {
+                    if (!confirmUndeleteDependentPrimitives(r, toDelete))
+                        return;
+                }
+
+                toDelete.add(r);
+                cmd = new UndeletePrimitivesCommand(toDelete);
+            }
+        }
+
+        Main.worker.submit(new RelationMemberGoneChecker());
+    }
+
+    /**
+     * User has decided to keep his local version of a primitive which had been deleted
+     * on the server
+     * 
+     * @param id the primitive id
+     */
+    protected void handlePrimitiveGoneKeepMine(long id) {
+        OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id);
+        if (primitive instanceof Node) {
+            undeleteNode((Node)primitive);
+        } else if (primitive instanceof Way) {
+            undeleteWay((Way)primitive);
+        } else if (primitive instanceof Relation) {
+            undeleteRelation((Relation)primitive);
+        }
+    }
+
+    /**
+     * User has decided to delete his local version of a primitive which had been deleted
+     * on the server
+     * 
+     * @param id the primitive id
+     */
+    protected void handlePrimitiveGoneDeleteMine(long id) {
+        OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id);
+        PurgePrimitivesCommand cmd = new PurgePrimitivesCommand(primitive);
+        Main.main.undoRedo.add(cmd);
+        Main.map.mapView.repaint();
+    }
+
+    /**
+     * handle an exception thrown because a primitive was deleted on the server
+     * 
+     * @param id the primitive id
+     */
+    protected void handlePrimitiveGoneException(long id) {
+        Object[] options = new Object[] {
+                tr("Keep mine"),
+                tr("Delete mine"),
+                tr("Cancel")
+        };
+        Object defaultOption = options[0];
+        String msg =  tr("<html>The OSM primitive with id <strong>{0}</strong> has been deleted<br>"
+                + "on the server by another mapper.<br>"
+                + "<br>"
+                + "Click <strong>{1}</strong> to keep your primitive and ignore the deleted state.<br>"
+                + "Your primitive will be assigend a new id.<br>"
+                + "Click <strong>{2}</strong> to accept the state on the server and to delete your primitive.<br>"
+                + "Click <strong>{3}</strong> to cancel.<br>",
+                id, options[0], options[1], options[2]
+        );
+        int ret = JOptionPane.showOptionDialog(
+                null,
+                msg,
+                tr("Primitive deleted on the server"),
+                JOptionPane.YES_NO_CANCEL_OPTION,
+                JOptionPane.ERROR_MESSAGE,
+                null,
+                options,
+                defaultOption
+        );
+        switch(ret) {
+        case JOptionPane.CLOSED_OPTION: return;
+        case JOptionPane.CANCEL_OPTION: return;
+        case 0: handlePrimitiveGoneKeepMine(id); break;
+        case 1: handlePrimitiveGoneDeleteMine(id); break;
+        default:
+            // should not happen
+            throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
+        }
+    }
+
+    /**
+     * handle an exception thrown during updating a primitive
+     * 
+     * @param id the id of the primitive
+     * @param e the exception
+     */
+    protected void handleUpdateException(long id, Exception e) {
+        if (e instanceof OsmApiException) {
+            OsmApiException ex = (OsmApiException)e;
+            // if the primitive was deleted on the server ask the user
+            // whether he wants to undelete it
+            //
+            if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) {
+                handlePrimitiveGoneException(id);
+                return;
+            }
+        }
+
+        e.printStackTrace();
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                tr("Failed to update the selected primitives."),
+                tr("Update failed"),
+                JOptionPane.ERROR_MESSAGE
+        );
+    }
+
+    /**
+     * 
+     * @param id
+     */
+    protected void handleMissingPrimitive(long id) {
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                tr("Could not find primitive with id {0} in the current dataset", id),
+                tr("Missing primitive"),
+                JOptionPane.ERROR_MESSAGE
+        );
+    }
+
+    /**
+     * Updates the primitive with id <code>id</code> with the current state kept on the server.
+     * 
+     * @param id
+     */
+    public void updatePrimitive(long id) {
+        OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id);
+        if (primitive == null) {
+            handleMissingPrimitive(id);
+            return;
+        }
+        OsmServerObjectReader reader = new OsmServerObjectReader(
+                id,
+                OsmPrimitiveType.from(primitive),
+                true);
+        DataSet ds = null;
+        try {
+            ds = reader.parseOsm();
+        } catch(Exception e) {
+            handleUpdateException(id, e);
+            return;
+        }
+        Main.main.editLayer().mergeFrom(ds);
+    }
+
+
+    public UpdateSelectionAction() {
+        super(tr("Update Selection"),
+                "updateselection",
+                tr("Updates the currently selected primitives from the server"),
+                Shortcut.registerShortcut("file:updateselection",
+                        tr("Update Selection"),
+                        KeyEvent.VK_U,
+                        Shortcut.GROUP_HOTKEY + Shortcut.GROUPS_ALT2),
+                        true);
+    }
+
+
+    public void actionPerformed(ActionEvent e) {
+        Collection<OsmPrimitive> selection = Main.ds.getSelected();
+        if (selection.size() == 0) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("There are no selected primitives to update."),
+                    tr("Selection empty"),
+                    JOptionPane.INFORMATION_MESSAGE
+            );
+            return;
+        }
+
+        // check whether the current selection has an acceptable range.
+        // We don't want to hammer the API with hundreds of individual
+        // GET requests.
+        //
+        if (selection.size() > DEFAULT_MAX_SIZE_UPDATE_SELECTION) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("<html>There are  <strong>{0}</strong> primitives <br>"
+                            + "selected for individual update. Please reduce the selection<br>"
+                            + "to max. {1} primitives.</html>",
+                            selection.size(), DEFAULT_MAX_SIZE_UPDATE_SELECTION
+                    ),
+                    tr("Selection too big"),
+                    JOptionPane.WARNING_MESSAGE
+            );
+            return;
+        }
+        for(OsmPrimitive primitive : selection) {
+            // FIXME: users should be able to abort this loop
+            //
+            updatePrimitive(primitive.id);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 1670)
@@ -4,5 +4,4 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.awt.EventQueue;
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
@@ -13,6 +12,5 @@
 import java.util.LinkedList;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
+import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -23,14 +21,11 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.historycombobox.SuggestingJHistoryComboBox;
-import org.openstreetmap.josm.io.DiffResultReader;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiException;
@@ -47,8 +42,11 @@
  * An dialog is displayed asking the user to specify a rectangle to grab.
  * The url and account settings from the preferences are used.
+ * 
+ * If the upload fails this action offers various options to resolve conflicts.
  *
  * @author imi
  */
 public class UploadAction extends JosmAction {
+    static private Logger logger = Logger.getLogger(UploadAction.class.getName());
 
     public static final String HISTORY_KEY = "upload.comment.history";
@@ -215,5 +213,4 @@
                         return;
                     }
-                    System.out.println("got exception: " + sxe.toString());
                     uploadFailed = true;
                     lastException = sxe;
@@ -230,59 +227,326 @@
                 server.disconnectActiveConnection();
                 uploadCancelled = true;
-            };
-
-        }
+            }
+        }
+
         Main.worker.execute(new UploadDiffTask());
     }
 
-    public void handleFailedUpload(Exception e) {
+    /**
+     * Synchronizes the local state of an {@see OsmPrimitive} with its state on the
+     * server. The method uses an individual GET for the primitive.
+     * 
+     * @param id the primitive ID
+     */
+    protected void synchronizePrimitive(final String id) {
+
+        /**
+         * The asynchronous task to update a a specific id
+         *
+         */
+        class UpdatePrimitiveTask extends  PleaseWaitRunnable {
+
+            private boolean uploadCancelled = false;
+            private boolean uploadFailed = false;
+            private Exception lastException = null;
+
+            public UpdatePrimitiveTask() {
+                super(tr("Updating primitive"),false /* don't ignore exceptions */);
+            }
+
+            @Override protected void realRun() throws SAXException, IOException {
+                try {
+                    UpdateSelectionAction act = new UpdateSelectionAction();
+                    act.updatePrimitive(Long.parseLong(id));
+                } catch (Exception sxe) {
+                    if (uploadCancelled) {
+                        System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + sxe.toString());
+                        return;
+                    }
+                    uploadFailed = true;
+                    lastException = sxe;
+                }
+            }
+
+            @Override protected void finish() {
+                if (uploadFailed) {
+                    handleFailedUpload(lastException);
+                }
+            }
+
+            @Override protected void cancel() {
+                OsmApi.getOsmApi().cancel();
+                uploadCancelled = true;
+            }
+        }
+
+        Main.worker.execute(new UpdatePrimitiveTask());
+    }
+
+    /**
+     * Synchronizes the local state of the dataset with the state on the server.
+     * 
+     * Reuses the functionality of {@see UpdateDataAction}.
+     * 
+     * @see UpdateDataAction#actionPerformed(ActionEvent)
+     */
+    protected void synchronizeDataSet() {
+        UpdateDataAction act = new UpdateDataAction();
+        act.actionPerformed(new ActionEvent(this,0,""));
+    }
+
+    /**
+     * Handles the case that a conflict in a specific {@see OsmPrimitive} was detected while
+     * uploading
+     * 
+     * @param primitiveType  the type of the primitive, either <code>node</code>, <code>way</code> or
+     *    <code>relation</code>
+     * @param id  the id of the primitive
+     * @param serverVersion  the version of the primitive on the server
+     * @param myVersion  the version of the primitive in the local dataset
+     */
+    protected void handleUploadConflictForKnownConflict(String primitiveType, String id, String serverVersion, String myVersion) {
+        Object[] options = new Object[] {
+                tr("Synchronize {0} {1} only", tr(primitiveType), id),
+                tr("Synchronize entire dataset"),
+                tr("Cancel")
+        };
+        Object defaultOption = options[0];
+        String msg =  tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
+                + "of your nodes, ways, or relations.<br>"
+                + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>"
+                + "the server has version {2}, your version is {3}.<br>"
+                + "<br>"
+                + "Click <strong>{4}</strong> to synchronize the conflicting primitive only.<br>"
+                + "Click <strong>{5}</strong> to synchronize the entire local dataset with the server.<br>"
+                + "Click <strong>{6}</strong> to abort and continue editing.<br></html>",
+                tr(primitiveType), id, serverVersion, myVersion,
+                options[0], options[1], options[2]
+        );
+        int optionsType = JOptionPane.YES_NO_CANCEL_OPTION;
+        int ret = JOptionPane.showOptionDialog(
+                null,
+                msg,
+                tr("Conflict detected"),
+                optionsType,
+                JOptionPane.ERROR_MESSAGE,
+                null,
+                options,
+                defaultOption
+        );
+        switch(ret) {
+        case JOptionPane.CLOSED_OPTION: return;
+        case JOptionPane.CANCEL_OPTION: return;
+        case 0: synchronizePrimitive(id); break;
+        case 1: synchronizeDataSet(); break;
+        default:
+            // should not happen
+            throw new IllegalStateException(tr("unexpected return value. Got {0}", ret));
+        }
+    }
+
+    /**
+     * Handles the case that a conflict was detected while uploading where we don't
+     * know what {@see OsmPrimitive} actually caused the conflict (for whatever reason)
+     * 
+     */
+    protected void handleUploadConflictForUnknownConflict() {
+        Object[] options = new Object[] {
+                tr("Synchronize entire dataset"),
+                tr("Cancel")
+        };
+        Object defaultOption = options[0];
+        String msg =  tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
+                + "of your nodes, ways, or relations.<br>"
+                + "<br>"
+                + "Click <strong>{0}</strong> to synchronize the entire local dataset with the server.<br>"
+                + "Click <strong>{1}</strong> to abort and continue editing.<br></html>",
+                options[0], options[1]
+        );
+        int optionsType = JOptionPane.YES_NO_OPTION;
+        int ret = JOptionPane.showOptionDialog(
+                null,
+                msg,
+                tr("Conflict detected"),
+                optionsType,
+                JOptionPane.ERROR_MESSAGE,
+                null,
+                options,
+                defaultOption
+        );
+        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));
+        }
+    }
+
+    /**
+     * handles an upload conflict, i.e. an error indicated by a HTTP return code 409.
+     * 
+     * @param e  the exception
+     */
+    protected void handleUploadConflict(OsmApiException e) {
+        String pattern = "Version mismatch: Provided (\\d+), server had: (\\d+) of (\\S+) (\\d+)";
+        Pattern p = Pattern.compile(pattern);
+        Matcher m = p.matcher(e.getErrorHeader());
+        if (m.matches()) {
+            handleUploadConflictForKnownConflict(m.group(3), m.group(4), m.group(2),m.group(1));
+        } else {
+            logger.warning(tr("Warning: error header \"{0}\" did not match expected pattern \"{1}\"", e.getErrorHeader(),pattern));
+            handleUploadConflictForUnknownConflict();
+        }
+    }
+
+    /**
+     * Handles an upload error due to a violated precondition, i.e. a HTTP return code 412
+     * 
+     * @param e the exception
+     */
+    protected void handlePreconditionFailed(OsmApiException e) {
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                tr("<html>Uploading to the server <strong>failed</strong> because your current<br>"
+                        +"dataset violates a precondition.<br>"
+                        +"The error message is:<br>"
+                        + "{0}"
+                        + "</html>",
+                        e.getMessage()
+                ),
+                tr("Precondition violation"),
+                JOptionPane.ERROR_MESSAGE
+        );
+        e.printStackTrace();
+    }
+
+
+    /**
+     * Handles an error due to a delete request on an already deleted
+     * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we know what
+     * {@see OsmPrimitive} is responsible for the error.
+     * 
+     *  Reuses functionality of the {@see UpdateSelectionAction} to resolve
+     *  conflicts due to mismatches in the deleted state.
+     * 
+     * @param primitiveType the type of the primitive
+     * @param id the id of the primitive
+     * 
+     * @see UpdateSelectionAction#handlePrimitiveGoneException(long)
+     */
+    protected void handleGoneForKnownPrimitive(String primitiveType, String id) {
+        UpdateSelectionAction act = new UpdateSelectionAction();
+        act.handlePrimitiveGoneException(Long.parseLong(id));
+    }
+
+    /**
+     * handles the case of an error due to a delete request on an already deleted
+     * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which
+     * {@see OsmPrimitive} is causing the error.
+     * 
+     * @param e the exception
+     */
+    protected void handleGoneForUnknownPrimitive(OsmApiException e) {
+        String msg =  tr("<html>Uploading <strong>failed</strong> because a primitive you tried to<br>"
+                + "delete on the server is already deleted.<br>"
+                + "<br>"
+                + "The error message is:<br>"
+                + "{0}"
+                + "</html>",
+                e.getMessage()
+        );
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                msg,
+                tr("Primitive already deleted"),
+                JOptionPane.ERROR_MESSAGE
+        );
+
+    }
+
+    /**
+     * Handles an error which is caused by a delete request for an already deleted
+     * {@see OsmPrimitive} on the server, i.e. a HTTP response code of 410.
+     * Note that an <strong>update</strong> on an already deleted object results
+     * in a 409, not a 410.
+     * 
+     * @param e the exception
+     */
+    protected void handleGone(OsmApiException e) {
+        String pattern = "The (\\S+) with the id (\\d+) has already been deleted";
+        Pattern p = Pattern.compile(pattern);
+        Matcher m = p.matcher(e.getErrorHeader());
+        if (m.matches()) {
+            handleGoneForKnownPrimitive(m.group(1), m.group(2));
+        } else {
+            logger.warning(tr("Error header \"{0}\" doesn't match expected pattern \"{1}\"",e.getErrorHeader(), pattern));
+            handleGoneForUnknownPrimitive(e);
+        }
+    }
+
+
+    /**
+     * error handler for any exception thrown during upload
+     * 
+     * @param e the exception
+     */
+    protected void handleFailedUpload(Exception e) {
+        // API initialization failed. Notify the user and return.
+        //
         if (e instanceof OsmApiInitializationException) {
-            handleOsmApiInitializationException(e);
+            handleOsmApiInitializationException((OsmApiInitializationException)e);
             return;
         }
+
         if (e instanceof OsmApiException) {
             OsmApiException ex = (OsmApiException)e;
+            // There was an upload conflict. Let the user decide whether
+            // and how to resolve it
+            //
             if(ex.getResponseCode() == HttpURLConnection.HTTP_CONFLICT) {
-                Pattern p = Pattern.compile("Version mismatch: Provided (\\d+), server had: (\\d+) of (\\S+) (\\d+)");
-                Matcher m = p.matcher(ex.getErrorHeader());
-                String msg;
-                if (m.matches()) {
-                    msg =  tr("<html>Uploading <strong>failed</strong> because the server has a newer version of one<br>"
-                            + "of your nodes, ways or relations.<br>"
-                            + "The conflict is caused by the <strong>{0}</strong> with id <strong>{1}</strong>,<br>"
-                            + "the server has version {2}, your version is {3}.<br>"
-                            + "Please synchronize your local dataset using <br>"
-                            + "<strong>File-&gt;Update Data</strong>, resolve<br>"
-                            + "any conflicts and try to upload again.</html>",
-                            m.group(3),m.group(4), m.group(2), m.group(1)
-                    );
-                } else {
-                    msg =  tr("<html>Uploading failed because the server has a newer version of one<br>"
-                            + "of your nodes, ways or relations.<br>"
-                            + "Please synchronize your local dataset using <br>"
-                            + "<strong>File-&gt;Update Data</strong>, resolve<br>"
-                            + "any conflicts and try to upload again.</html>"
-                    );
-                }
-                JOptionPane.showMessageDialog(
-                        null,
-                        msg,
-                        tr("Upload to OSM API failed"),
-                        JOptionPane.WARNING_MESSAGE
-                );
+                handleUploadConflict(ex);
                 return;
             }
-        }
+            // There was a precondition failed. Notify the user.
+            //
+            else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
+                handlePreconditionFailed(ex);
+                return;
+            }
+            // Tried to delete an already deleted primitive? Let the user
+            // decide whether and how to resolve this conflict.
+            //
+            else if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) {
+                handleGone(ex);
+                return;
+            }
+        }
+
+        // For any other exception just notify the user
+        //
+        String msg = e.getMessage().substring(0,Math.min(80, e.getMessage().length()));
+        if (msg.length() < e.getMessage().length()) {
+            msg += " ...";
+        }
+        e.printStackTrace();
         JOptionPane.showMessageDialog(
                 null,
-                e.getMessage(),
+                msg,
                 tr("Upload to OSM API failed"),
                 JOptionPane.ERROR_MESSAGE
         );
-    }
-
-    protected void handleOsmApiInitializationException(Exception e) {
+
+    }
+
+    /**
+     * handles an exception caught during OSM API initialization
+     * 
+     * @param e the exception
+     */
+    protected void handleOsmApiInitializationException(OsmApiInitializationException e) {
         JOptionPane.showMessageDialog(
-                null,
+                Main.parent,
                 tr(   "Failed to initialize communication with the OSM server {0}.\n"
                         + "Check the server URL in your preferences and your internet connection.",
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java	(revision 1670)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.io.OsmServerLocationReader;
 import org.openstreetmap.josm.io.OsmServerReader;
+import org.openstreetmap.josm.io.OsmTransferException;
 import org.xml.sax.SAXException;
 
@@ -32,6 +33,7 @@
     private static Bounds currentBounds;
     private Future<Task> task = null;
+    private DataSet downloadedData;
 
-    private static class Task extends PleaseWaitRunnable {
+    private class Task extends PleaseWaitRunnable {
         private OsmServerReader reader;
         private DataSet dataSet;
@@ -49,5 +51,5 @@
         }
 
-        @Override public void realRun() throws IOException, SAXException {
+        @Override public void realRun() throws IOException, SAXException, OsmTransferException {
             Main.pleaseWaitDlg.setCustomText(msg);
             dataSet = reader.parseOsm();
@@ -59,16 +61,18 @@
             if (dataSet.allPrimitives().isEmpty()) {
                 // If silent is set to true, we don't want to see information messages
-                if(!silent)
+                if(!silent) {
                     errorMessage = tr("No data imported.");
+                }
                 // need to synthesize a download bounds lest the visual indication of downloaded
                 // area doesn't work
                 dataSet.dataSources.add(new DataSource(currentBounds, "OpenStreetMap server"));
             }
-
+            rememberDownloadedData(dataSet);
             OsmDataLayer layer = new OsmDataLayer(dataSet, tr("Data Layer {0}", num), null);
-            if (newLayer)
+            if (newLayer) {
                 Main.main.addLayer(layer);
-            else
+            } else {
                 Main.main.editLayer().mergeFrom(layer);
+            }
 
             Main.pleaseWaitDlg.setCustomText("");
@@ -76,10 +80,19 @@
 
         @Override protected void cancel() {
-            if (reader != null)
+            if (reader != null) {
                 reader.cancel();
+            }
             Main.pleaseWaitDlg.cancel.setEnabled(false);
         }
     }
     private JCheckBox checkBox = new JCheckBox(tr("OpenStreetMap data"), true);
+
+    private void rememberDownloadedData(DataSet ds) {
+        this.downloadedData = ds;
+    }
+
+    public DataSet getDownloadedData() {
+        return downloadedData;
+    }
 
     public void download(DownloadAction action, double minlat, double minlon,
@@ -96,5 +109,5 @@
 
         boolean newLayer = action != null
-                                && (action.dialog == null || action.dialog.newLayer.isSelected());
+        && (action.dialog == null || action.dialog.newLayer.isSelected());
 
         Task t = new Task(newLayer,
@@ -124,5 +137,5 @@
                 false,
                 getDataLayersCount(),
-                "");
+        "");
         task = Main.worker.submit(t, t);
     }
@@ -145,13 +158,14 @@
         int num = 0;
         for(Layer l : Main.map.mapView.getAllLayers())
-            if(l instanceof OsmDataLayer)
+            if(l instanceof OsmDataLayer) {
                 num++;
+            }
         return num;
     }
 
-   /*
-    * (non-Javadoc)
-    * @see org.openstreetmap.josm.gui.download.DownloadDialog.DownloadTask#getErrorMessage()
-    */
+    /*
+     * (non-Javadoc)
+     * @see org.openstreetmap.josm.gui.download.DownloadDialog.DownloadTask#getErrorMessage()
+     */
     public String getErrorMessage() {
         if(task == null)
@@ -161,6 +175,6 @@
             Task t = task.get();
             return t.errorMessage == null
-                ? ""
-                : t.errorMessage;
+            ? ""
+                    : t.errorMessage;
         } catch (Exception e) {
             return "";
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskList.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskList.java	(revision 1670)
@@ -4,14 +4,22 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.EventQueue;
+import java.awt.event.ActionEvent;
 import java.awt.geom.Area;
 import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.UpdateSelectionAction;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.gui.download.DownloadDialog.DownloadTask;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -62,6 +70,7 @@
     public void download(boolean newLayer, Collection<Area> areas) {
         List<Rectangle2D> rects = new LinkedList<Rectangle2D>();
-        for(Area a : areas)
+        for(Area a : areas) {
             rects.add(a.getBounds2D());
+        }
 
         download(newLayer, rects);
@@ -76,17 +85,105 @@
         for(DownloadTask dt : osmTasks) {
             String err = dt.getErrorMessage();
-            if(err.equals(""))
+            if(err.equals("")) {
                 continue;
+            }
             errors += "* " + err + "\r\n";
         }
 
-        osmTasks.clear();
-        if(errors.equals(""))
+        if(! errors.equals("")) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("The following errors occured during mass download:") + "\r\n" + errors,
+                    tr("Errors during Download"),
+                    JOptionPane.ERROR_MESSAGE);
             return;
+        }
 
-        JOptionPane.showMessageDialog(Main.parent,
-                tr("The following errors occured during mass download:") + "\r\n" + errors,
-                tr("Errors during Download"),
-                JOptionPane.ERROR_MESSAGE);
+        Set<Long> myPrimitiveIds = Main.main.editLayer().data.getPrimitiveIds();
+        Set<Long> downloadedIds = getDownloadedIds();
+        myPrimitiveIds.removeAll(downloadedIds);
+        if (! myPrimitiveIds.isEmpty()) {
+            handlePotentiallyDeletedPrimitives(myPrimitiveIds);
+        }
+    }
+
+    protected void checkPotentiallyDeletedPrimitives(Set<Long> potentiallyDeleted) {
+        DataSet ds =  Main.main.editLayer().data;
+        ArrayList<OsmPrimitive> toSelect = new ArrayList<OsmPrimitive>();
+        for (Long id : potentiallyDeleted) {
+            OsmPrimitive primitive = ds.getPrimitiveById(id);
+            if (primitive != null) {
+                toSelect.add(primitive);
+            }
+        }
+        ds.setSelected(toSelect);
+        EventQueue.invokeLater(
+                new Runnable() {
+                    public void run() {
+                        new UpdateSelectionAction().actionPerformed(new ActionEvent(this, 0, ""));
+                    }
+                }
+        );
+    }
+
+    protected void handlePotentiallyDeletedPrimitives(Set<Long> potentiallyDeleted) {
+        String [] options = {
+                "Check individually",
+                "Ignore"
+        };
+
+        String message = tr("<html>"
+                +  "There are {0} primitives in your local dataset which<br>"
+                + "might be deleted on the server. If you later try to delete or<br>"
+                + "update them on the server the server is likely to report a<br>"
+                + "conflict.<br>"
+                + "<br>"
+                + "Click <strong>{1}</strong> to check these primitives individually.<br>"
+                + "Click <strong>{2}</strong> to ignore.<br>"
+                + "</html>",
+                potentiallyDeleted.size(), options[0], options[1]
+        );
+
+        int ret = JOptionPane.showOptionDialog(
+                Main.parent,
+                message,
+                tr("Deleted or moved primitives"),
+                JOptionPane.YES_NO_OPTION,
+                JOptionPane.WARNING_MESSAGE,
+                null,
+                options,
+                options[0]
+        );
+        switch(ret) {
+        case JOptionPane.CLOSED_OPTION: return;
+        case JOptionPane.NO_OPTION: return;
+        case JOptionPane.YES_OPTION: checkPotentiallyDeletedPrimitives(potentiallyDeleted); break;
+        }
+    }
+
+    protected boolean wasDownloaded(long id, DataSet ds) {
+        OsmPrimitive primitive = ds.getPrimitiveById(id);
+        return primitive != null;
+    }
+
+    public boolean wasDownloaded(long id) {
+        for (DownloadTask task : osmTasks) {
+            if(task instanceof DownloadOsmTask) {
+                DataSet ds = ((DownloadOsmTask)task).getDownloadedData();
+                if(wasDownloaded(id,ds)) return true;
+            }
+        }
+        return false;
+    }
+
+
+    public Set<Long> getDownloadedIds() {
+        HashSet<Long> ret = new HashSet<Long>();
+        for (DownloadTask task : osmTasks) {
+            if(task instanceof DownloadOsmTask) {
+                DataSet ds = ((DownloadOsmTask)task).getDownloadedData();
+                ret.addAll(ds.getPrimitiveIds());
+            }
+        }
+        return ret;
     }
 }
Index: trunk/src/org/openstreetmap/josm/actions/search/SelectionWebsiteLoader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/search/SelectionWebsiteLoader.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/actions/search/SelectionWebsiteLoader.java	(revision 1670)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.io.OsmIdReader;
+import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.io.ProgressInputStream;
 import org.xml.sax.SAXException;
@@ -47,8 +48,9 @@
             for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
                 if (ids.containsKey(osm.id) && osm.getClass().getName().toLowerCase().endsWith(ids.get(osm.id))) {
-                    if (mode == SearchAction.SearchMode.remove)
+                    if (mode == SearchAction.SearchMode.remove) {
                         sel.remove(osm);
-                    else
+                    } else {
                         sel.add(osm);
+                    }
                 }
             }
@@ -59,4 +61,19 @@
             e.printStackTrace();
             JOptionPane.showMessageDialog(Main.parent,tr("Parsing error in URL: \"{0}\"",url));
+        } catch(OsmTransferException e) {
+            e.printStackTrace();
+            if (e.getCause() != null) {
+                if (e.getCause() instanceof IOException ) {
+                    JOptionPane.showMessageDialog(Main.parent, tr("Could not read from URL: \"{0}\"",url),
+                            tr("Error"), JOptionPane.ERROR_MESSAGE);
+                } else if (e.getCause() instanceof SAXException) {
+                    JOptionPane.showMessageDialog(Main.parent,tr("Parsing error in URL: \"{0}\"",url),
+                            tr("Error"), JOptionPane.ERROR_MESSAGE);
+                }
+            } else {
+                JOptionPane.showMessageDialog(Main.parent,tr("Error while communicating with server.",url),
+                        tr("Error"), JOptionPane.ERROR_MESSAGE);
+            }
+
         }
     }
@@ -66,6 +83,7 @@
     }
     @Override protected void finish() {
-        if (sel != null)
+        if (sel != null) {
             Main.ds.setSelected(sel);
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/command/CoordinateConflictResolveCommand.java	(revision 1670)
@@ -33,18 +33,4 @@
     /** the merge decision */
     private final MergeDecisionType decision;
-
-
-    /**
-     * replies a (localized) display name for the type of an OSM primitive
-     * 
-     * @param primitive the primitive
-     * @return a localized display name
-     */
-    protected String getPrimitiveTypeAsString(OsmPrimitive primitive) {
-        if (primitive instanceof Node) return tr("node");
-        if (primitive instanceof Way) return tr("way");
-        if (primitive instanceof Relation) return tr("relation");
-        return "";
-    }
 
     /**
Index: trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java	(revision 1670)
@@ -0,0 +1,246 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.command;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.JLabel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Physically removes an {@see OsmPrimitive} from the dataset of the edit
+ * layer and disconnects any references from {@see Way}s or {@see Relation}s
+ * to this primitive.
+ * 
+ * This command is necessary if a local {@see OsmPrimitive} has been deleted on
+ * the server by another user and if the local user decides to delete his version
+ * too. If he only deleted it "logically" JOSM would try to delete it on the server
+ * which would result in an non resolvable conflict.
+ * 
+ */
+public class PurgePrimitivesCommand extends Command{
+
+    /**
+     * Represents a pair of {@see OsmPrimitive} where the parent referrs to
+     * the child, either because a {@see Way} includes a {@see Node} or
+     * because a {@see Relation} refers to any other {@see OsmPrimitive}
+     * via a relation member.
+     * 
+     */
+    static class OsmParentChildPair {
+        private OsmPrimitive parent;
+        private OsmPrimitive child;
+
+
+        public OsmParentChildPair(OsmPrimitive parent, OsmPrimitive child) {
+            this.parent = parent;
+            this.child = child;
+        }
+
+        public OsmPrimitive getParent() {
+            return parent;
+        }
+
+        public OsmPrimitive getChild() {
+            return child;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((child == null) ? 0 : child.hashCode());
+            result = prime * result + ((parent == null) ? 0 : parent.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;
+            OsmParentChildPair other = (OsmParentChildPair) obj;
+            if (child == null) {
+                if (other.child != null)
+                    return false;
+            } else if (child != other.child)
+                return false;
+            if (parent == null) {
+                if (other.parent != null)
+                    return false;
+            } else if (parent != other.parent)
+                return false;
+            return true;
+        }
+    }
+
+    /**
+     * creates a list of all {@see OsmParentChildPair}s for a given {@see OsmPrimitive}
+     * as child and given set of parents. We don't use {@see CollectBackReferencesVisitor}
+     * because it seems quite inefficient.
+     * 
+     * @param parents  the set of potential parents
+     * @param child the child
+     * @return the list of {@see OsmParentChildPair}
+     */
+    protected List<OsmParentChildPair> getParentChildPairs(List<OsmPrimitive> parents, OsmPrimitive child) {
+        ArrayList<OsmParentChildPair> pairs = new ArrayList<OsmParentChildPair>();
+        for (OsmPrimitive parent : parents) {
+            if (parent instanceof Way) {
+                Way w = (Way)parent;
+                for (OsmPrimitive node : w.nodes) {
+                    if (node == child) {
+                        OsmParentChildPair pair = new OsmParentChildPair(parent, node);
+                        if (! pairs.contains(pair)) {
+                            pairs.add(pair);
+                        }
+                    }
+                }
+            } else if (parent instanceof Relation) {
+                Relation r = (Relation)parent;
+                for (RelationMember member : r.members) {
+                    if (member.member == child) {
+                        OsmParentChildPair pair = new OsmParentChildPair(parent, member.member);
+                        if (! pairs.contains(pair)) {
+                            pairs.add(pair);
+                        }
+                    }
+                }
+            }
+        }
+        return pairs;
+    }
+
+    /** the primitive to purge */
+    private OsmPrimitive primitive;
+
+    /** the set of primitives to purge as consequence of purging
+     * {@see #primitive}, including {@see #primitive}
+     */
+    private ArrayList<OsmPrimitive> purgedPrimitives;
+
+    /** the set of {@see OsmParentChildPair}. We keep a reference
+     * to this set for the {@see #fillModifiedData(Collection, Collection, Collection)} operation
+     */
+    private ArrayList<OsmParentChildPair> pairs;
+
+    /**
+     * constructor
+     * @param node  the node to undelete
+     */
+    public PurgePrimitivesCommand(OsmPrimitive primitive) {
+        this.primitive = primitive;
+        purgedPrimitives = new ArrayList<OsmPrimitive>();
+        pairs = new ArrayList<OsmParentChildPair>();
+    }
+
+    @Override
+    public MutableTreeNode description() {
+        return new DefaultMutableTreeNode(
+                new JLabel(
+                        tr("Purging 1 primitive"),
+                        ImageProvider.get("data", "object"),
+                        JLabel.HORIZONTAL
+                )
+        );
+    }
+
+    /**
+     * Purges an {@see OsmPrimitive} <code>toPurge</code> from a {@see DataSet}.
+     * 
+     * @param toPurge the primitive to purge
+     * @param ds  the dataset to purge from
+     * @param hive the hive of {@see OsmPrimitive}s we remember other {@see OsmPrimitive}
+     * we have to purge because we purge <code>toPurge</code>.
+     * 
+     */
+    protected void purge(OsmPrimitive toPurge, DataSet ds, ArrayList<OsmPrimitive> hive) {
+        ArrayList<OsmPrimitive> parents = new ArrayList<OsmPrimitive>();
+        parents.addAll(Main.ds.ways);
+        parents.addAll(Main.ds.relations);
+        List<OsmParentChildPair> pairs = getParentChildPairs(parents, primitive);
+        hive.remove(toPurge);
+        for (OsmParentChildPair pair: pairs) {
+            if (pair.getParent() instanceof Way) {
+                Way w = (Way)pair.getParent();
+                System.out.println("removing reference from way " + w.id);
+                w.nodes.remove(primitive);
+                // if a way ends up with less than two node we
+                // remember it on the "hive"
+                //
+                if (w.nodes.size() < 2) {
+                    System.out.println(tr("Warning: Purging way {0} because number of nodes dropped below 2. Current is {1}",
+                            w.id,w.nodes.size()));
+                    if (!hive.contains(w)) {
+                        hive.add(w);
+                    }
+                }
+            } else if (pair.getParent() instanceof Relation) {
+                Relation r = (Relation)pair.getParent();
+                System.out.println("removing reference from relation " + r.id);
+                r.removeMembersFor(primitive);
+            }
+        }
+    }
+
+    @Override
+    public boolean executeCommand() {
+        ArrayList<OsmPrimitive> hive = new ArrayList<OsmPrimitive>();
+
+        // iteratively purge the primitive and all primitives
+        // which violate invariants after they loose a reference to
+        // the primitive (i.e. ways which end up with less than two
+        // nodes)
+        hive.add(primitive);
+        while(! hive.isEmpty()) {
+            OsmPrimitive toPurge = hive.get(0);
+            purge(toPurge, Main.ds, hive);
+            if (toPurge instanceof Node) {
+                Main.ds.nodes.remove(toPurge);
+            } else if (primitive instanceof Way) {
+                Main.ds.ways.remove(toPurge);
+            } else if (primitive instanceof Relation) {
+                Main.ds.relations.remove(toPurge);
+            }
+            purgedPrimitives.add(toPurge);
+        }
+        return super.executeCommand();
+    }
+
+    @Override
+    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
+            Collection<OsmPrimitive> added) {
+        for (OsmParentChildPair pair : pairs) {
+            modified.add(pair.getParent());
+        }
+        // we don't need pairs anymore
+        pairs = null;
+    }
+
+    @Override
+    public void undoCommand() {
+        for (OsmPrimitive purged : purgedPrimitives) {
+            Main.ds.addPrimitive(purged);
+        }
+
+        // will restore the former references to the purged nodes
+        //
+        super.undoCommand();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java	(revision 1670)
@@ -14,4 +14,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
@@ -52,17 +53,4 @@
 
     /**
-     * replies a (localized) display name for the type of an OSM primitive
-     * 
-     * @param primitive the primitive
-     * @return a localized display name
-     */
-    protected String getPrimitiveTypeAsString(OsmPrimitive primitive) {
-        if (primitive instanceof Node) return tr("node");
-        if (primitive instanceof Way) return tr("way");
-        if (primitive instanceof Relation) return tr("relation");
-        return "";
-    }
-
-    /**
      * constructor
      * 
@@ -82,5 +70,5 @@
         return new DefaultMutableTreeNode(
                 new JLabel(
-                        tr("Resolve {0} tag conflicts in {1} {2}",getNumDecidedConflicts(), getPrimitiveTypeAsString(my), my.id),
+                        tr("Resolve {0} tag conflicts in {1} {2}",getNumDecidedConflicts(), OsmPrimitiveType.from(my).getLocalizedDisplayNameSingular(), my.id),
                         ImageProvider.get("data", "object"),
                         JLabel.HORIZONTAL
Index: trunk/src/org/openstreetmap/josm/command/UndeletePrimitivesCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/UndeletePrimitivesCommand.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/command/UndeletePrimitivesCommand.java	(revision 1670)
@@ -0,0 +1,82 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.command;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.swing.JLabel;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Represents a command for undeleting a node which was deleted on the server.
+ * The command remembers the former node id and sets the node id to 0. This turns
+ * the node into a new node which can be uploaded to the server.
+ *
+ */
+public class UndeletePrimitivesCommand extends Command {
+
+    /** the node to undelete */
+    private ArrayList<OsmPrimitive> toUndelete;
+
+    /**
+     * constructor
+     * @param node  the node to undelete
+     */
+    public UndeletePrimitivesCommand(OsmPrimitive node) {
+        toUndelete = new ArrayList<OsmPrimitive>();
+        toUndelete.add(node);
+    }
+
+    /**
+     * constructor
+     * @param node  the node to undelete
+     */
+    public UndeletePrimitivesCommand(OsmPrimitive ... toUndelete) {
+        this.toUndelete = new ArrayList<OsmPrimitive>();
+        for (int i=0; i < toUndelete.length; i++) {
+            this.toUndelete.add(toUndelete[i]);
+        }
+    }
+
+    /**
+     * constructor
+     * @param node  the node to undelete
+     */
+    public UndeletePrimitivesCommand(Collection<OsmPrimitive> toUndelete) {
+        this.toUndelete = new ArrayList<OsmPrimitive>();
+        this.toUndelete.addAll(toUndelete);
+    }
+
+
+    @Override
+    public MutableTreeNode description() {
+        return new DefaultMutableTreeNode(
+                new JLabel(
+                        tr("Undelete {0} primitives", toUndelete.size()),
+                        ImageProvider.get("data", "object"),
+                        JLabel.HORIZONTAL
+                )
+        );
+    }
+
+    @Override
+    public boolean executeCommand() {
+        super.executeCommand();
+        for(OsmPrimitive primitive: toUndelete) {
+            primitive.id = 0;
+        }
+        return true;
+    }
+
+    @Override
+    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
+            Collection<OsmPrimitive> added) {
+        modified.addAll(toUndelete);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java	(revision 1670)
@@ -13,4 +13,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
@@ -37,23 +38,9 @@
     }
 
-    //FIXME copied from TagConflictResolveCommand -> refactor
-    /**
-     * replies a (localized) display name for the type of an OSM primitive
-     * 
-     * @param primitive the primitive
-     * @return a localized display name
-     */
-    protected String getPrimitiveTypeAsString(OsmPrimitive primitive) {
-        if (primitive instanceof Node) return tr("node");
-        if (primitive instanceof Way) return tr("way");
-        if (primitive instanceof Relation) return tr("relation");
-        return "";
-    }
-
     @Override
     public MutableTreeNode description() {
         return new DefaultMutableTreeNode(
                 new JLabel(
-                        tr("Resolve version conflicts for {0} {1}",getPrimitiveTypeAsString(my), my.id),
+                        tr("Resolve version conflicts for {0} {1}",OsmPrimitiveType.from(my).getLocalizedDisplayNameSingular(), my.id),
                         ImageProvider.get("data", "object"),
                         JLabel.HORIZONTAL
Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 1670)
@@ -10,6 +10,8 @@
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import org.openstreetmap.josm.data.SelectionChangedListener;
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 /**
@@ -78,6 +80,7 @@
         Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
         for (OsmPrimitive osm : allPrimitives())
-            if (!osm.deleted)
+            if (!osm.deleted) {
                 o.add(osm);
+            }
         return o;
     }
@@ -86,6 +89,7 @@
         Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
         for (OsmPrimitive osm : allPrimitives())
-            if (!osm.deleted && !osm.incomplete)
+            if (!osm.deleted && !osm.incomplete) {
                 o.add(osm);
+            }
         return o;
     }
@@ -94,6 +98,7 @@
         Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
         for (OsmPrimitive osm : allPrimitives())
-            if (!osm.deleted && !osm.incomplete && !(osm instanceof Relation))
+            if (!osm.deleted && !osm.incomplete && !(osm instanceof Relation)) {
                 o.add(osm);
+            }
         return o;
     }
@@ -150,6 +155,7 @@
         clearSelection(ways);
         clearSelection(relations);
-        for (OsmPrimitive osm : selection)
+        for (OsmPrimitive osm : selection) {
             osm.selected = true;
+        }
         fireSelectionChanged(selection);
     }
@@ -164,6 +170,7 @@
         clearSelection(relations);
         for (OsmPrimitive o : osm)
-            if (o != null)
+            if (o != null) {
                 o.selected = true;
+            }
         fireSelectionChanged(Arrays.asList(osm));
     }
@@ -176,6 +183,7 @@
         if (list == null)
             return;
-        for (OsmPrimitive osm : list)
+        for (OsmPrimitive osm : list) {
             osm.selected = false;
+        }
     }
 
@@ -189,6 +197,7 @@
             return sel;
         for (OsmPrimitive osm : list)
-            if (osm.selected && !osm.deleted)
+            if (osm.selected && !osm.deleted) {
                 sel.add(osm);
+            }
         return sel;
     }
@@ -200,18 +209,23 @@
      */
     public static void fireSelectionChanged(Collection<? extends OsmPrimitive> sel) {
-        for (SelectionChangedListener l : selListeners)
+        for (SelectionChangedListener l : selListeners) {
             l.selectionChanged(sel);
+        }
     }
 
     @Override public DataSet clone() {
         DataSet ds = new DataSet();
-        for (Node n : nodes)
+        for (Node n : nodes) {
             ds.nodes.add(new Node(n));
-        for (Way w : ways)
+        }
+        for (Way w : ways) {
             ds.ways.add(new Way(w));
-        for (Relation e : relations)
+        }
+        for (Relation e : relations) {
             ds.relations.add(new Relation(e));
-        for (DataSource source : dataSources)
+        }
+        for (DataSource source : dataSources) {
             ds.dataSources.add(new DataSource(source.bounds, source.origin));
+        }
         ds.version = version;
         return ds;
@@ -260,3 +274,40 @@
         return selArr;
     }
+
+    /**
+     * returns a  primitive with a given id from the data set. null, if no such primitive
+     * exists
+     * 
+     * @param id  the id, > 0 required
+     * @return the primitive
+     * @exception IllegalArgumentException thrown, if id <= 0
+     */
+    public OsmPrimitive getPrimitiveById(long id) {
+        if (id <= 0)
+            throw new IllegalArgumentException(tr("parameter {0} > 0 required. Got {1}.", "id", id));
+        for (OsmPrimitive primitive : nodes) {
+            if (primitive.id == id) return primitive;
+        }
+        for (OsmPrimitive primitive : ways) {
+            if (primitive.id == id) return primitive;
+        }
+        for (OsmPrimitive primitive : relations) {
+            if (primitive.id == id) return primitive;
+        }
+        return null;
+    }
+
+    public Set<Long> getPrimitiveIds() {
+        HashSet<Long> ret = new HashSet<Long>();
+        for (OsmPrimitive primitive : nodes) {
+            ret.add(primitive.id);
+        }
+        for (OsmPrimitive primitive : ways) {
+            ret.add(primitive.id);
+        }
+        for (OsmPrimitive primitive : relations) {
+            ret.add(primitive.id);
+        }
+        return ret;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitiveType.java	(revision 1670)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public enum OsmPrimitiveType {
+
+    NODE ("node", tr("node"), tr("nodes")),
+    WAY  ("way", tr("way"), tr("ways")),
+    RELATION ("relation", tr("relation"), tr("relations")),
+    CHANGESET ("changeset", tr("changeset"), tr("changesets"));
+
+    private String apiTypeName;
+    private String localizedDisplayNameSingular;
+    private String localizedDisplayNamePlural;
+
+    OsmPrimitiveType(String apiTypeName, String localizedDisplayNameSingular, String localizedDisplayNamePlural) {
+        this.apiTypeName = apiTypeName;
+        this.localizedDisplayNameSingular = localizedDisplayNameSingular;
+        this.localizedDisplayNamePlural = localizedDisplayNamePlural;
+    }
+
+    public String getAPIName() {
+        return apiTypeName;
+    }
+
+    public String getLocalizedDisplayNameSingular() {
+        return localizedDisplayNameSingular;
+    }
+
+    public String getLocalizedDisplayNamePlural() {
+        return localizedDisplayNamePlural;
+    }
+
+    public static OsmPrimitiveType fromApiTypeName(String typeName) {
+        for (OsmPrimitiveType type : OsmPrimitiveType.values()) {
+            if (type.getAPIName().equals(typeName)) return type;
+        }
+        throw new IllegalArgumentException(tr("parameter ''{0}'' is not a valid type name, got ''{1}''", "typeName", typeName));
+    }
+
+    public static OsmPrimitiveType from(OsmPrimitive obj) {
+        return from(obj.getClass());
+    }
+
+    public static OsmPrimitiveType from(Class cls) {
+        if (cls.equals(Node.class)) return NODE;
+        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/Relation.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/data/osm/Relation.java	(revision 1670)
@@ -78,4 +78,5 @@
     }
 
+    @Override
     public String getName() {
         String name;
@@ -84,22 +85,28 @@
         } else {
             name = get("type");
-            if (name == null)
+            if (name == null) {
                 name = tr("relation");
+            }
 
             name += " (";
-            if(names == null)
-              names = Main.pref.getCollection("relation.nameOrder", Arrays.asList(defnames));
+            if(names == null) {
+                names = Main.pref.getCollection("relation.nameOrder", Arrays.asList(defnames));
+            }
             String nameTag = null;
             for (String n : names) {
                 nameTag = get(n);
-                if (nameTag != null) break;
+                if (nameTag != null) {
+                    break;
+                }
             }
-            if (nameTag != null)
+            if (nameTag != null) {
                 name += "\"" + nameTag + "\", ";
+            }
 
             int mbno = members.size();
             name += trn("{0} member", "{0} members", mbno, mbno) + ")";
-            if(errors != null)
+            if(errors != null) {
                 name = "*"+name;
+            }
         }
         return name;
@@ -113,5 +120,5 @@
         return false;
     }
-    
+
     public RelationMember firstMember() {
         if (incomplete) return null;
@@ -122,3 +129,21 @@
         return (members.size() == 0) ? null : members.get(members.size() -1);
     }
+
+    /**
+     * removes all members with member.member == primitive
+     * 
+     * @param primitive the primitive to check for
+     */
+    public void removeMembersFor(OsmPrimitive primitive) {
+        if (primitive == null)
+            return;
+
+        ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
+        for (RelationMember member: members) {
+            if (member.member == primitive) {
+                todelete.add(member);
+            }
+        }
+        members.removeAll(todelete);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/history/History.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/History.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/History.java	(revision 1670)
@@ -0,0 +1,196 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.NoSuchElementException;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class History{
+    private static interface FilterPredicate {
+        boolean matches(HistoryOsmPrimitive primitive);
+    }
+
+    private static History filter(History history, FilterPredicate predicate) {
+        ArrayList<HistoryOsmPrimitive> out = new ArrayList<HistoryOsmPrimitive>();
+        for (HistoryOsmPrimitive primitive: history.versions) {
+            if (predicate.matches(primitive)) {
+                out.add(primitive);
+            }
+        }
+        return new History(history.id, out);
+    }
+
+    private ArrayList<HistoryOsmPrimitive> versions;
+    long id;
+
+    protected History(long id, List<HistoryOsmPrimitive> versions) {
+        this.id = id;
+        this.versions = new ArrayList<HistoryOsmPrimitive>();
+        this.versions.addAll(versions);
+    }
+
+    public History sortAscending() {
+        ArrayList<HistoryOsmPrimitive> copy = new ArrayList<HistoryOsmPrimitive>(versions);
+        Collections.sort(
+                copy,
+                new Comparator<HistoryOsmPrimitive>() {
+                    public int compare(HistoryOsmPrimitive o1, HistoryOsmPrimitive o2) {
+                        return o1.compareTo(o2);
+                    }
+                }
+        );
+        return new History(id, copy);
+    }
+
+    public History sortDescending() {
+        ArrayList<HistoryOsmPrimitive> copy = new ArrayList<HistoryOsmPrimitive>(versions);
+        Collections.sort(
+                copy,
+                new Comparator<HistoryOsmPrimitive>() {
+                    public int compare(HistoryOsmPrimitive o1, HistoryOsmPrimitive o2) {
+                        return o2.compareTo(o1);
+                    }
+                }
+        );
+        return new History(id, copy);
+    }
+
+    public History from(final Date fromDate) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getTimestamp().compareTo(fromDate) >= 0;
+                    }
+                }
+        );
+    }
+
+    public History until(final Date untilDate) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getTimestamp().compareTo(untilDate) <= 0;
+                    }
+                }
+        );
+    }
+
+    public History between(Date fromDate, Date untilDate) {
+        return this.from(fromDate).until(untilDate);
+    }
+
+    public History from(final long fromVersion) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getVersion() >= fromVersion;
+                    }
+                }
+        );
+    }
+
+    public History until(final long untilVersion) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getVersion() <= untilVersion;
+                    }
+                }
+        );
+    }
+
+    public History between(long fromVersion, long untilVersion) {
+        return this.from(fromVersion).until(untilVersion);
+    }
+
+    public History forUser(final String user) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getUser().equals(user);
+                    }
+                }
+        );
+    }
+
+    public History forUserId(final long uid) {
+        return filter(
+                this,
+                new FilterPredicate() {
+                    public boolean matches(HistoryOsmPrimitive primitive) {
+                        return primitive.getUid() == uid;
+                    }
+                }
+        );
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public boolean contains(long version){
+        for (HistoryOsmPrimitive primitive: versions) {
+            if (primitive.matches(id,version))
+                return true;
+        }
+        return false;
+    }
+
+    public HistoryOsmPrimitive getByVersion(long version) {
+        for (HistoryOsmPrimitive primitive: versions) {
+            if (primitive.matches(id,version))
+                return primitive;
+        }
+        throw new NoSuchElementException(tr("There's no primitive with version {0} in this history", version));
+    }
+
+    public HistoryOsmPrimitive getByDate(Date date) {
+        sortAscending();
+
+        if (versions.isEmpty())
+            throw new NoSuchElementException(tr("There's no version valid at date ''{0}'' in this history", date));
+        if (get(0).getTimestamp().compareTo(date)> 0)
+            throw new NoSuchElementException(tr("There's no version valid at date ''{0}'' in this history", date));
+        for (int i = 1; i < versions.size();i++) {
+            if (get(i-1).getTimestamp().compareTo(date) <= 0
+                    && get(i).getTimestamp().compareTo(date) >= 0)
+                return get(i);
+        }
+        return getLatest();
+    }
+
+    public HistoryOsmPrimitive get(int idx) {
+        if (idx < 0 || idx >= versions.size())
+            throw new IndexOutOfBoundsException(tr("parameter ''{0}'' in range 0..{1} expected, got {2}", "idx", versions.size()-1, idx));
+        return versions.get(idx);
+    }
+
+    public HistoryOsmPrimitive getEarliest() {
+        if (isEmpty())
+            throw new NoSuchElementException(tr("no earliest version found. History is empty."));
+        return sortAscending().versions.get(0);
+    }
+
+    public HistoryOsmPrimitive getLatest() {
+        if (isEmpty())
+            throw new NoSuchElementException(tr("no latest version found. History is empty."));
+        return sortDescending().versions.get(0);
+    }
+
+    public int getNumVersions() {
+        return versions.size();
+    }
+
+    public boolean isEmpty() {
+        return versions.isEmpty();
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryDataSet.java	(revision 1670)
@@ -0,0 +1,49 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+public class HistoryDataSet {
+
+    private HashMap<Long, ArrayList<HistoryOsmPrimitive>> data;
+
+    public HistoryDataSet() {
+        data = new HashMap<Long, ArrayList<HistoryOsmPrimitive>>();
+    }
+
+    public HistoryOsmPrimitive get(long id, long version) {
+        ArrayList<HistoryOsmPrimitive> versions = data.get(id);
+        if (versions == null)
+            throw new NoSuchElementException(tr("Didn't find an  primitive with id {0} in this dataset", id));
+
+        for (HistoryOsmPrimitive primitive: versions) {
+            if (primitive.matches(id, version))
+                return primitive;
+        }
+        throw new NoSuchElementException(tr("Didn't find an primitive with id {0} and version {1} in this dataset", id, version));
+    }
+
+    public void put(HistoryOsmPrimitive primitive) {
+        if (data.get(primitive.getId()) == null) {
+            data.put(primitive.getId(), new ArrayList<HistoryOsmPrimitive>());
+        }
+        data.get(primitive.getId()).add(primitive);
+    }
+
+    /**
+     * Replies the history for a given primitive with id <code>id</code>
+     * 
+     * @param id the id
+     * @return the history
+     */
+    public History getHistory(long id) {
+        ArrayList<HistoryOsmPrimitive> versions = data.get(id);
+        if (versions == null)
+            throw new NoSuchElementException(tr("Didn't find an historized primitive with id {0} in this dataset", id));
+        return new History(id, versions);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryNode.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryNode.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryNode.java	(revision 1670)
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import java.util.Date;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+
+
+/**
+ * Represents an immutable OSM node in the context of a historical view on
+ * OSM data.
+ *
+ */
+public class HistoryNode extends HistoryOsmPrimitive {
+    public HistoryNode(long id, long version, boolean visible, String user, long uid, long changesetId, Date timestamp) {
+        super(id, version, visible, user, uid, changesetId, timestamp);
+    }
+
+    @Override
+    public OsmPrimitiveType getType() {
+        return OsmPrimitiveType.NODE;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryOsmPrimitive.java	(revision 1670)
@@ -0,0 +1,145 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import java.util.Date;
+import java.util.HashMap;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Represents an immutable OSM primitive in the context of a historical view on
+ * OSM data.
+ *
+ */
+public abstract class HistoryOsmPrimitive implements Comparable<HistoryOsmPrimitive> {
+
+    private long id;
+    private boolean visible;
+    private String user;
+    private long uid;
+    private long changesetId;
+    private Date timestamp;
+    private long version;
+    private HashMap<String, String> tags;
+
+    protected void ensurePositiveLong(long value, String name) {
+        if (value <= 0)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' > 0 expected, got ''{1}''", name, value));
+    }
+
+    protected void ensureNotNull(Object obj, String name) {
+        if (obj == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", name));
+    }
+
+    /**
+     * constructor
+     * 
+     * @param id the id (>0 required)
+     * @param version the version (> 0 required)
+     * @param visible whether the primitive is still visible
+     * @param user  the user (! null required)
+     * @param uid the user id (> 0 required)
+     * @param changesetId the changeset id (> 0 required)
+     * @param timestamp the timestamp (! null required)
+     * 
+     * @throws IllegalArgumentException thrown if preconditions are violated
+     */
+    public HistoryOsmPrimitive(long id, long version, boolean visible, String user, long uid, long changesetId, Date timestamp) throws IllegalArgumentException {
+        ensurePositiveLong(id, "id");
+        ensurePositiveLong(version, "version");
+        ensurePositiveLong(uid, "uid");
+        ensurePositiveLong(changesetId, "changesetId");
+        ensureNotNull(user, "user");
+        ensureNotNull(timestamp, "timestamp");
+        this.id = id;
+        this.version = version;
+        this.visible = visible;
+        this.user = user;
+        this.uid = uid;
+        this.changesetId  = changesetId;
+        this.timestamp = timestamp;
+        tags = new HashMap<String, String>();
+    }
+
+    public long getId() {
+        return id;
+    }
+    public boolean isVisible() {
+        return visible;
+    }
+    public String getUser() {
+        return user;
+    }
+    public long getUid() {
+        return uid;
+    }
+    public long getChangesetId() {
+        return changesetId;
+    }
+    public Date getTimestamp() {
+        return timestamp;
+    }
+
+    public long getVersion() {
+        return version;
+    }
+
+    public boolean matches(long id, long version) {
+        return this.id == id && this.version == version;
+    }
+
+    public boolean matches(long id) {
+        return this.id == id;
+    }
+
+    public abstract OsmPrimitiveType getType();
+
+    public int compareTo(HistoryOsmPrimitive o) {
+        if (this.id != o.id)
+            throw new ClassCastException(tr("can't compare primitive with id ''{0}'' to primitive with id ''{1}''", o.id, this.id));
+        return new Long(this.version).compareTo(o.version);
+    }
+
+    public void put(String key, String value) {
+        tags.put(key, value);
+    }
+
+    public String get(String key) {
+        return tags.get(key);
+    }
+
+    public boolean hasTag(String key) {
+        return tags.get(key) != null;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (int) (id ^ (id >>> 32));
+        result = prime * result + (int) (version ^ (version >>> 32));
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        // equal semantics is valid for subclasses like {@see HistoryOsmNode} etc. too.
+        // So, don't enforce equality of class.
+        //
+        //        if (getClass() != obj.getClass())
+        //            return false;
+        HistoryOsmPrimitive other = (HistoryOsmPrimitive) obj;
+        if (id != other.id)
+            return false;
+        if (version != other.version)
+            return false;
+        return true;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryRelation.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryRelation.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryRelation.java	(revision 1670)
@@ -0,0 +1,113 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+
+/**
+ * Represents an immutable OSM relation in the context of a historical view on
+ * OSM data.
+ *
+ */
+public class HistoryRelation extends HistoryOsmPrimitive{
+
+    private ArrayList<RelationMember> members;
+
+    /**
+     * constructor
+     * 
+     * @param id the id (>0 required)
+     * @param version the version (> 0 required)
+     * @param visible whether the primitive is still visible
+     * @param user  the user (! null required)
+     * @param uid the user id (> 0 required)
+     * @param changesetId the changeset id (> 0 required)
+     * @param timestamp the timestamp (! null required)
+     * 
+     * @throws IllegalArgumentException thrown if preconditions are violated
+     */
+    public HistoryRelation(long id, long version, boolean visible, String user, long uid, long changesetId,
+            Date timestamp) throws IllegalArgumentException {
+        super(id, version, visible, user, uid, changesetId, timestamp);
+        members = new ArrayList<RelationMember>();
+    }
+    /**
+     * constructor
+     * 
+     * @param id the id (>0 required)
+     * @param version the version (> 0 required)
+     * @param visible whether the primitive is still visible
+     * @param user  the user (! null required)
+     * @param uid the user id (> 0 required)
+     * @param changesetId the changeset id (> 0 required)
+     * @param timestamp the timestamp (! null required)
+     * @param members list of members for this relation
+     * 
+     * @throws IllegalArgumentException thrown if preconditions are violated
+     */
+    public HistoryRelation(long id, long version, boolean visible, String user, long uid, long changesetId,
+            Date timestamp, ArrayList<RelationMember> members) {
+        this(id, version, visible, user, uid, changesetId, timestamp);
+        if (members != null) {
+            this.members.addAll(members);
+        }
+    }
+
+    /**
+     * replies an immutable list of members of this relation
+     * 
+     * @return an immutable list of members of this relation
+     */
+    public List<RelationMember> getMembers() {
+        return Collections.unmodifiableList(members);
+    }
+
+    /**
+     * replies the number of members
+     * 
+     * @return the number of members
+     * 
+     */
+    public int getNumMembers() {
+        return members.size();
+    }
+
+    /**
+     * replies the idx-th member
+     * @param idx the index
+     * @return the idx-th member
+     * @throws IndexOutOfBoundsException thrown, if idx is out of bounds
+     */
+    public RelationMember getRelationMember(int idx) throws IndexOutOfBoundsException  {
+        if (idx < 0 || idx >= members.size())
+            throw new IndexOutOfBoundsException(tr("parameter {0} not in range 0..{1}, got {2}", "idx", members.size(),idx));
+        return members.get(idx);
+    }
+
+    /**
+     * replies the type, i.e. {@see OsmPrimitiveType#RELATION}
+     * 
+     */
+    @Override
+    public OsmPrimitiveType getType() {
+        return OsmPrimitiveType.RELATION;
+    }
+
+    /**
+     * adds a member to the list of members
+     * 
+     * @param member the member (must not be null)
+     * @exception IllegalArgumentException thrown, if member is null
+     */
+    public void addMember(RelationMember member) throws IllegalArgumentException {
+        if (member == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "member"));
+        members.add(member);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/HistoryWay.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/HistoryWay.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/HistoryWay.java	(revision 1670)
@@ -0,0 +1,79 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+/**
+ * Represents an immutable OSM way in the context of a historical view on
+ * OSM data.
+ *
+ */
+public class HistoryWay extends HistoryOsmPrimitive {
+
+    private ArrayList<Long> nodeIds;
+
+    public HistoryWay(long id, long version, boolean visible, String user, long uid, long changesetId, Date timestamp) {
+        super(id, version, visible, user, uid, changesetId, timestamp);
+        nodeIds = new ArrayList<Long>();
+    }
+
+    public HistoryWay(long id, long version, boolean visible, String user, long uid, long changesetId, Date timestamp, ArrayList<Long> nodeIdList) {
+        this(id, version, visible, user, uid, changesetId, timestamp);
+        this.nodeIds.addAll(nodeIdList);
+    }
+
+    /**
+     * replies the number of nodes in this way
+     * @return the number of nodes
+     */
+    public int getNumNodes() {
+        return nodeIds.size();
+    }
+
+    /**
+     * replies the idx-th node id in the list of node ids of this way
+     * 
+     * @param idx the index
+     * @return the idx-th node id
+     * @exception IndexOutOfBoundsException thrown, if  idx <0 || idx >= {#see {@link #getNumNodes()}
+     */
+    public long getNodeId(int idx) throws IndexOutOfBoundsException {
+        if (idx < 0 || idx >= nodeIds.size())
+            throw new IndexOutOfBoundsException(tr("parameter {0} not in range 0..{1}, got {2}", "idx", nodeIds.size(),idx));
+        return nodeIds.get(idx);
+    }
+
+    /**
+     * replies an immutable list of the ways node ids
+     * 
+     * @return the ways node ids
+     */
+    public List<Long> getNodes() {
+        return Collections.unmodifiableList(nodeIds);
+    }
+
+    /**
+     * replies the ways type, i.e. {@see OsmPrimitiveType#WAY}
+     * 
+     * @return the ways type
+     */
+    @Override
+    public OsmPrimitiveType getType() {
+        return OsmPrimitiveType.WAY;
+    }
+
+    /**
+     * adds a node id to the list nodes of this way
+     * 
+     * @param ref the node id to add
+     */
+    public void addNode(long ref) {
+        nodeIds.add(ref);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/osm/history/RelationMember.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/history/RelationMember.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/data/osm/history/RelationMember.java	(revision 1670)
@@ -0,0 +1,62 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm.history;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+/**
+ * Represents a relation member in the the context of a historical view on
+ * OSM data.
+ *
+ */
+public class RelationMember {
+
+    private String role;
+    private OsmPrimitiveType primitiveType;
+    private long primitiveId;
+
+    /**
+     * 
+     * @param role  the role
+     * @param primitiveType  the type (must not be null)
+     * @param primitiveId the id (>0 required)
+     * 
+     * @exception IllegalArgumentException thrown, if primitiveType is null
+     * @exception IllegalArgumentException thrown, if primitiveId <= 0
+     */
+    public RelationMember(String role, OsmPrimitiveType primitiveType, long primitiveId) {
+        this.role = (role == null ? "" : role);
+        if (primitiveType == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "primitiveType"));
+        this.primitiveType = primitiveType;
+        if (primitiveId <=0)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' > 0 expected, got ''{1}''", "primitiveId", primitiveId));
+        this.primitiveId = primitiveId;
+    }
+
+    /**
+     * replies the member role
+     * @return the member role
+     */
+    public String getRole() {
+        return role;
+    }
+
+    /**
+     * replies the type of the referenced OSM primitive
+     * 
+     * @return the type of the referenced OSM primitive
+     */
+    public OsmPrimitiveType getPrimitiveType() {
+        return primitiveType;
+    }
+
+    /**
+     * replies the id of the referenced OSM primitive
+     * 
+     * @return the id of the referenced OSM primitive
+     */
+    public long getPrimitiveId() {
+        return primitiveId;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 1670)
@@ -62,4 +62,5 @@
 import org.openstreetmap.josm.actions.UnselectAllAction;
 import org.openstreetmap.josm.actions.UpdateDataAction;
+import org.openstreetmap.josm.actions.UpdateSelectionAction;
 import org.openstreetmap.josm.actions.UploadAction;
 import org.openstreetmap.josm.actions.ZoomInAction;
@@ -94,4 +95,5 @@
     public final DownloadAction download = new DownloadAction();
     public final JosmAction update = new UpdateDataAction();
+    public final JosmAction updateSelection = new UpdateSelectionAction();
     public final JosmAction upload = new UploadAction();
     public final JosmAction exit = new ExitAction();
@@ -194,4 +196,5 @@
         add(fileMenu, upload);
         add(fileMenu, update);
+        add(fileMenu, updateSelection);
         fileMenu.addSeparator();
         add(fileMenu, exit);
Index: trunk/src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java	(revision 1670)
@@ -17,4 +17,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.io.OsmTransferException;
 import org.xml.sax.SAXException;
 
@@ -109,4 +110,11 @@
                 x.printStackTrace();
                 errorMessage = x.getMessage();
+            } catch(OsmTransferException x) {
+                x.printStackTrace();
+                if (x.getCause() != null) {
+                    errorMessage = x.getCause().getMessage();
+                } else {
+                    errorMessage = x.getMessage();
+                }
             } finally {
                 closeDialog();
@@ -134,5 +142,5 @@
      * is called. finish() is called in any case.
      */
-    protected abstract void realRun() throws SAXException, IOException;
+    protected abstract void realRun() throws SAXException, IOException, OsmTransferException;
 
     /**
@@ -158,14 +166,16 @@
                         Main.pleaseWaitDlg.dispose();
                     }
-                    if (errorMessage != null && !silent)
+                    if (errorMessage != null && !silent) {
                         JOptionPane.showMessageDialog(Main.parent, errorMessage);
+                    }
                 }
             };
 
             // make sure, this is called in the dispatcher thread ASAP
-            if (EventQueue.isDispatchThread())
+            if (EventQueue.isDispatchThread()) {
                 runnable.run();
-            else
+            } else {
                 EventQueue.invokeAndWait(runnable);
+            }
 
         } catch (InterruptedException e) {
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java	(revision 1670)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
@@ -44,4 +45,5 @@
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
 import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -145,5 +147,7 @@
                             String key = propertyData.getValueAt(i, 0).toString();
                             String value = propertyData.getValueAt(i, 1).toString();
-                            if (key.length() > 0 && value.length() > 0) clone.put(key, value);
+                            if (key.length() > 0 && value.length() > 0) {
+                                clone.put(key, value);
+                            }
                         }
                         refreshTables();
@@ -175,6 +179,7 @@
                 {
                     sel = new ArrayList<OsmPrimitive>(cnt);
-                    for (int i : memberTable.getSelectedRows())
+                    for (int i : memberTable.getSelectedRows()) {
                         sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
+                    }
                 }
                 else
@@ -182,6 +187,7 @@
                     cnt = memberTable.getRowCount();
                     sel = new ArrayList<OsmPrimitive>(cnt);
-                    for (int i = 0; i < cnt; ++i)
+                    for (int i = 0; i < cnt; ++i) {
                         sel.add((OsmPrimitive)memberTable.getValueAt(i, 1));
+                    }
                 }
                 Main.ds.setSelected(sel);
@@ -239,5 +245,5 @@
 
         buttonPanel.add(createButton(marktr("Add Selected"),"addselected",
-        tr("Add all currently selected objects as members"), KeyEvent.VK_D, new ActionListener() {
+                tr("Add all currently selected objects as members"), KeyEvent.VK_D, new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 addSelected();
@@ -246,5 +252,5 @@
 
         buttonPanel.add(createButton(marktr("Remove Selected"),"removeselected",
-        tr("Remove all currently selected objects from relation"), KeyEvent.VK_S, new ActionListener() {
+                tr("Remove all currently selected objects from relation"), KeyEvent.VK_S, new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 deleteSelected();
@@ -259,5 +265,5 @@
 
         buttonPanel.add(createButton(marktr("Remove"),"remove",
-        tr("Remove the member in the current table row from this relation"), KeyEvent.VK_M, new ActionListener() {
+                tr("Remove the member in the current table row from this relation"), KeyEvent.VK_M, new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 int[] rows = memberTable.getSelectedRows();
@@ -273,5 +279,5 @@
 
         buttonPanel.add(createButton(marktr("Download Members"),"downloadincomplete",
-        tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_K, new ActionListener() {
+                tr("Download all incomplete ways and nodes in relation"), KeyEvent.VK_K, new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 downloadRelationMembers();
@@ -303,6 +309,7 @@
     protected void buttonAction(ActionEvent evt) {
         String a = evt.getActionCommand();
-        if(applyChangesText.equals(a))
+        if(applyChangesText.equals(a)) {
             applyChanges();
+        }
 
         setVisible(false);
@@ -348,6 +355,7 @@
                     break;
                 } else if (m.member instanceof Relation) {
-                    if (m.member == this.relation)
+                    if (m.member == this.relation) {
                         break;
+                    }
                     m = ((Relation)m.member).lastMember();
                     depth++;
@@ -366,6 +374,7 @@
                             break;
                         } else if (m.member instanceof Relation) {
-                            if (m.member == this.relation)
+                            if (m.member == this.relation) {
                                 break;
+                            }
                             m = ((Relation)(m.member)).firstMember();
                             depth++;
@@ -374,6 +383,7 @@
                         }
                     }
-                    if (way2 != null)
+                    if (way2 != null) {
                         break;
+                    }
                 }
             }
@@ -404,5 +414,5 @@
                 }
 
-                // end of section to determine linkedness. 
+                // end of section to determine linkedness.
 
                 memberData.addRow(new Object[]{em.role, em.member, linked ? tr("yes") : tr("no")});
@@ -416,5 +426,5 @@
     private SideButton createButton(String name, String iconName, String tooltip, int mnemonic, ActionListener actionListener) {
         return
-            new SideButton(name, iconName, "relationEditor",
+        new SideButton(name, iconName, "relationEditor",
                 tooltip,
                 Shortcut.registerShortcut("relationeditor:"+iconName,
@@ -422,6 +432,6 @@
                         mnemonic,
                         Shortcut.GROUP_MNEMONIC),
-                actionListener
-            );
+                        actionListener
+        );
     }
 
@@ -483,5 +493,7 @@
         for (RelationMember rm : clone.members) {
             if (rm != null) {
-                while (m[i] != null) i++;
+                while (m[i] != null) {
+                    i++;
+                }
                 m[i++] = rm;
             }
@@ -509,5 +521,5 @@
         }
         if (download) {
-            OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmServerObjectReader.TYPE_REL, true);
+            OsmServerObjectReader reader = new OsmServerObjectReader(clone.id, OsmPrimitiveType.RELATION, true);
             try {
                 DataSet dataSet = reader.parseOsm();
@@ -515,11 +527,13 @@
                     final MergeVisitor visitor = new MergeVisitor(Main.main
                             .editLayer().data, dataSet);
-                    for (final OsmPrimitive osm : dataSet.allPrimitives())
+                    for (final OsmPrimitive osm : dataSet.allPrimitives()) {
                         osm.visit(visitor);
+                    }
                     visitor.fixReferences();
 
                     // copy the merged layer's data source info
-                    for (DataSource src : dataSet.dataSources)
+                    for (DataSource src : dataSet.dataSources) {
                         Main.main.editLayer().data.dataSources.add(src);
+                    }
                     Main.main.editLayer().fireDataChange();
 
@@ -530,17 +544,23 @@
                     JOptionPane.showMessageDialog(Main.parent,
                             tr("There were conflicts during import."));
-                    if (!dlg.isVisible())
+                    if (!dlg.isVisible()) {
                         dlg.action
-                                .actionPerformed(new ActionEvent(this, 0, ""));
-                }
-
-            } catch (SAXException e) {
+                        .actionPerformed(new ActionEvent(this, 0, ""));
+                    }
+                }
+            } catch(OsmTransferException e) {
                 e.printStackTrace();
-                JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getMessage(),
-                tr("Error"), JOptionPane.ERROR_MESSAGE);
-            } catch (IOException e) {
-                e.printStackTrace();
-                JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getMessage(),
-                tr("Error"), JOptionPane.ERROR_MESSAGE);
+                if (e.getCause() != null) {
+                    if (e.getCause() instanceof SAXException) {
+                        JOptionPane.showMessageDialog(this,tr("Error parsing server response.")+": "+e.getCause().getMessage(),
+                                tr("Error"), JOptionPane.ERROR_MESSAGE);
+                    } else if(e.getCause() instanceof IOException) {
+                        JOptionPane.showMessageDialog(this,tr("Cannot connect to server.")+": "+e.getCause().getMessage(),
+                                tr("Error"), JOptionPane.ERROR_MESSAGE);
+                    }
+                } else {
+                    JOptionPane.showMessageDialog(this,tr("Error when communicating with server.")+": "+e.getMessage(),
+                            tr("Error"), JOptionPane.ERROR_MESSAGE);
+                }
             }
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 1670)
@@ -51,4 +51,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
@@ -77,10 +78,15 @@
         public final int[] normal = new int[3];
         public final int[] deleted = new int[3];
-        public final String[] names = {"node", "way", "relation"};
+        public final String[] names = {
+                OsmPrimitiveType.NODE.getAPIName(),
+                OsmPrimitiveType.WAY.getAPIName(),
+                OsmPrimitiveType.RELATION.getAPIName()
+        };
 
         private void inc(final OsmPrimitive osm, final int i) {
             normal[i]++;
-            if (osm.deleted)
+            if (osm.deleted) {
                 deleted[i]++;
+            }
         }
 
@@ -201,8 +207,9 @@
 
         SimplePaintVisitor painter;
-        if (Main.pref.getBoolean("draw.wireframe"))
+        if (Main.pref.getBoolean("draw.wireframe")) {
             painter = new SimplePaintVisitor();
-        else
+        } else {
             painter = new MapPaintVisitor();
+        }
         painter.setGraphics(g);
         painter.setNavigatableComponent(mv);
@@ -216,40 +223,48 @@
         tool += undeletedSize(data.nodes)+" "+trn("node", "nodes", undeletedSize(data.nodes))+", ";
         tool += undeletedSize(data.ways)+" "+trn("way", "ways", undeletedSize(data.ways));
-        if (data.version != null) tool += ", " + tr("version {0}", data.version);
+        if (data.version != null) {
+            tool += ", " + tr("version {0}", data.version);
+        }
         File f = getAssociatedFile();
-        if (f != null)
+        if (f != null) {
             tool = "<html>"+tool+"<br>"+f.getPath()+"</html>";
+        }
         return tool;
     }
 
     @Override public void mergeFrom(final Layer from) {
-        final MergeVisitor visitor = new MergeVisitor(data,((OsmDataLayer)from).data);
-        for (final OsmPrimitive osm : ((OsmDataLayer)from).data.allPrimitives()) {
-//            i++;
-//            if(i%100 == 0) {
-//                double perc = (((double)i) / ((double)max) * 100.0);
-//                System.out.format(" " + (int)perc + "%%");
-//            }
+        mergeFrom(((OsmDataLayer)from).data);
+    }
+
+    /**
+     * merges the primitives in dataset <code>from</code> into the dataset of
+     * this layer
+     * 
+     * @param from  the source data set
+     */
+    public void mergeFrom(final DataSet from) {
+        final MergeVisitor visitor = new MergeVisitor(data,from);
+        for (final OsmPrimitive osm : from.allPrimitives()) {
             osm.visit(visitor);
         }
         visitor.fixReferences();
-//        System.out.println("");
 
         Area a = data.getDataSourceArea();
-        
-        // copy the merged layer's data source info; 
+
+        // copy the merged layer's data source info;
         // only add source rectangles if they are not contained in the
         // layer already.
-        for (DataSource src : ((OsmDataLayer)from).data.dataSources) {
-            if (a == null || !a.contains(src.bounds.asRect()))
+        for (DataSource src : from.dataSources) {
+            if (a == null || !a.contains(src.bounds.asRect())) {
                 data.dataSources.add(src);
-        }
-        
+            }
+        }
+
         // copy the merged layer's API version, downgrade if required
         if (data.version == null) {
-            data.version = ((OsmDataLayer)from).data.version;
+            data.version = from.version;
         } else {
-            if ("0.5".equals(data.version) ^ "0.5".equals(((OsmDataLayer)from).data.version)) {
-                System.err.println("Warning: mixing 0.6 and 0.5 data results in version 0.5");
+            if ("0.5".equals(data.version) ^ "0.5".equals(from.version)) {
+                System.err.println(tr("Warning: mixing 0.6 and 0.5 data results in version 0.5"));
                 data.version = "0.5";
             }
@@ -263,7 +278,8 @@
         final ConflictDialog dlg = Main.map.conflictDialog;
         dlg.add(visitor.conflicts);
-        JOptionPane.showMessageDialog(Main.parent,tr("There were conflicts during import."));
-        if (!dlg.isVisible())
+        JOptionPane.showMessageDialog(Main.parent,tr("There were {0} conflicts during import.", visitor.conflicts.size()));
+        if (!dlg.isVisible()) {
             dlg.action.actionPerformed(new ActionEvent(this, 0, ""));
+        }
     }
 
@@ -274,6 +290,7 @@
     @Override public void visitBoundingBox(final BoundingXYVisitor v) {
         for (final Node n : data.nodes)
-            if (!n.deleted && !n.incomplete)
+            if (!n.deleted && !n.incomplete) {
                 v.visit(n);
+            }
     }
 
@@ -299,10 +316,13 @@
         if (processed != null) {
             final Set<OsmPrimitive> processedSet = new HashSet<OsmPrimitive>(processed);
-            for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();)
+            for (final Iterator<Node> it = data.nodes.iterator(); it.hasNext();) {
                 cleanIterator(it, processedSet);
-            for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();)
+            }
+            for (final Iterator<Way> it = data.ways.iterator(); it.hasNext();) {
                 cleanIterator(it, processedSet);
-            for (final Iterator<Relation> it = data.relations.iterator(); it.hasNext();)
+            }
+            for (final Iterator<Relation> it = data.relations.iterator(); it.hasNext();) {
                 cleanIterator(it, processedSet);
+            }
         }
 
@@ -330,6 +350,7 @@
             return;
         osm.modified = false;
-        if (osm.deleted)
+        if (osm.deleted) {
             it.remove();
+        }
     }
 
@@ -342,6 +363,7 @@
             return;
         this.modified = modified;
-        for (final ModifiedChangedListener l : listenerModified)
+        for (final ModifiedChangedListener l : listenerModified) {
             l.modifiedChanged(modified, this);
+        }
     }
 
@@ -352,6 +374,7 @@
         int size = 0;
         for (final OsmPrimitive osm : list)
-            if (!osm.deleted)
+            if (!osm.deleted) {
                 size++;
+            }
         return size;
     }
@@ -359,12 +382,14 @@
     @Override public Object getInfoComponent() {
         final DataCountVisitor counter = new DataCountVisitor();
-        for (final OsmPrimitive osm : data.allPrimitives())
+        for (final OsmPrimitive osm : data.allPrimitives()) {
             osm.visit(counter);
+        }
         final JPanel p = new JPanel(new GridBagLayout());
         p.add(new JLabel(tr("{0} consists of:", name)), GBC.eol());
         for (int i = 0; i < counter.normal.length; ++i) {
             String s = counter.normal[i]+" "+trn(counter.names[i],counter.names[i]+"s",counter.normal[i]);
-            if (counter.deleted[i] > 0)
+            if (counter.deleted[i] > 0) {
                 s += tr(" ({0} deleted.)",counter.deleted[i]);
+            }
             p.add(new JLabel(s, ImageProvider.get("data", counter.names[i]), JLabel.HORIZONTAL), GBC.eop().insets(15,0,0,0));
         }
@@ -375,13 +400,12 @@
 
     @Override public Component[] getMenuEntries() {
-        if (Main.applet) {
+        if (Main.applet)
             return new Component[]{
-                    new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
-                    new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
-                    new JSeparator(),
-                    new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
-                    new JSeparator(),
-                    new JMenuItem(new LayerListPopup.InfoAction(this))};
-        }
+                new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+                new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+                new JSeparator(),
+                new JMenuItem(new RenameLayerAction(getAssociatedFile(), this)),
+                new JSeparator(),
+                new JMenuItem(new LayerListPopup.InfoAction(this))};
         return new Component[]{
                 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
@@ -409,10 +433,13 @@
         HashSet<Node> doneNodes = new HashSet<Node>();
         for (Way w : data.ways) {
-            if (w.incomplete || w.deleted) continue;
+            if (w.incomplete || w.deleted) {
+                continue;
+            }
             GpxTrack trk = new GpxTrack();
             gpxData.tracks.add(trk);
 
-            if (w.get("name") != null)
+            if (w.get("name") != null) {
                 trk.attr.put("name", w.get("name"));
+            }
 
             ArrayList<WayPoint> trkseg = null;
@@ -429,5 +456,5 @@
                     doneNodes.add(n);
                 }
-                WayPoint wpt = new WayPoint(n.getCoor());                
+                WayPoint wpt = new WayPoint(n.getCoor());
                 if (!n.isTimestampEmpty())
                 {
@@ -442,5 +469,7 @@
         // records them?
         for (Node n : data.nodes) {
-            if (n.incomplete || n.deleted || doneNodes.contains(n)) continue;
+            if (n.incomplete || n.deleted || doneNodes.contains(n)) {
+                continue;
+            }
             WayPoint wpt = new WayPoint(n.getCoor());
             if (!n.isTimestampEmpty()) {
Index: trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/BoundingBoxDownloader.java	(revision 1670)
@@ -50,6 +50,7 @@
                 Main.pleaseWaitDlg.currentAction.setText(tr("Downloading points {0} to {1}...", i * 5000, ((i + 1) * 5000)));
                 InputStream in = getInputStream(url+i, Main.pleaseWaitDlg);
-                if (in == null)
+                if (in == null) {
                     break;
+                }
                 GpxData currentGpx = new GpxReader(in, null).data;
                 if (result == null) {
@@ -89,5 +90,6 @@
      * @return A data set containing all data retrieved from that url
      */
-    public DataSet parseOsm() throws SAXException, IOException {
+    @Override
+    public DataSet parseOsm() throws OsmTransferException {
         try {
             Main.pleaseWaitDlg.progress.setValue(0);
@@ -106,13 +108,13 @@
             if (cancel)
                 return null;
-            throw e;
+            throw new OsmTransferException(e);
         } catch (SAXException e) {
+            throw new OsmTransferException(e);
+        } catch(OsmTransferException e) {
             throw e;
         } catch (Exception e) {
             if (cancel)
                 return null;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e);
+            throw new OsmTransferException(e);
         }
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 1670)
@@ -29,8 +29,6 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.Changeset;
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
 import org.xml.sax.Attributes;
@@ -67,4 +65,5 @@
         if (api == null) {
             api = new OsmApi(serverUrl);
+            instances.put(serverUrl,api);
         }
         return api;
@@ -80,5 +79,5 @@
         String serverUrl = Main.pref.get("osm-server.url");
         if (serverUrl == null)
-            throw new IllegalStateException(tr("preference {0} missing. Can't initialize OsmApi", "osm-server.url"));
+            throw new IllegalStateException(tr("preference ''{0}'' missing. Can't initialize OsmApi", "osm-server.url"));
         return getOsmApi(serverUrl);
     }
@@ -96,10 +95,4 @@
      */
     private String version = null;
-
-    /**
-     * Maximum downloadable area from server (degrees squared), from capabilities response
-     * FIXME: make download dialog use this, instead of hard-coded default.
-     */
-    private String maxArea = null;
 
     /** the api capabilities */
@@ -143,29 +136,4 @@
 
     /**
-     * creates an instance of the OSM API. Initializes the server URL with the
-     * value of the preference <code>osm-server.url</code>
-     * 
-     * @exception IllegalStateException thrown, if the preference <code>osm-server.url</code> is not set
-     */
-    protected OsmApi() {
-        this.serverUrl = Main.pref.get("osm-server.url");
-        if (serverUrl == null)
-            throw new IllegalStateException(tr("preference {0} missing. Can't initialize OsmApi", "osm-server.url"));
-    }
-
-    /**
-     * Helper that returns the lower-case type name of an OsmPrimitive
-     * @param o the primitive
-     * @return "node", "way", "relation", or "changeset"
-     */
-    public static String which(OsmPrimitive o) {
-        if (o instanceof Node) return "node";
-        if (o instanceof Way) return "way";
-        if (o instanceof Relation) return "relation";
-        if (o instanceof Changeset) return "changeset";
-        return "";
-    }
-
-    /**
      * Returns the OSM protocol version we use to talk to the server.
      * @return protocol version, or null if not yet negotiated.
@@ -185,9 +153,6 @@
     /**
      * Initializes this component by negotiating a protocol version with the server.
-     * 
-     * @exception UnknownHostException thrown, if the API host is unknown
-     * @exception SocketTimeoutException thrown, if the connection to the API host  times out
-     * @exception ConnectException throw, if the connection to the API host fails
-     * @exception Exception any other exception
+     *
+     * @exception OsmApiInitializationException thrown, if an exception occurs
      */
     public void initialize() throws OsmApiInitializationException {
@@ -238,34 +203,4 @@
 
     /**
-     * Helper that makes an int from the first whitespace separated token in a string.
-     * @param s the string
-     * @return the integer represenation of the first token in the string
-     * @throws OsmTransferException if the string is empty or does not represent a number
-     */
-    public static int parseInt(String s) throws OsmTransferException {
-        StringTokenizer t = new StringTokenizer(s);
-        try {
-            return Integer.parseInt(t.nextToken());
-        } catch (Exception x) {
-            throw new OsmTransferException(tr("Cannot read numeric value from response"));
-        }
-    }
-
-    /**
-     * Helper that makes a long from the first whitespace separated token in a string.
-     * @param s the string
-     * @return the long represenation of the first token in the string
-     * @throws OsmTransferException if the string is empty or does not represent a number
-     */
-    public static long parseLong(String s) throws OsmTransferException {
-        StringTokenizer t = new StringTokenizer(s);
-        try {
-            return Long.parseLong(t.nextToken());
-        } catch (Exception x) {
-            throw new OsmTransferException(tr("Cannot read numeric value from response"));
-        }
-    }
-
-    /**
      * Returns the base URL for API requests, including the negotiated version number.
      * @return base URL string
@@ -293,6 +228,12 @@
     public void createPrimitive(OsmPrimitive osm) throws OsmTransferException {
         initialize();
-        osm.id = parseLong(sendRequest("PUT", which(osm)+"/create", toXml(osm, true)));
-        osm.version = 1;
+        String ret = "";
+        try {
+            ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/create", toXml(osm, true));
+            osm.id = Long.parseLong(ret.trim());
+            osm.version = 1;
+        } catch(NumberFormatException e){
+            throw new OsmTransferException(tr("unexpected format of id replied by the server, got ''{0}''", ret));
+        }
     }
 
@@ -309,8 +250,14 @@
         if (version.equals("0.5")) {
             // legacy mode does not return the new object version.
-            sendRequest("PUT", which(osm)+"/" + osm.id, toXml(osm, true));
+            sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.id, toXml(osm, true));
         } else {
+            String ret = null;
             // normal mode (0.6 and up) returns new object version.
-            osm.version = parseInt(sendRequest("PUT", which(osm)+"/" + osm.id, toXml(osm, true)));
+            try {
+                ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.id, toXml(osm, true));
+                osm.version = Integer.parseInt(ret.trim());
+            } catch(NumberFormatException e) {
+                throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.id, ret));
+            }
         }
     }
@@ -324,5 +271,5 @@
         initialize();
         // legacy mode does not require payload. normal mode (0.6 and up) requires payload for version matching.
-        sendRequest("DELETE", which(osm)+"/" + osm.id, version.equals("0.5") ? null : toXml(osm, false));
+        sendRequest("DELETE", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.id, version.equals("0.5") ? null : toXml(osm, false));
     }
 
@@ -334,5 +281,5 @@
     public void createChangeset(String comment) throws OsmTransferException {
         changeset = new Changeset();
-        Main.pleaseWaitDlg.currentAction.setText(tr("Opening changeset..."));
+        notifyStatusMessage(tr("Opening changeset..."));
         Properties sysProp = System.getProperties();
         Object ua = sysProp.get("http.agent");
@@ -349,5 +296,5 @@
     public void stopChangeset() throws OsmTransferException {
         initialize();
-        Main.pleaseWaitDlg.currentAction.setText(tr("Closing changeset..."));
+        notifyStatusMessage(tr("Closing changeset..."));
         sendRequest("PUT", "changeset" + "/" + changeset.id + "/close", null);
         changeset = null;
@@ -372,12 +319,10 @@
         CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset, OsmApi.this);
 
+        notifyStatusMessage(tr("Preparing..."));
         for (OsmPrimitive osm : list) {
-            int progress = Main.pleaseWaitDlg.progress.getValue();
-            Main.pleaseWaitDlg.currentAction.setText(tr("Preparing..."));
             osm.visit(duv);
-            Main.pleaseWaitDlg.progress.setValue(progress+1);
-        }
-
-        Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
+            notifyRelativeProgress(1);
+        }
+        notifyStatusMessage(tr("Uploading..."));
 
         String diff = duv.getDocument();
@@ -517,3 +462,24 @@
         }
     }
+
+    /**
+     * notifies any listeners about the current state of this API. Currently just
+     * displays the message in the global progress dialog, see {@see Main#pleaseWaitDlg}
+     * 
+     * @param message a status message.
+     */
+    protected void notifyStatusMessage(String message) {
+        Main.pleaseWaitDlg.currentAction.setText(message);
+    }
+
+    /**
+     * notifies any listeners about the current about a relative progress. Currently just
+     * increments the progress monitor in the in the global progress dialog, see {@see Main#pleaseWaitDlg}
+     * 
+     * @param int the delta
+     */
+    protected void notifyRelativeProgress(int delta) {
+        int current= Main.pleaseWaitDlg.progress.getValue();
+        Main.pleaseWaitDlg.progress.setValue(current + delta);
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmConnection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 1670)
@@ -22,5 +22,5 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.ExtendedDialog; 
+import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.tools.Base64;
 import org.openstreetmap.josm.tools.GBC;
@@ -33,11 +33,4 @@
  */
 public class OsmConnection {
-
-    public static class OsmParseException extends Exception {
-        public OsmParseException() {super();}
-        public OsmParseException(String message, Throwable cause) {super(message, cause);}
-        public OsmParseException(String message) {super(message);}
-        public OsmParseException(Throwable cause) {super(cause);}
-    }
 
     protected boolean cancel = false;
@@ -77,6 +70,7 @@
             if (passwordtried || username.equals("") || password.equals("")) {
                 JPanel p = new JPanel(new GridBagLayout());
-                if (!username.equals("") && !password.equals(""))
+                if (!username.equals("") && !password.equals("")) {
                     p.add(new JLabel(tr("Incorrect password or username.")), GBC.eop());
+                }
                 p.add(new JLabel(tr("Username")), GBC.std().insets(0,0,10,0));
                 JTextField usernameField = new JTextField(username, 20);
@@ -92,10 +86,10 @@
                 p.add(savePassword, GBC.eop());
 
-                int choice = new ExtendedDialog(Main.parent, 
-                        tr("Enter Password"), 
+                int choice = new ExtendedDialog(Main.parent,
+                        tr("Enter Password"),
                         p,
-                        new String[] {tr("Login"), tr("Cancel")}, 
-                        new String[] {"ok.png", "cancel.png"}).getValue();  
-                
+                        new String[] {tr("Login"), tr("Cancel")},
+                        new String[] {"ok.png", "cancel.png"}).getValue();
+
                 if (choice != 1) {
                     authCancelled = true;
Index: trunk/src/org/openstreetmap/josm/io/OsmHistoryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmHistoryReader.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/io/OsmHistoryReader.java	(revision 1670)
@@ -0,0 +1,223 @@
+// License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Date;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
+import org.openstreetmap.josm.data.osm.history.HistoryNode;
+import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
+import org.openstreetmap.josm.data.osm.history.HistoryRelation;
+import org.openstreetmap.josm.data.osm.history.HistoryWay;
+import org.openstreetmap.josm.gui.PleaseWaitDialog;
+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 OSM history data.
+ *
+ * It is slightly different from {@see OsmReader} because we don't build an internal graph of
+ * {@see OsmPrimitive}s. We use objects derived from {@see HistoryOsmPrimitive} instead and we
+ * keep the data in a dedicated {@see HistoryDataSet}.
+ * 
+ */
+public class OsmHistoryReader {
+
+    private InputStream in;
+    private HistoryDataSet data;
+
+    private class Parser extends DefaultHandler {
+
+        /** the current primitive to be read */
+        private HistoryOsmPrimitive current;
+        private Locator locator;
+
+        @Override
+        public void setDocumentLocator(Locator locator) {
+            this.locator = locator;
+        }
+
+        protected String getCurrentPosition() {
+            if (locator == null)
+                return "";
+            return "(" + locator.getLineNumber() + "," + locator.getColumnNumber() + ")";
+        }
+
+        protected void throwException(String message) throws SAXException {
+            throw new SAXException(
+                    getCurrentPosition()
+                    +   message
+            );
+        }
+
+        protected long getMandatoryAttributeLong(Attributes attr, String name) throws SAXException{
+            String v = attr.getValue(name);
+            if (v == null) {
+                throwException(tr("mandatory attribute ''{0}'' missing", name));
+            }
+            Long l = 0l;
+            try {
+                l = Long.parseLong(v);
+            } catch(NumberFormatException e) {
+                throwException(tr("illegal value for mandatory attribute ''{0}'' of type long, got ''{1}''", name, v));
+            }
+            if (l < 0) {
+                throwException(tr("illegal value for mandatory attribute ''{0}'' of type long (>=0), got ''{1}''", name, v));
+            }
+            return l;
+        }
+
+        protected int getMandatoryAttributeInt(Attributes attr, String name) throws SAXException{
+            String v = attr.getValue(name);
+            if (v == null) {
+                throwException(tr("mandatory attribute ''{0}'' missing", name));
+            }
+            Integer i = 0;
+            try {
+                i = Integer.parseInt(v);
+            } catch(NumberFormatException e) {
+                throwException(tr("illegal value for mandatory attribute ''{0}'' of type int, got ''{1}''", name, v));
+            }
+            if (i < 0) {
+                throwException(tr("illegal value for mandatory attribute ''{0}'' of type int (>=0), got ''{1}''", name, v));
+            }
+            return i;
+        }
+
+        protected String getMandatoryAttributeString(Attributes attr, String name) throws SAXException{
+            String v = attr.getValue(name);
+            if (v == null) {
+                throwException(tr("mandatory attribute ''{0}'' missing", name));
+            }
+            return v;
+        }
+
+        protected boolean getMandatoryAttributeBoolean(Attributes attr, String name) throws SAXException{
+            String v = attr.getValue(name);
+            if (v == null) {
+                throwException(tr("mandatory attribute ''{0}'' missing", name));
+            }
+            if (v.equals("true")) return true;
+            if (v.equals("false")) return false;
+            throwException(tr("illegal value for mandatory attribute ''{0}'' of type boolean, got ''{1}''", name, v));
+            // not reached
+            return false;
+        }
+
+        protected  HistoryOsmPrimitive createPrimitive(Attributes atts, OsmPrimitiveType type) throws SAXException {
+            long id = getMandatoryAttributeLong(atts,"id");
+            long version = getMandatoryAttributeLong(atts,"version");
+            long changesetId = getMandatoryAttributeLong(atts,"changeset");
+            boolean visible= getMandatoryAttributeBoolean(atts, "visible");
+            long uid = getMandatoryAttributeLong(atts, "uid");
+            String user = getMandatoryAttributeString(atts, "user");
+            String v = getMandatoryAttributeString(atts, "timestamp");
+            Date timestamp = DateUtils.fromString(v);
+            HistoryOsmPrimitive primitive = null;
+            if (type.equals(OsmPrimitiveType.NODE)) {
+                primitive = new HistoryNode(
+                        id,version,visible,user,uid,changesetId,timestamp
+                );
+            } else if (type.equals(OsmPrimitiveType.WAY)) {
+                primitive = new HistoryWay(
+                        id,version,visible,user,uid,changesetId,timestamp
+                );
+            }if (type.equals(OsmPrimitiveType.RELATION)) {
+                primitive = new HistoryRelation(
+                        id,version,visible,user,uid,changesetId,timestamp
+                );
+            }
+            return primitive;
+        }
+
+        protected void startNode(Attributes atts) throws SAXException {
+            current= createPrimitive(atts, OsmPrimitiveType.NODE);
+        }
+
+        protected void startWay(Attributes atts) throws SAXException {
+            current= createPrimitive(atts, OsmPrimitiveType.WAY);
+        }
+        protected void startRelation(Attributes atts) throws SAXException {
+            current= createPrimitive(atts, OsmPrimitiveType.RELATION);
+        }
+
+        protected void handleTag(Attributes atts) throws SAXException {
+            String key= getMandatoryAttributeString(atts, "k");
+            String value= getMandatoryAttributeString(atts, "v");
+            current.put(key,value);
+        }
+
+        protected void handleNodeReference(Attributes atts) throws SAXException {
+            long ref = getMandatoryAttributeLong(atts, "ref");
+            ((HistoryWay)current).addNode(ref);
+        }
+
+        protected void handleMember(Attributes atts) throws SAXException {
+            long ref = getMandatoryAttributeLong(atts, "ref");
+            String v = getMandatoryAttributeString(atts, "type");
+            OsmPrimitiveType type = null;
+            try {
+                type = OsmPrimitiveType.fromApiTypeName(v);
+            } catch(IllegalArgumentException e) {
+                throwException(tr("illegal value for mandatory attribute ''{0}'' of type OsmPrimitiveType, got ''{1}''", "type", v));
+            }
+            String role = getMandatoryAttributeString(atts, "role");
+            org.openstreetmap.josm.data.osm.history.RelationMember member = new org.openstreetmap.josm.data.osm.history.RelationMember(role, type,ref);
+            ((HistoryRelation)current).addMember(member);
+        }
+
+        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+            if (qName.equals("node")) {
+                startNode(atts);
+            } else if (qName.equals("way")) {
+                startWay(atts);
+            } else if (qName.equals("relation")) {
+                startRelation(atts);
+            } else if (qName.equals("tag")) {
+                handleTag(atts);
+            } else if (qName.equals("nd")) {
+                handleNodeReference(atts);
+            } else if (qName.equals("member")) {
+                handleMember(atts);
+            }
+        }
+
+        @Override
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            if (qName.equals("node")
+                    || qName.equals("way")
+                    || qName.equals("relation")) {
+                data.put(current);
+            }
+        }
+    }
+
+    public OsmHistoryReader(InputStream source) {
+        this.in = source;
+        data = new HistoryDataSet();
+    }
+
+    public HistoryDataSet parse(PleaseWaitDialog dialog) throws SAXException, IOException {
+        InputSource inputSource = new InputSource(new InputStreamReader(in, "UTF-8"));
+        dialog.currentAction.setText("Parsing OSM history data ...");
+        try {
+            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());
+        } catch (ParserConfigurationException e1) {
+            e1.printStackTrace(); // broken SAXException chaining
+            throw new SAXException(e1);
+        }
+        return data;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java	(revision 1670)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerHistoryReader.java	(revision 1670)
@@ -0,0 +1,97 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
+import org.xml.sax.SAXException;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+/**
+ * Reads the history of an {@see OsmPrimitive} from the OSM API server.
+ * 
+ */
+public class OsmServerHistoryReader extends OsmServerReader {
+
+    private OsmPrimitiveType primitiveType;
+    private long id;
+
+    /**
+     * constructor
+     * 
+     * @param type the type of the primitive whose history is to be fetched from the server.
+     *   Must not be null.
+     * @param id the id of the primitive
+     * 
+     *  @exception IllegalArgumentException thrown, if type is null
+     */
+    public OsmServerHistoryReader(OsmPrimitiveType type, long id) throws IllegalArgumentException {
+        if (type == null)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "type"));
+        if (id < 0)
+            throw new IllegalArgumentException(tr("parameter ''{0}'' >= 0 expected, got ''{1}''", "id", id));
+        this.primitiveType = type;
+        this.id = id;
+    }
+
+    /**
+     * don't use - not implemented!
+     * 
+     * @exception NotImplementedException
+     */
+    @Override
+    public DataSet parseOsm() throws OsmTransferException {
+        throw new NotImplementedException();
+    }
+
+    /**
+     * Fetches the history from the OSM API and parses it
+     * 
+     * @return the data set with the parsed history data
+     * @throws OsmTransferException thrown, if an exception occurs
+     */
+    public HistoryDataSet parseHistory() throws OsmTransferException {
+        InputStream in = null;
+        try {
+            Main.pleaseWaitDlg.progress.setValue(0);
+            Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
+            StringBuffer sb = new StringBuffer();
+            sb.append(primitiveType.getAPIName())
+            .append("/").append(id).append("/history");
+
+            in = getInputStream(sb.toString(), Main.pleaseWaitDlg);
+            if (in == null)
+                return null;
+            Main.pleaseWaitDlg.currentAction.setText(tr("Downloading history..."));
+            final OsmHistoryReader reader = new OsmHistoryReader(in);
+            HistoryDataSet data = reader.parse(Main.pleaseWaitDlg);
+            return data;
+        } catch (IOException e) {
+            if (cancel)
+                return null;
+            throw new OsmTransferException(e);
+        } catch (SAXException e) {
+            throw new OsmTransferException(e);
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch (Exception e) {
+            if (cancel)
+                return null;
+            throw new OsmTransferException(e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch(Exception e) {}
+                activeConnection = null;
+            }
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerLocationReader.java	(revision 1670)
@@ -22,5 +22,6 @@
      * Method to download OSM files from somewhere
      */
-    public DataSet parseOsm() throws SAXException, IOException {
+    @Override
+    public DataSet parseOsm() throws OsmTransferException {
         try {
             Main.pleaseWaitDlg.progress.setValue(0);
@@ -32,7 +33,4 @@
             Main.pleaseWaitDlg.currentAction.setText(tr("Downloading OSM data..."));
             final DataSet data = OsmReader.parseDataSet(in, null, Main.pleaseWaitDlg);
-//          Bounds bounds = new Bounds(new LatLon(lat1, lon1), new LatLon(lat2, lon2));
-//          DataSource src = new DataSource(bounds, origin);
-//          data.dataSources.add(src);
             in.close();
             activeConnection = null;
@@ -41,13 +39,13 @@
             if (cancel)
                 return null;
-            throw e;
+            throw new OsmTransferException(e);
         } catch (SAXException e) {
+            throw new OsmTransferException(e);
+        } catch(OsmTransferException e) {
             throw e;
         } catch (Exception e) {
             if (cancel)
                 return null;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e);
+            throw new OsmTransferException(e);
         }
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java	(revision 1670)
@@ -7,21 +7,18 @@
 import java.io.InputStream;
 
+import javax.swing.JOptionPane;
+
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.xml.sax.SAXException;
-
-import javax.swing.JOptionPane;
 
 public class OsmServerObjectReader extends OsmServerReader {
 
-    public final static  String TYPE_WAY = "way";
-    public final static  String TYPE_REL = "relation";
-    public final static  String TYPE_NODE = "node";
-
     long id;
-    String type;
+    OsmPrimitiveType type;
     boolean full;
 
-    public OsmServerObjectReader(long id, String type, boolean full) {
+    public OsmServerObjectReader(long id, OsmPrimitiveType type, boolean full) {
         this.id = id;
         this.type = type;
@@ -34,14 +31,16 @@
      * @throws IOException
      */
-    public DataSet parseOsm() throws SAXException, IOException {
+    @Override
+    public DataSet parseOsm() throws OsmTransferException {
         try {
             Main.pleaseWaitDlg.progress.setValue(0);
             Main.pleaseWaitDlg.currentAction.setText(tr("Contacting OSM Server..."));
             StringBuffer sb = new StringBuffer();
-            sb.append(type);
+            sb.append(type.getAPIName());
             sb.append("/");
             sb.append(id);
-            if (full)
+            if (full && ! type.equals(OsmPrimitiveType.NODE)) {
                 sb.append("/full");
+            }
 
             final InputStream in = getInputStream(sb.toString(), Main.pleaseWaitDlg);
@@ -52,7 +51,4 @@
             final DataSet data = osm.getDs();
 
-//          Bounds bounds = new Bounds(new LatLon(lat1, lon1), new LatLon(lat2, lon2));
-//          DataSource src = new DataSource(bounds, origin);
-//          data.dataSources.add(src);
             if (osm.getParseNotes().length() != 0) {
                 JOptionPane.showMessageDialog(Main.parent, osm.getParseNotes());
@@ -64,13 +60,13 @@
             if (cancel)
                 return null;
-            throw e;
+            throw new OsmTransferException(e);
         } catch (SAXException e) {
+            throw new OsmTransferException(e);
+        } catch(OsmTransferException e) {
             throw e;
         } catch (Exception e) {
             if (cancel)
                 return null;
-            if (e instanceof RuntimeException)
-                throw (RuntimeException)e;
-            throw new RuntimeException(e);
+            throw new OsmTransferException(e);
         }
     }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerReader.java	(revision 1670)
@@ -4,11 +4,13 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.io.IOException;
+import java.io.BufferedReader;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.zip.GZIPInputStream;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
-import java.util.zip.GZIPInputStream;
 
 import javax.swing.JOptionPane;
@@ -17,5 +19,4 @@
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.PleaseWaitDialog;
-import org.xml.sax.SAXException;
 
 /**
@@ -39,34 +40,22 @@
      * @return An reader reading the input stream (servers answer) or <code>null</code>.
      */
-    protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
-
-        // initialize API. Abort download in case of configuration or network
-        // errors
-        //
-        try {
-            api.initialize();
-        } catch(Exception e) {
-            JOptionPane.showMessageDialog(
-                    null,
-                    tr(   "Failed to initialize communication with the OSM server {0}.\n"
-                            + "Check the server URL in your preferences and your internet connection.",
-                            Main.pref.get("osm-server.url")
-                    ),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-            e.printStackTrace();
-            return null;
-        }
-
+    protected InputStream getInputStream(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws OsmTransferException  {
+        api.initialize();
         urlStr = api.getBaseUrl() + urlStr;
         return getInputStreamRaw(urlStr, pleaseWaitDlg);
     }
 
-    protected InputStream getInputStreamRaw(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws IOException {
-
-        //        System.out.println("download: "+urlStr);
-        URL url = new URL(urlStr);
-        activeConnection = (HttpURLConnection)url.openConnection();
+    protected InputStream getInputStreamRaw(String urlStr, PleaseWaitDialog pleaseWaitDlg) throws OsmTransferException {
+        URL url = null;
+        try {
+            url = new URL(urlStr);
+        } catch(MalformedURLException e) {
+            throw new OsmTransferException(e);
+        }
+        try {
+            activeConnection = (HttpURLConnection)url.openConnection();
+        } catch(Exception e) {
+            throw new OsmTransferException(tr("Failed to open connection to API {0}", url.toExternalForm()), e);
+        }
         if (cancel) {
             activeConnection.disconnect();
@@ -81,27 +70,49 @@
 
         try {
+            System.out.println("GET " + url);
             activeConnection.connect();
+        } catch (Exception e) {
+            throw new OsmTransferException(tr("Couldn't connect to the osm server. Please check your internet connection."), e);
         }
-        catch (Exception e) {
-            throw new IOException(tr("Couldn't connect to the osm server. Please check your internet connection."));
+        try {
+            if (isAuthCancelled() && activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
+                throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null);
+
+            if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                String errorHeader = activeConnection.getHeaderField("Error");
+                InputStream i = null;
+                i = activeConnection.getErrorStream();
+                StringBuilder errorBody = new StringBuilder();
+                if (i != null) {
+                    BufferedReader in = new BufferedReader(new InputStreamReader(i));
+                    String s;
+                    while((s = in.readLine()) != null) {
+                        errorBody.append(s);
+                        errorBody.append("\n");
+                    }
+                }
+
+                throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString());
+            }
+
+            String encoding = activeConnection.getContentEncoding();
+            InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg);
+            if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
+                inputStream = new GZIPInputStream(inputStream);
+            }
+            else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
+                inputStream = new InflaterInputStream(inputStream, new Inflater(true));
+            }
+            return inputStream;
+        } catch(Exception e) {
+            if (e instanceof OsmTransferException)
+                throw (OsmTransferException)e;
+            else
+                throw new OsmTransferException(e);
+
         }
-
-        if (isAuthCancelled() && activeConnection.getResponseCode() == 401)
-            return null;
-        if (activeConnection.getResponseCode() == 500)
-            throw new IOException(tr("Server returned internal error. Try a reduced area or retry after waiting some time."));
-
-        String encoding = activeConnection.getContentEncoding();
-        InputStream inputStream = new ProgressInputStream(activeConnection, pleaseWaitDlg);
-        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
-            inputStream = new GZIPInputStream(inputStream);
-        }
-        else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
-            inputStream = new InflaterInputStream(inputStream, new Inflater(true));
-        }
-        return inputStream;
     }
 
-    public abstract DataSet parseOsm() throws SAXException, IOException;
+    public abstract DataSet parseOsm() throws OsmTransferException;
 
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 1670)
@@ -101,5 +101,5 @@
         boolean useChangeset = Main.pref.getBoolean("osm-server.atomic-upload", apiVersion.compareTo("0.6")>=0);
         if (useChangeset && ! canUseChangeset) {
-            System.out.println(tr("WARNING: preference '{0}' or api version {1} of dataset requires to use changesets, but API is not handle them. Ignoring changesets.", "osm-server.atomic-upload", apiVersion));
+            System.out.println(tr("WARNING: preference ''{0}'' or api version ''{1}'' of dataset requires to use changesets, but API is not handle them. Ignoring changesets.", "osm-server.atomic-upload", apiVersion));
             useChangeset = false;
         }
Index: trunk/src/org/openstreetmap/josm/io/OsmWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/OsmWriter.java	(revision 1670)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
@@ -25,10 +26,10 @@
 
     public final String DEFAULT_API_VERSION = "0.6";
-    
+
     /**
      * The counter for newly created objects. Starts at -1 and goes down.
      */
     private long newIdCounter = -1;
- 
+
     /**
      * All newly created ids and their primitive that uses it. This is a back reference
@@ -41,5 +42,5 @@
     private String version;
     private Changeset changeset;
-    
+
     public OsmWriter(PrintWriter out, boolean osmConform, String version) {
         super(out);
@@ -47,5 +48,5 @@
         this.version = (version == null ? DEFAULT_API_VERSION : version);
     }
-    
+
     public void setWithBody(boolean wb) {
         this.withBody = wb;
@@ -57,5 +58,5 @@
         this.version = v;
     }
-    
+
     public void header() {
         out.println("<?xml version='1.0' encoding='UTF-8'?>");
@@ -70,12 +71,15 @@
     public void writeContent(DataSet ds) {
         for (Node n : ds.nodes)
-            if (shouldWrite(n))
+            if (shouldWrite(n)) {
                 visit(n);
+            }
         for (Way w : ds.ways)
-            if (shouldWrite(w))
+            if (shouldWrite(w)) {
                 visit(w);
+            }
         for (Relation e : ds.relations)
-            if (shouldWrite(e))
+            if (shouldWrite(e)) {
                 visit(e);
+            }
     }
 
@@ -100,5 +104,5 @@
         out.print(" lat='"+n.getCoor().lat()+"' lon='"+n.getCoor().lon()+"'");
         if (!withBody) {
-            out.println("/>");  
+            out.println("/>");
         } else {
             addTags(n, "node", true);
@@ -110,9 +114,10 @@
         addCommon(w, "way");
         if (!withBody) {
-            out.println("/>");  
+            out.println("/>");
         } else {
             out.println(">");
-            for (Node n : w.nodes)
+            for (Node n : w.nodes) {
                 out.println("    <nd ref='"+getUsedId(n)+"' />");
+            }
             addTags(w, "way", false);
         }
@@ -123,10 +128,10 @@
         addCommon(e, "relation");
         if (!withBody) {
-            out.println("/>");  
+            out.println("/>");
         } else {
             out.println(">");
             for (RelationMember em : e.members) {
                 out.print("    <member type='");
-                out.print(OsmApi.which(em.member));
+                out.print(OsmPrimitiveType.from(em.member).getAPIName());
                 out.println("' ref='"+getUsedId(em.member)+"' role='" +
                         XmlWriter.encode(em.role == null ? "" : em.role) + "' />");
@@ -160,14 +165,17 @@
     private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) {
         if (osm.keys != null) {
-            if (tagOpen)
+            if (tagOpen) {
                 out.println(">");
-            for (Entry<String, String> e : osm.keys.entrySet())
+            }
+            for (Entry<String, String> e : osm.keys.entrySet()) {
                 out.println("    <tag k='"+ XmlWriter.encode(e.getKey()) +
                         "' v='"+XmlWriter.encode(e.getValue())+ "' />");
+            }
             out.println("  </" + tagname + ">");
-        } else if (tagOpen)
+        } else if (tagOpen) {
             out.println(" />");
-        else
+        } else {
             out.println("  </" + tagname + ">");
+        }
     }
 
@@ -180,14 +188,16 @@
         out.print("  <"+tagname);
         if (id != 0) {
-             out.print(" id='"+getUsedId(osm)+"'");
+            out.print(" id='"+getUsedId(osm)+"'");
         }
         if (!osmConform) {
             String action = null;
-            if (osm.deleted)
+            if (osm.deleted) {
                 action = "delete";
-            else if (osm.modified)
+            } else if (osm.modified) {
                 action = "modify";
-            if (action != null)
+            }
+            if (action != null) {
                 out.print(" action='"+action+"'");
+            }
         }
         if (!osm.isTimestampEmpty()) {
@@ -199,14 +209,16 @@
         }
         out.print(" visible='"+osm.visible+"'");
-        if (osm.version != -1)
+        if (osm.version != -1) {
             out.print(" version='"+osm.version+"'");
-        if (this.changeset != null && this.changeset.id != 0)
+        }
+        if (this.changeset != null && this.changeset.id != 0) {
             out.print(" changeset='"+this.changeset.id+"'" );
-    }
-    
+        }
+    }
+
     public void close() {
         out.close();
     }
-    
+
     public void flush() {
         out.flush();
Index: trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java	(revision 1669)
+++ trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java	(revision 1670)
@@ -22,11 +22,5 @@
     private PleaseWaitDialog pleaseWaitDlg;
 
-    public class OsmServerException extends IOException {
-        private OsmServerException(String e) {
-            super(e);
-        }
-    }
-
-    public ProgressInputStream(URLConnection con, PleaseWaitDialog pleaseWaitDlg) throws IOException, OsmServerException {
+    public ProgressInputStream(URLConnection con, PleaseWaitDialog pleaseWaitDlg) throws OsmTransferException {
         this.connection = con;
 
@@ -35,6 +29,6 @@
         } catch (IOException e) {
             if (con.getHeaderField("Error") != null)
-                throw new OsmServerException(tr(con.getHeaderField("Error")));
-            throw e;
+                throw new OsmTransferException(tr(con.getHeaderField("Error")));
+            throw new OsmTransferException(e);
         }
 
@@ -43,8 +37,9 @@
         if (pleaseWaitDlg == null)
             return;
-        if (contentLength > 0)
+        if (contentLength > 0) {
             pleaseWaitDlg.progress.setMaximum(contentLength);
-        else
+        } else {
             pleaseWaitDlg.progress.setMaximum(0);
+        }
         pleaseWaitDlg.progress.setValue(0);
     }
@@ -56,6 +51,7 @@
     @Override public int read(byte[] b, int off, int len) throws IOException {
         int read = in.read(b, off, len);
-        if (read != -1)
+        if (read != -1) {
             advanceTicker(read);
+        }
         return read;
     }
@@ -63,6 +59,7 @@
     @Override public int read() throws IOException {
         int read = in.read();
-        if (read != -1)
+        if (read != -1) {
             advanceTicker(1);
+        }
         return read;
     }
@@ -76,6 +73,7 @@
             return;
 
-        if (pleaseWaitDlg.progress.getMaximum() == 0 && connection.getContentLength() != -1)
+        if (pleaseWaitDlg.progress.getMaximum() == 0 && connection.getContentLength() != -1) {
             pleaseWaitDlg.progress.setMaximum(connection.getContentLength());
+        }
 
         readSoFar += amount;
@@ -89,8 +87,9 @@
             String cur = pleaseWaitDlg.currentAction.getText();
             int i = cur.indexOf(' ');
-            if (i != -1)
+            if (i != -1) {
                 cur = cur.substring(0, i) + progStr;
-            else
+            } else {
                 cur += progStr;
+            }
             pleaseWaitDlg.currentAction.setText(cur);
         }
