Index: /trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 2569)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
 import org.openstreetmap.josm.gui.io.UploadDialog;
+import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -168,5 +169,6 @@
             return;
         Main.worker.execute(
-                createUploadTask(
+                new UploadPrimitivesTask(
+                        UploadDialog.getUploadDialog().getUploadStrategySpecification(),
                         layer,
                         apiData.getPrimitives(),
@@ -552,8 +554,4 @@
     }
 
-    public UploadPrimitivesTask createUploadTask(OsmDataLayer layer, Collection<OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
-        return new UploadPrimitivesTask(layer, toUpload, changeset, closeChangesetAfterUpload);
-    }
-
     /**
      * The task for uploading a collection of primitives
@@ -569,7 +567,9 @@
         private boolean closeChangesetAfterUpload;
         private HashSet<OsmPrimitive> processedPrimitives;
+        private UploadStrategySpecification strategy;
 
         /**
-         *
+         * Creates the task
+         * @param strategy the upload strategy
          * @param layer  the OSM data layer for which data is uploaded
          * @param toUpload the collection of primitives to upload
@@ -577,9 +577,10 @@
          * @param closeChangesetAfterUpload true, if the changeset is to be closed after uploading
          */
-        private UploadPrimitivesTask(OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
+        private UploadPrimitivesTask(UploadStrategySpecification strategy, OsmDataLayer layer, Collection <OsmPrimitive> toUpload, Changeset changeset, boolean closeChangesetAfterUpload) {
             super(tr("Uploading data for layer ''{0}''", layer.getName()),false /* don't ignore exceptions */);
             this.toUpload = toUpload;
             this.layer = layer;
             this.changeset = changeset;
+            this.strategy = strategy;
             this.closeChangesetAfterUpload = closeChangesetAfterUpload;
             this.processedPrimitives = new HashSet<OsmPrimitive>();
@@ -628,5 +629,5 @@
                     try {
                         getProgressMonitor().subTask(tr("Uploading {0} objects ...", toUpload.size()));
-                        writer.uploadOsm(layer.data.getVersion(), toUpload, changeset, getProgressMonitor().createSubTaskMonitor(1, false));
+                        writer.uploadOsm(strategy, toUpload, changeset, getProgressMonitor().createSubTaskMonitor(1, false));
                         processedPrimitives.addAll(writer.getProcessedPrimitives());
                         // if we get here we've successfully uploaded the data. Exit the loop.
Index: /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/actions/upload/ApiPreconditionCheckerHook.java	(revision 2569)
@@ -73,5 +73,5 @@
                         // value
                         System.out.println(
-                                tr("Warning: automatically truncating value of tag ''{0}'' on deleted primitive {1}",
+                                tr("Warning: automatically truncating value of tag ''{0}'' on deleted object {1}",
                                         e.getKey(),
                                         Long.toString(osmPrimitive.getId())
@@ -82,5 +82,5 @@
                     }
                     JOptionPane.showMessageDialog(Main.parent,
-                            tr("Length of value for tag ''{0}'' on primitive {1} exceeds the max. allowed length {2}. Values length is {3}.",
+                            tr("Length of value for tag ''{0}'' on object {1} exceeds the max. allowed length {2}. Values length is {3}.",
                                     e.getKey(), Long.toString(osmPrimitive.getId()), 255, e.getValue().length()
                             ),
Index: /trunk/src/org/openstreetmap/josm/command/RelationMemberConflictResolverCommand.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/command/RelationMemberConflictResolverCommand.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/command/RelationMemberConflictResolverCommand.java	(revision 2569)
@@ -68,6 +68,6 @@
         super.executeCommand();
 
-        // replace the list of nodes of 'my' way by the list of merged
-        // nodes
+        // replace the list of members of 'my' relation by the list of merged
+        // members
         //
         my.setMembers(mergedMembers);
@@ -87,5 +87,5 @@
     public void undoCommand() {
         if (! Main.map.mapView.hasLayer(layer)) {
-            logger.warning(tr("Can't undo command ''{0}'' because layer ''{1}'' is not present any more",
+            logger.warning(tr("Can''t undo command ''{0}'' because layer ''{1}'' is not present any more",
                     this.toString(),
                     layer.toString()
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMergeModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMergeModel.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/pair/ListMergeModel.java	(revision 2569)
@@ -15,5 +15,4 @@
 import java.util.HashMap;
 import java.util.Observable;
-import java.util.logging.Logger;
 
 import javax.swing.AbstractListModel;
@@ -120,7 +119,9 @@
         return entries.get(MERGED_ENTRIES);
     }
+
     protected ArrayList<T> getMyEntries() {
         return entries.get(MY_ENTRIES);
     }
+
     protected ArrayList<T> getTheirEntries() {
         return entries.get(THEIR_ENTRIES);
Index: /trunk/src/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberListMergeModel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberListMergeModel.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberListMergeModel.java	(revision 2569)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.ArrayList;
 import java.util.logging.Logger;
 
@@ -16,5 +17,4 @@
  * The model for merging two lists of relation members
  *
- *
  */
 public class RelationMemberListMergeModel extends ListMergeModel<RelationMember>{
@@ -24,11 +24,5 @@
     @Override
     public boolean isEqualEntry(RelationMember e1, RelationMember e2) {
-        boolean ret = e1.getRole().equals(e2.getRole());
-        if (!e1.getMember().isNew() ) {
-            ret = ret && (e1.getMember().getId() == e2.getMember().getId());
-        } else {
-            ret = ret && (e1 == e2);
-        }
-        return ret;
+        return e1.equals(e2);
     }
 
@@ -119,5 +113,6 @@
         if (! isFrozen())
             throw new IllegalArgumentException(tr("Merged nodes not frozen yet. Can't build resolution command"));
-        return new RelationMemberConflictResolverCommand(my, their, getMergedEntries());
+        ArrayList<RelationMember> entries = getMergedEntries();
+        return new RelationMemberConflictResolverCommand(my, their, entries);
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/gui/io/SaveLayersDialog.java	(revision 2569)
@@ -298,6 +298,6 @@
         public void cancel() {
             switch(model.getMode()) {
-                case EDITING_DATA: cancelWhenInEditingModel(); break;
-                case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;
+            case EDITING_DATA: cancelWhenInEditingModel(); break;
+            case UPLOADING_AND_SAVING: cancelSafeAndUploadTask(); break;
             }
         }
@@ -333,6 +333,6 @@
                 Mode mode = (Mode)evt.getNewValue();
                 switch(mode) {
-                    case EDITING_DATA: setEnabled(true); break;
-                    case UPLOADING_AND_SAVING: setEnabled(false); break;
+                case EDITING_DATA: setEnabled(true); break;
+                case UPLOADING_AND_SAVING: setEnabled(false); break;
                 }
             }
@@ -367,6 +367,6 @@
                 SaveLayersModel.Mode mode = (SaveLayersModel.Mode)evt.getNewValue();
                 switch(mode) {
-                    case EDITING_DATA: setEnabled(true); break;
-                    case UPLOADING_AND_SAVING: setEnabled(false); break;
+                case EDITING_DATA: setEnabled(true); break;
+                case UPLOADING_AND_SAVING: setEnabled(false); break;
                 }
             }
@@ -408,4 +408,5 @@
 
                 currentTask = new UploadLayerTask(
+                        UploadDialog.getUploadDialog().getUploadStrategySpecification(),
                         layerInfo.getLayer(),
                         monitor,
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java	(revision 2569)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.gui.io;
 
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.tr;
 import static org.openstreetmap.josm.tools.I18n.trn;
@@ -50,4 +51,5 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
 import org.openstreetmap.josm.gui.SideButton;
@@ -58,5 +60,4 @@
 import org.openstreetmap.josm.gui.tagging.TagModel;
 import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
-import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -192,6 +193,6 @@
                 JComponent.WHEN_IN_FOCUSED_WINDOW
         );
-        pnl.add(new SideButton(new ContextSensitiveHelpAction("/Dialogs/UploadDialog")));
-        HelpUtil.setHelpContext(getRootPane(),"/Dialogs/UploadDialog");
+        pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Dialogs/UploadDialog"))));
+        HelpUtil.setHelpContext(getRootPane(),ht("/Dialogs/UploadDialog"));
         return pnl;
     }
@@ -293,4 +294,5 @@
             pnlLists.add(spDelete, gcList);
         }
+        pnlChangesetSelection.setNumUploadedObjects(add.size() + update.size() + delete.size());
     }
 
@@ -320,4 +322,13 @@
         cs.put("comment", getUploadComment());
         return cs;
+    }
+
+    /**
+     * Replies the {@see UploadStrategySpecification} the user entered in the dialog.
+     * 
+     * @return the {@see UploadStrategySpecification} the user entered in the dialog.
+     */
+    public UploadStrategySpecification getUploadStrategySpecification() {
+        return pnlChangesetSelection.getUploadStrategySpecification();
     }
 
@@ -487,12 +498,25 @@
 
         protected void warnIllegalUploadComment() {
-            JOptionPane.showMessageDialog(
+            HelpAwareOptionPane.showOptionDialog(
                     UploadDialog.this,
                     tr("Please enter a comment for this upload changeset (min. 3 characters)"),
                     tr("Illegal upload comment"),
-                    JOptionPane.ERROR_MESSAGE
+                    JOptionPane.ERROR_MESSAGE,
+                    ht("/Dialog/UploadDialog#IllegalUploadComment")
 
             );
         }
+
+        protected void warnIllegalChunkSize() {
+            HelpAwareOptionPane.showOptionDialog(
+                    UploadDialog.this,
+                    tr("Please enter a valid chunk size first"),
+                    tr("Illegal chunk size"),
+                    JOptionPane.ERROR_MESSAGE,
+                    ht("/Dialog/UploadDialog#IllegalChunkSize")
+            );
+        }
+
+
         public void actionPerformed(ActionEvent e) {
             if (getUploadComment().trim().length() < 3) {
@@ -502,7 +526,15 @@
                 return;
             }
+            UploadStrategySpecification strategy = getUploadStrategySpecification();
+            if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)) {
+                if (strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
+                    warnIllegalChunkSize();
+                    southTabbedPane.setSelectedIndex(0);
+                    pnlChangesetSelection.initEditingOfChunkSize();
+                    return;
+                }
+            }
             setCanceled(false);
             setVisible(false);
-
         }
     }
@@ -607,5 +639,5 @@
         private OpenChangesetModel model;
         private HistoryComboBox cmt;
-        private JCheckBox cbUseAtomicUpload;
+        private UploadStrategySelectionPanel pnlUploadStrategy;
 
         /**
@@ -615,17 +647,8 @@
          * @return the panel
          */
-        protected JPanel buildAtomicUploadControlPanel() {
-            JPanel pnl = new JPanel();
-            pnl.setLayout(new GridBagLayout());
-            GridBagConstraints gc = new GridBagConstraints();
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            gc.anchor = GridBagConstraints.FIRST_LINE_START;
-            pnl.add(cbUseAtomicUpload = new JCheckBox(tr("Upload all changes in one request")), gc);
-            cbUseAtomicUpload.setToolTipText(tr("Enable to upload all changes in one request, disable to use one request per changed primitive"));
-            boolean useAtomicUpload = Main.pref.getBoolean("osm-server.atomic-upload", true);
-            cbUseAtomicUpload.setSelected(useAtomicUpload);
-            cbUseAtomicUpload.setEnabled(OsmApi.getOsmApi().hasSupportForDiffUploads());
-            return pnl;
+        protected JPanel buildUploadStrategySelectionPanel() {
+            pnlUploadStrategy = new UploadStrategySelectionPanel();
+            pnlUploadStrategy.initFromPreferences();
+            return pnlUploadStrategy;
         }
 
@@ -678,5 +701,5 @@
             gc.weightx = 1.0;
             gc.anchor = GridBagConstraints.FIRST_LINE_START;
-            add(buildAtomicUploadControlPanel(), gc);
+            add(buildUploadStrategySelectionPanel(), gc);
 
             // -- changeset command
@@ -764,5 +787,5 @@
             cmt.addCurrentItemToHistory();
             Main.pref.putCollection(HISTORY_KEY, cmt.getHistory());
-            Main.pref.put("osm-server.atomic-upload", cbUseAtomicUpload.isSelected());
+            pnlUploadStrategy.saveToPreferences();
         }
 
@@ -819,4 +842,8 @@
         }
 
+        public void initEditingOfChunkSize() {
+            pnlUploadStrategy.initEditingOfChunkSize();
+        }
+
         protected void refreshGUI() {
             rbExisting.setEnabled(model.getSize() > 0);
@@ -848,4 +875,13 @@
                 return new Changeset();
             return cs;
+        }
+
+        /**
+         * Replies the {@see UploadStrategySpecification} the user entered in the dialog.
+         * 
+         * @return the {@see UploadStrategySpecification} the user entered in the dialog.
+         */
+        public UploadStrategySpecification getUploadStrategySpecification() {
+            return pnlUploadStrategy.getUploadStrategySpecification();
         }
 
@@ -932,4 +968,8 @@
         public boolean isCloseAfterUpload() {
             return cbCloseAfterUpload.isSelected();
+        }
+
+        public void setNumUploadedObjects(int numUploadedObjects) {
+            pnlUploadStrategy.setNumUploadedObjects(numUploadedObjects);
         }
 
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadLayerTask.java	(revision 2569)
@@ -44,7 +44,10 @@
     private Collection<OsmPrimitive> toUpload;
     private HashSet<OsmPrimitive> processedPrimitives;
+    private UploadStrategySpecification strategy;
 
     /**
-     *
+     * Creates the upload task
+     * 
+     * @param strategy the upload strategy specification
      * @param layer the layer. Must not be null.
      * @param monitor  a progress monitor. If monitor is null, uses {@see NullProgressMonitor#INSTANCE}
@@ -52,8 +55,11 @@
      * @param closeChangesetAfterUpload true, if the changeset should be closed after the upload
      * @throws IllegalArgumentException thrown, if layer is null
+     * @throws IllegalArgumentException thrown if strategy is null
      */
-    public UploadLayerTask(OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, boolean closeChangesetAfterUpload) {
+    public UploadLayerTask(UploadStrategySpecification strategy, OsmDataLayer layer, ProgressMonitor monitor, Changeset changeset, boolean closeChangesetAfterUpload) {
         if (layer == null)
-            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", layer));
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "layer"));
+        if (strategy == null)
+            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "strategy"));
         if (monitor == null) {
             monitor = NullProgressMonitor.INSTANCE;
@@ -62,4 +68,5 @@
         this.monitor = monitor;
         this.changeset = changeset;
+        this.strategy = strategy;
         this.closeChangesetAfterUpload = closeChangesetAfterUpload;
         processedPrimitives = new HashSet<OsmPrimitive>();
@@ -103,5 +110,5 @@
     @Override
     public void run() {
-        monitor.subTask(tr("Preparing primitives to upload ..."));
+        monitor.indeterminateSubTask(tr("Preparing primitives to upload ..."));
         APIDataSet ds = new APIDataSet(layer.data);
         try {
@@ -120,5 +127,5 @@
                     ProgressMonitor m = monitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
                     if (isCancelled()) return;
-                    writer.uploadOsm(layer.data.getVersion(), toUpload, changeset, m);
+                    writer.uploadOsm(strategy, toUpload, changeset, m);
                     processedPrimitives.addAll(writer.getProcessedPrimitives());
                     break;
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategy.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategy.java	(revision 2569)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategy.java	(revision 2569)
@@ -0,0 +1,100 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import org.openstreetmap.josm.Main;
+
+public enum UploadStrategy {
+    /**
+     * Uploads the objects individually, one request per object
+     */
+    INDIVIDUAL_OBJECTS_STRATEGY("individualobjects"),
+    /**
+     * Upload the objects in junks of n objects using m diff uploads
+     */
+    CHUNKED_DATASET_STRATEGY("chunked"),
+    /**
+     * Upload the objects in one request using 1 diff upload
+     */
+    SINGLE_REQUEST_STRATEGY("singlerequest");
+
+    private String preferenceValue;
+
+    UploadStrategy(String preferenceValue) {
+        this.preferenceValue = preferenceValue;
+    }
+
+    public static UploadStrategy fromPreference(String preferenceValue) {
+        if (preferenceValue == null) return null;
+        preferenceValue = preferenceValue.trim().toLowerCase();
+        for (UploadStrategy strategy: values()) {
+            if (strategy.getPreferenceValue().equals(preferenceValue))
+                return strategy;
+        }
+        return null;
+    }
+
+    /**
+     * Replies the value which is written to the preferences for a specific
+     * upload strategy
+     * 
+     * @return the value which is written to the preferences for a specific
+     * upload strategy
+     */
+    public String getPreferenceValue() {
+        return preferenceValue;
+    }
+
+    /**
+     * the default upload strategy
+     */
+    public final static UploadStrategy DEFAULT_UPLOAD_STRATEGY = SINGLE_REQUEST_STRATEGY;
+
+    /**
+     * Replies the upload strategy currently configured in the preferences.
+     * 
+     * First checks for the preference key <pre>osm-server.upload-strategy</pre>. If not
+     * present, checks for the legacy preference key <pre>osm-server.atomic-upload</pre>.
+     * 
+     * If both are missing or if the preference value is illegal, {@see #DEFAULT_UPLOAD_STRATEGY}
+     * is replied.
+     * 
+     * @return the upload strategy currently configured in the preferences.
+     */
+    public static UploadStrategy getFromPreferences() {
+        String v = Main.pref.get("osm-server.upload-strategy");
+        if (v == null) {
+            // legacy support. Until 12/2009 we had osm-server.atomic-upload only.
+            // If we still find "osm-server.atomic-upload" we use it and remove it.
+            // When the preferences are saved the next time, "osm-server.upload-strategy"
+            // will be inserted.
+            v = Main.pref.get("osm-server.atomic-upload");
+            if (v != null) {
+                Main.pref.removeFromCollection("osm-server.atomic-upload", v);
+            }
+            v = v.trim().toLowerCase();
+            if (v.equals("true"))
+                return SINGLE_REQUEST_STRATEGY;
+            else if (v.equals("false"))
+                return INDIVIDUAL_OBJECTS_STRATEGY;
+            else
+                return DEFAULT_UPLOAD_STRATEGY;
+        }
+        UploadStrategy strategy = fromPreference(v);
+        if (strategy == null) {
+            System.err.println(tr("Warning: unexpected value for key ''{0}'' in preferences, got ''{1}''", "osm-server.upload-strategy", v ));
+            return DEFAULT_UPLOAD_STRATEGY;
+        }
+        return strategy;
+    }
+
+    /**
+     * Saves the upload strategy <code>strategy</code> to the preferences.
+     * 
+     * @param strategy the strategy to save
+     */
+    public static void saveToPreferences(UploadStrategy strategy) {
+        Main.pref.put("osm-server.upload-strategy", strategy.getPreferenceValue());
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java	(revision 2569)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySelectionPanel.java	(revision 2569)
@@ -0,0 +1,307 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.openstreetmap.josm.Main;
+
+public class UploadStrategySelectionPanel extends JPanel {
+    private static final Color BG_COLOR_ERROR = new Color(255,224,224);
+
+    private ButtonGroup bgStrategies;
+    private Map<UploadStrategy, JRadioButton> rbStrategy;
+    private Map<UploadStrategy, JLabel> lblNumRequests;
+    private JTextField tfChunkSize;
+
+    private long numUploadedObjects = 0;
+
+    public UploadStrategySelectionPanel() {
+        build();
+    }
+
+    protected void build() {
+        setLayout(new GridBagLayout());
+        bgStrategies = new ButtonGroup();
+        rbStrategy = new HashMap<UploadStrategy, JRadioButton>();
+        lblNumRequests = new HashMap<UploadStrategy, JLabel>();
+        for (UploadStrategy strategy: UploadStrategy.values()) {
+            rbStrategy.put(strategy, new JRadioButton());
+            lblNumRequests.put(strategy, new JLabel());
+            bgStrategies.add(rbStrategy.get(strategy));
+        }
+
+        // -- single request strategy
+        GridBagConstraints gc = new GridBagConstraints();
+        gc.gridx = 0;
+        gc.gridy = 0;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.insets = new Insets(0,5,0,5);
+        gc.anchor = GridBagConstraints.FIRST_LINE_START;
+        add(rbStrategy.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
+        gc.gridx = 1;
+        gc.gridy = 0;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 2;
+        add(new JLabel(tr("Upload data in one request")), gc);
+        gc.gridx = 3;
+        gc.gridy = 0;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 1;
+        add(lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY), gc);
+
+        // -- chunked dataset strategy
+        gc.gridx = 0;
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        add(rbStrategy.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
+        gc.gridx = 1;
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 1;
+        add(new JLabel(tr("Upload data in chunks of objects. Chunk size: ")), gc);
+        gc.gridx = 2;
+        gc.gridy = 1;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 1;
+        add(tfChunkSize = new JTextField(4), gc);
+        gc.gridx = 3;
+        gc.gridy = 1;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 1;
+        add(lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY), gc);
+
+        // -- single request strategy
+        gc.gridx = 0;
+        gc.gridy = 2;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        add(rbStrategy.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
+        gc.gridx = 1;
+        gc.gridy = 2;
+        gc.weightx = 0.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 2;
+        add(new JLabel(tr("Upload each object individually")), gc);
+        gc.gridx = 3;
+        gc.gridy = 2;
+        gc.weightx = 1.0;
+        gc.weighty = 0.0;
+        gc.gridwidth = 1;
+        add(lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY), gc);
+
+
+        tfChunkSize.addFocusListener(new TextFieldFocusHandler());
+        tfChunkSize.getDocument().addDocumentListener(new ChunkSizeInputVerifier());
+        StrategyChangeListener strategyChangeListener = new StrategyChangeListener();
+        for(UploadStrategy strategy: UploadStrategy.values()) {
+            rbStrategy.get(strategy).addChangeListener(strategyChangeListener);
+        }
+
+    }
+
+    public void setNumUploadedObjects(int numUploadedObjects) {
+        this.numUploadedObjects = Math.max(numUploadedObjects,0);
+        updateNumRequestsLabels();
+    }
+
+    public void setUploadStrategySpecification(UploadStrategySpecification strategy) {
+        if (strategy == null) return;
+        rbStrategy.get(strategy.getStrategy()).setSelected(true);
+        if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)) {
+            tfChunkSize.setEnabled(strategy.equals(UploadStrategy.CHUNKED_DATASET_STRATEGY));
+            if (strategy.getChunkSize() != UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
+                tfChunkSize.setText(Integer.toString(strategy.getChunkSize()));
+            } else {
+                tfChunkSize.setText("1");
+            }
+        }
+    }
+
+    public UploadStrategySpecification getUploadStrategySpecification() {
+        UploadStrategy strategy = getUploadStrategy();
+        int chunkSize = getChunkSize();
+        switch(strategy) {
+        case INDIVIDUAL_OBJECTS_STRATEGY: return UploadStrategySpecification.createIndividualObjectStrategy();
+        case SINGLE_REQUEST_STRATEGY: return UploadStrategySpecification.createSingleRequestUploadStrategy();
+        case CHUNKED_DATASET_STRATEGY: return UploadStrategySpecification.createChunkedUploadStrategy(chunkSize);
+        }
+        // should not happen
+        return null;
+    }
+
+    protected UploadStrategy getUploadStrategy() {
+        UploadStrategy strategy = null;
+        for (UploadStrategy s: rbStrategy.keySet()) {
+            if (rbStrategy.get(s).isSelected()) {
+                strategy = s;
+                break;
+            }
+        }
+        return strategy;
+    }
+
+
+    protected int getChunkSize() {
+        int chunkSize;
+        try {
+            chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
+            return chunkSize;
+        } catch(NumberFormatException e) {
+            return UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE;
+        }
+    }
+
+    public void initFromPreferences() {
+        UploadStrategy strategy = UploadStrategy.getFromPreferences();
+        rbStrategy.get(strategy).setSelected(true);
+        int chunkSize = Main.pref.getInteger("osm-server.upload-strategy.chunk-size", 1);
+        tfChunkSize.setText(Integer.toString(chunkSize));
+        updateNumRequestsLabels();
+    }
+
+    public void saveToPreferences() {
+        UploadStrategy strategy = getUploadStrategy();
+        UploadStrategy.saveToPreferences(strategy);
+        int chunkSize;
+        try {
+            chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
+            Main.pref.putInteger("osm-server.upload-strategy.chunk-size", chunkSize);
+        } catch(NumberFormatException e) {
+            // don't save invalid value to preferences
+        }
+    }
+
+    protected void updateNumRequestsLabels() {
+        lblNumRequests.get(UploadStrategy.SINGLE_REQUEST_STRATEGY).setText(tr("(1 request)"));
+        if (numUploadedObjects == 0) {
+            lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(tr("(# requests unknown)"));
+            lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
+        } else {
+            lblNumRequests.get(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY).setText(
+                    trn("({0} request)", "({0} requests)", numUploadedObjects, numUploadedObjects)
+            );
+            lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
+            int chunkSize = getChunkSize();
+            if (chunkSize == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
+                lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(tr("(# requests unknown)"));
+            } else {
+                int chunks = (int)Math.ceil((double)numUploadedObjects / (double)chunkSize);
+                lblNumRequests.get(UploadStrategy.CHUNKED_DATASET_STRATEGY).setText(
+                        trn("({0} request)", "({0} requests)", chunks, chunks)
+                );
+            }
+        }
+    }
+
+    public void initEditingOfChunkSize() {
+        tfChunkSize.requestFocusInWindow();
+    }
+
+    class TextFieldFocusHandler implements FocusListener {
+        public void focusGained(FocusEvent e) {
+            Component c = e.getComponent();
+            if (c instanceof JTextField) {
+                JTextField tf = (JTextField)c;
+                tf.selectAll();
+            }
+        }
+        public void focusLost(FocusEvent e) {}
+    }
+
+    class ChunkSizeInputVerifier implements DocumentListener, PropertyChangeListener {
+
+        protected void setErrorFeedback(JTextField tf, String message) {
+            tf.setBorder(BorderFactory.createLineBorder(Color.RED, 1));
+            tf.setToolTipText(message);
+            tf.setBackground(BG_COLOR_ERROR);
+        }
+
+        protected void clearErrorFeedback(JTextField tf, String message) {
+            tf.setBorder(UIManager.getBorder("TextField.border"));
+            tf.setToolTipText(message);
+            tf.setBackground(UIManager.getColor("TextField.background"));
+        }
+
+        protected void valiateChunkSize() {
+            try {
+                int chunkSize = Integer.parseInt(tfChunkSize.getText().trim());
+                if (chunkSize <= 0) {
+                    setErrorFeedback(tfChunkSize, tr("Illegal chunk size <= 0. Please enter an integer > 1"));
+                } else {
+                    clearErrorFeedback(tfChunkSize, tr("Please enter an integer > 1"));
+                }
+            } catch(NumberFormatException e) {
+                setErrorFeedback(tfChunkSize, tr("Value ''{0}'' is not a number. Please enter an integer > 1", tfChunkSize.getText().trim()));
+            } finally {
+                updateNumRequestsLabels();
+            }
+        }
+
+        public void changedUpdate(DocumentEvent arg0) {
+            valiateChunkSize();
+        }
+
+        public void insertUpdate(DocumentEvent arg0) {
+            valiateChunkSize();
+        }
+
+        public void removeUpdate(DocumentEvent arg0) {
+            valiateChunkSize();
+        }
+
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getSource() == tfChunkSize
+                    && evt.getPropertyName().equals("enabled")
+                    && (Boolean)evt.getNewValue()
+            ) {
+                valiateChunkSize();
+            }
+        }
+    }
+
+    class StrategyChangeListener implements ChangeListener {
+        public void stateChanged(ChangeEvent e) {
+            UploadStrategy strategy = getUploadStrategy();
+            if (strategy == null) return;
+            switch(strategy) {
+            case CHUNKED_DATASET_STRATEGY:
+                tfChunkSize.setEnabled(true);
+                tfChunkSize.requestFocusInWindow();
+                break;
+            default:
+                tfChunkSize.setEnabled(false);
+            }
+        }
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySpecification.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySpecification.java	(revision 2569)
+++ /trunk/src/org/openstreetmap/josm/gui/io/UploadStrategySpecification.java	(revision 2569)
@@ -0,0 +1,32 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+public class UploadStrategySpecification {
+    static public final int UNSPECIFIED_CHUNK_SIZE = -1;
+    static public UploadStrategySpecification createIndividualObjectStrategy() {
+        return new UploadStrategySpecification(UploadStrategy.INDIVIDUAL_OBJECTS_STRATEGY, 1);
+    }
+
+    static public UploadStrategySpecification createChunkedUploadStrategy(int chunkSize) {
+        return new UploadStrategySpecification(UploadStrategy.CHUNKED_DATASET_STRATEGY, chunkSize);
+    }
+
+    static public UploadStrategySpecification createSingleRequestUploadStrategy() {
+        return new UploadStrategySpecification(UploadStrategy.SINGLE_REQUEST_STRATEGY, UNSPECIFIED_CHUNK_SIZE);
+    }
+
+    private UploadStrategy strategy;
+    private int chunkSize;
+
+    private UploadStrategySpecification(UploadStrategy strategy, int chunkSize) {
+        this.strategy = strategy;
+        this.chunkSize = chunkSize;
+    }
+
+    public UploadStrategy getStrategy() {
+        return strategy;
+    }
+    public int getChunkSize() {
+        return chunkSize;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/io/ChangesetClosedException.java	(revision 2569)
@@ -12,5 +12,54 @@
 import java.util.regex.Pattern;
 
+/**
+ * A ChangesetClosedException is thrown if the server replies with a HTTP
+ * return code 409 (Conflict) with the error header {@see #ERROR_HEADER_PATTERN}.
+ *
+ * Depending on the context the exception is thrown in we have to react differently.
+ * <ul>
+ *   <li>if it is thrown when we try to update a changeset, the changeset was most
+ *   likely closed before, either explicitly by the user or because of a timeout</li>
+ *   <li>if it is thrown when we try to upload data to the changeset, the changeset
+ *   was most likely closed because we reached the servers capability limit for the size
+ *   of a changeset.</li>
+ *  </ul>
+ */
 public class ChangesetClosedException extends OsmTransferException {
+    /** the error header pattern for in case of HTTP response 409 indicating
+     * that a changeset was closed
+     */
+    final static public String ERROR_HEADER_PATTERN = "The changeset (\\d+) was closed at (.*)";
+
+    static enum Source {
+        /**
+         * The exception was thrown when a changeset was updated. This most likely means
+         * that the changeset was closed before.
+         */
+        UPDATE_CHANGESET,
+        /**
+         * The exception was thrown when data was uploaded to the changeset. This most
+         * likely means that the servers capability limits for a changeset have been
+         * exceeded.
+         */
+        UPLOAD_DATA,
+        /**
+         * Unspecified source
+         */
+        UNSPECIFIED
+    }
+
+    /**
+     * Replies true if <code>errorHeader</code> matches with {@see #ERROR_HEADER_PATTERN}
+     * 
+     * @param errorHeader the error header
+     * @return true if <code>errorHeader</code> matches with {@see #ERROR_HEADER_PATTERN}
+     */
+    static public boolean errorHeaderMatchesPattern(String errorHeader) {
+        if (errorHeader == null)
+            return false;
+        Pattern p = Pattern.compile(ERROR_HEADER_PATTERN);
+        Matcher m = p.matcher(errorHeader);
+        return m.matches();
+    }
 
     /** the changeset id */
@@ -18,8 +67,9 @@
     /** the date on which the changeset was closed */
     private Date closedOn;
+    /** the source */
+    private Source source;
 
     protected void parseErrorHeader(String errorHeader) {
-        String pattern = "The changeset (\\d+) was closed at (.*)";
-        Pattern p = Pattern.compile(pattern);
+        Pattern p = Pattern.compile(ERROR_HEADER_PATTERN);
         Matcher m = p.matcher(errorHeader);
         if (m.matches()) {
@@ -39,15 +89,53 @@
     }
 
+    /**
+     * Creates the exception with the given <code>errorHeader</code>
+     * 
+     * @param errorHeader the error header
+     */
     public ChangesetClosedException(String errorHeader) {
         super(errorHeader);
         parseErrorHeader(errorHeader);
+        this.source = Source.UNSPECIFIED;
     }
 
+    /**
+     * Creates the exception with the given error header and the given
+     * source.
+     * 
+     * @param errorHeader the error header
+     * @param source the source for the exception
+     */
+    public ChangesetClosedException(String errorHeader, Source source) {
+        super(errorHeader);
+        parseErrorHeader(errorHeader);
+        this.source = source == null ? Source.UNSPECIFIED : source;
+    }
+
+    /**
+     * Replies the id of the changeset which was closed
+     * 
+     * @return the id of the changeset which was closed
+     */
     public long getChangesetId() {
         return changesetId;
     }
 
+    /**
+     * Replies the date the changeset was closed
+     * 
+     * @return the date the changeset was closed. May be null if the date isn't known.
+     */
     public Date getClosedOn() {
         return closedOn;
     }
+
+    /**
+     * Replies the source where the exception was thrown
+     * 
+     * @return the source
+     */
+    public Source getSource() {
+        return source;
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 2569)
@@ -145,12 +145,4 @@
 
     /**
-     * Returns true if the negotiated version supports diff uploads.
-     * @return true if the negotiated version supports diff uploads
-     */
-    public boolean hasSupportForDiffUploads() {
-        return ((version != null) && (version.compareTo("0.6")>=0));
-    }
-
-    /**
      * Initializes this component by negotiating a protocol version with the server.
      *
@@ -168,9 +160,7 @@
             if (capabilities.supportsVersion("0.6")) {
                 version = "0.6";
-            } else if (capabilities.supportsVersion("0.5")) {
-                version = "0.5";
             } else {
                 System.err.println(tr("This version of JOSM is incompatible with the configured server."));
-                System.err.println(tr("It supports protocol versions 0.5 and 0.6, while the server says it supports {0} to {1}.",
+                System.err.println(tr("It supports protocol version 0.6, while the server says it supports {0} to {1}.",
                         capabilities.get("version", "minimum"), capabilities.get("version", "maximum")));
                 initialized = false;
@@ -256,9 +246,8 @@
 
     /**
-     * Modifies an OSM primitive on the server. For protocols greater than 0.5,
-     * the OsmPrimitive object passed in is modified by giving it the server-assigned
-     * version.
-     *
-     * @param osm the primitive. Must not be null
+     * Modifies an OSM primitive on the server.
+     *
+     * @param osm the primitive. Must not be null.
+     * @param monitor the progress monitor
      * @throws OsmTransferException if something goes wrong
      */
@@ -268,12 +257,7 @@
             ensureValidChangeset();
             initialize(monitor);
-            if (version.equals("0.5")) {
-                // legacy mode does not return the new object version.
-                sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true),monitor);
-            } else {
-                // normal mode (0.6 and up) returns new object version.
-                ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true), monitor);
-                osm.setOsmId(osm.getId(), Integer.parseInt(ret.trim()));
-            }
+            // normal mode (0.6 and up) returns new object version.
+            ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true), monitor);
+            osm.setOsmId(osm.getId(), Integer.parseInt(ret.trim()));
         } catch(NumberFormatException e) {
             throw new OsmTransferException(tr("Unexpected format of new version of modified primitive ''{0}''. Got ''{1}''.", osm.getId(), ret));
@@ -359,6 +343,6 @@
             );
         } catch(OsmApiException e) {
-            if (e.getResponseCode() == HttpURLConnection.HTTP_CONFLICT)
-                throw new ChangesetClosedException(e.getErrorHeader());
+            if (e.getResponseCode() == HttpURLConnection.HTTP_CONFLICT && ChangesetClosedException.errorHeaderMatchesPattern(e.getErrorHeader()))
+                throw new ChangesetClosedException(e.getErrorHeader(), ChangesetClosedException.Source.UPDATE_CHANGESET);
             throw e;
         } finally {
@@ -569,4 +553,9 @@
                 case HttpURLConnection.HTTP_GONE:
                     throw new OsmApiPrimitiveGoneException(errorHeader, errorBody);
+                case HttpURLConnection.HTTP_CONFLICT:
+                    if (ChangesetClosedException.errorHeaderMatchesPattern(errorHeader))
+                        throw new ChangesetClosedException(errorBody, ChangesetClosedException.Source.UPLOAD_DATA);
+                    else
+                        throw new OsmApiException(retCode, errorHeader, errorBody);
                 default:
                     throw new OsmApiException(retCode, errorHeader, errorBody);
@@ -642,4 +631,3 @@
         this.changeset = changeset;
     }
-
 }
Index: /trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 2568)
+++ /trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java	(revision 2569)
@@ -5,12 +5,15 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.logging.Logger;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -36,4 +39,5 @@
 
     private OsmApi api = OsmApi.getOsmApi();
+    private boolean canceled = false;
 
     private static final int MSECS_PER_SECOND = 1000;
@@ -42,4 +46,5 @@
 
     long uploadStartTime;
+
 
     public String timeLeft(int progress, int list_size) {
@@ -125,14 +130,55 @@
 
     /**
+     * Upload all changes in one diff upload
+     *
+     * @param primitives the collection of primitives to upload
+     * @param progressMonitor  the progress monitor
+     * @param chunkSize the size of the individual upload chunks. > 0 required.
+     * @throws IllegalArgumentException thrown if chunkSize <= 0
+     * @throws OsmTransferException thrown if an exception occurs
+     */
+    protected void uploadChangesInChunks(Collection<OsmPrimitive> primitives, ProgressMonitor progressMonitor, int chunkSize) throws OsmTransferException, IllegalArgumentException {
+        if (chunkSize <=0)
+            throw new IllegalArgumentException(tr("Value >0 expected for parameter ''{0}'', got {1}", "chunkSize", chunkSize));
+        try {
+            progressMonitor.beginTask(tr("Starting to upload in chunks..."));
+            List<OsmPrimitive> chunk = new ArrayList<OsmPrimitive>(chunkSize);
+            Iterator<OsmPrimitive> it = primitives.iterator();
+            int numChunks = (int)Math.ceil((double)primitives.size() / (double)chunkSize);
+            int i= 0;
+            while(it.hasNext()) {
+                i++;
+                progressMonitor.setCustomText(tr("({0}/{1}) Uploading {2} objects...", i,numChunks,chunkSize));
+                if (canceled) return;
+                int j = 0;
+                chunk.clear();
+                while(it.hasNext() && j < chunkSize) {
+                    if (canceled) return;
+                    j++;
+                    chunk.add(it.next());
+                }
+                processed.addAll(api.uploadDiff(chunk, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false)));
+            }
+        } catch(OsmTransferException e) {
+            throw e;
+        } catch(Exception e) {
+            throw new OsmTransferException(e);
+        } finally {
+            progressMonitor.finishTask();
+        }
+    }
+
+    /**
      * Send the dataset to the server.
      *
-     * @param apiVersion version of the data set
+     * @param strategy the upload strategy. Must not be null.
      * @param primitives list of objects to send
      * @param changeset the changeset the data is uploaded to. Must not be null.
      * @param monitor the progress monitor. If null, assumes {@see NullProgressMonitor#INSTANCE}
      * @throws IllegalArgumentException thrown if changeset is null
+     * @throws IllegalArgumentException thrown if strategy is null
      * @throws OsmTransferException thrown if something goes wrong
      */
-    public void uploadOsm(String apiVersion, Collection<OsmPrimitive> primitives, Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
+    public void uploadOsm(UploadStrategySpecification strategy, Collection<OsmPrimitive> primitives, Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
         if (changeset == null)
             throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "changeset"));
@@ -143,15 +189,4 @@
             api.initialize(monitor);
             // check whether we can use diff upload
-            //
-            boolean canUseDiffUpload = api.hasSupportForDiffUploads();
-            if (apiVersion == null) {
-                System.out.println(tr("WARNING: no API version defined for data to upload. Falling back to version 0.6"));
-                apiVersion = "0.6";
-            }
-            boolean useDiffUpload = Main.pref.getBoolean("osm-server.atomic-upload", apiVersion.compareTo("0.6")>=0);
-            if (useDiffUpload && ! canUseDiffUpload) {
-                System.out.println(tr("WARNING: preference ''{0}'' or API version ''{1}'' of dataset requires to use diff uploads, but API is not able to handle them. Ignoring diff upload.", "osm-server.atomic-upload", apiVersion));
-                useDiffUpload = false;
-            }
             if (changeset.getId() == 0) {
                 api.openChangeset(changeset,monitor.createSubTaskMonitor(0, false));
@@ -160,8 +195,14 @@
             }
             api.setChangeset(changeset);
-            if (useDiffUpload) {
+            switch(strategy.getStrategy()) {
+            case SINGLE_REQUEST_STRATEGY:
                 uploadChangesAsDiffUpload(primitives,monitor.createSubTaskMonitor(0,false));
-            } else {
+                break;
+            case INDIVIDUAL_OBJECTS_STRATEGY:
                 uploadChangesIndividually(primitives,monitor.createSubTaskMonitor(0,false));
+                break;
+            case CHUNKED_DATASET_STRATEGY:
+                uploadChangesInChunks(primitives,monitor.createSubTaskMonitor(0,false), strategy.getChunkSize());
+                break;
             }
         } catch(OsmTransferException e) {
@@ -186,4 +227,5 @@
 
     public void cancel() {
+        this.canceled = true;
         if (api != null) {
             api.cancel();
Index: /trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
===================================================================
--- /trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(revision 2568)
+++ /trunk/test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(revision 2569)
@@ -32,4 +32,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.projection.Mercator;
+import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 
@@ -124,5 +125,5 @@
         OsmServerWriter writer = new OsmServerWriter();
         Changeset cs = new Changeset();
-        writer.uploadOsm("0.6", primitives,cs,NullProgressMonitor.INSTANCE);
+        writer.uploadOsm(UploadStrategySpecification.createSingleRequestUploadStrategy(), primitives,cs,NullProgressMonitor.INSTANCE);
         OsmApi.getOsmApi().closeChangeset(cs, NullProgressMonitor.INSTANCE);
     }
Index: /trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
===================================================================
--- /trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 2568)
+++ /trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 2569)
@@ -33,4 +33,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.projection.Mercator;
+import org.openstreetmap.josm.gui.io.UploadStrategySpecification;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 
@@ -128,5 +129,5 @@
         OsmServerWriter writer = new OsmServerWriter();
         Changeset cs  = new Changeset();
-        writer.uploadOsm("0.6", primitives, cs, NullProgressMonitor.INSTANCE);
+        writer.uploadOsm(UploadStrategySpecification.createSingleRequestUploadStrategy(), primitives, cs, NullProgressMonitor.INSTANCE);
         OsmApi.getOsmApi().closeChangeset(cs, NullProgressMonitor.INSTANCE);
     }
Index: /trunk/test/functional/org/openstreetmap/josm/io/UploadStrategySelectionPanelTest.java
===================================================================
--- /trunk/test/functional/org/openstreetmap/josm/io/UploadStrategySelectionPanelTest.java	(revision 2569)
+++ /trunk/test/functional/org/openstreetmap/josm/io/UploadStrategySelectionPanelTest.java	(revision 2569)
@@ -0,0 +1,29 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.io;
+
+import java.awt.BorderLayout;
+
+import javax.swing.JFrame;
+
+import org.openstreetmap.josm.gui.io.UploadStrategySelectionPanel;
+
+public class UploadStrategySelectionPanelTest extends JFrame {
+
+    private UploadStrategySelectionPanel pnl;
+
+    protected void build()  {
+        getContentPane().setLayout(new BorderLayout());
+        pnl = new UploadStrategySelectionPanel();
+        getContentPane().add(pnl, BorderLayout.CENTER);
+        setSize(400,400);
+    }
+
+    public UploadStrategySelectionPanelTest() {
+        build();
+        pnl.setNumUploadedObjects(1500);
+    }
+
+    public static void main(String args[]) {
+        new UploadStrategySelectionPanelTest().setVisible(true);
+    }
+}
