Index: trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java	(revision 4080)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java	(revision 4081)
@@ -4,9 +4,16 @@
 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
 import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagConstraints;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Font;
 import java.awt.GridBagLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import javax.swing.BorderFactory;
+import javax.swing.GroupLayout;
 
 import javax.swing.JCheckBox;
@@ -14,17 +21,25 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
 import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.border.EtchedBorder;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadPrimitiveTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadReferrersTask;
 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.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.gui.widgets.OsmIdTextField;
 import org.openstreetmap.josm.gui.widgets.OsmPrimitiveTypesComboBox;
+import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -42,4 +57,20 @@
 
     public void actionPerformed(ActionEvent e) {
+        
+        JPanel all = new JPanel();
+        GroupLayout layout = new GroupLayout(all);
+        all.setLayout(layout);
+        layout.setAutoCreateGaps(true);
+        layout.setAutoCreateContainerGaps(true);
+        
+        JLabel lbl1 = new JLabel(tr("Object type:"));
+        OsmPrimitiveTypesComboBox cbType = new OsmPrimitiveTypesComboBox();
+        cbType.addItem(new SimpleListItem("mixed", tr("mixed")));
+        cbType.setToolTipText(tr("Choose the OSM object type"));
+        JLabel lbl2 = new JLabel(tr("Object ID:"));
+        OsmIdTextField tfId = new OsmIdTextField();
+        tfId.setToolTipText(tr("Enter the ID of the object that should be downloaded"));
+        // forward the enter key stroke to the download button
+        tfId.getKeymap().removeKeyStrokeBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
         JCheckBox layer = new JCheckBox(tr("Separate Layer"));
         layer.setToolTipText(tr("Select if the data should be downloaded into a new layer"));
@@ -48,31 +79,36 @@
         referrers.setToolTipText(tr("Select if the referrers of the object should be downloaded as well"));
         referrers.setSelected(Main.pref.getBoolean("downloadprimitive.referrers"));
-        JPanel all = new JPanel(new GridBagLayout());
-        GridBagConstraints gc = new GridBagConstraints();
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.anchor = GridBagConstraints.FIRST_LINE_START;
-        gc.gridy = 0;
-        gc.weightx = 0;
-        all.add(new JLabel(tr("Object type:")), gc);
-        OsmPrimitiveTypesComboBox cbType = new OsmPrimitiveTypesComboBox();
-        cbType.setToolTipText(tr("Choose the OSM object type"));
-        gc.weightx = 1;
-        all.add(cbType, gc);
-        gc.gridy = 1;
-        gc.weightx = 0;
-        all.add(new JLabel(tr("Object ID:")), gc);
-        OsmIdTextField tfId = new OsmIdTextField();
-        tfId.setToolTipText(tr("Enter the ID of the object that should be downloaded"));
-        // forward the enter key stroke to the download button
-        tfId.getKeymap().removeKeyStrokeBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
-        gc.weightx = 1;
-        all.add(tfId, gc);
-        gc.gridy = 2;
-        gc.fill = GridBagConstraints.BOTH;
-        gc.weighty = 1.0;
-        gc.weightx = 0;
-        all.add(referrers, gc);
-        gc.gridy = 3;
-        all.add(layer, gc);
+        HtmlPanel help = new HtmlPanel(tr("Object IDs can be separated by comma or space.<br/>"
+                + " Examples: <b><ul><li>1 2 5</li><li>1,2,5</li></ul><br/></b>"
+                + " In mixed mode, specify objects like this: <b>w123, n110, w12, r15</b><br/>"));
+        help.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
+        
+        layout.setVerticalGroup(layout.createSequentialGroup()
+            .addGroup(layout.createParallelGroup()
+                .addComponent(lbl1)
+                .addComponent(cbType, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE))
+            .addGroup(layout.createParallelGroup()
+                .addComponent(lbl2)
+                .addComponent(tfId, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE))
+            .addComponent(referrers)
+            .addComponent(layer)
+            .addComponent(help)
+        );
+        
+        layout.setHorizontalGroup(layout.createParallelGroup()
+            .addGroup(layout.createSequentialGroup()
+                .addGroup(layout.createParallelGroup()
+                    .addComponent(lbl1)
+                    .addComponent(lbl2)
+                )
+                .addGroup(layout.createParallelGroup()
+                    .addComponent(cbType)
+                    .addComponent(tfId))
+                )
+            .addComponent(referrers)
+            .addComponent(layer)
+            .addComponent(help)
+        );
+        
         ExtendedDialog dialog = new ExtendedDialog(Main.parent,
                 tr("Download object"),
@@ -93,20 +129,23 @@
         Main.pref.put("downloadprimitive.referrers", referrers.isSelected());
         Main.pref.put("download.newlayer", layer.isSelected());
-        int id = tfId.getOsmId();
-        if(id <= 0)
+
+        tfId.setType(cbType.getType());
+        if(tfId.readOsmIds()==false) {
             JOptionPane.showMessageDialog(
                     Main.parent,
-                    tr("Invalid ID specified. Cannot download object."),
+                    tr("Invalid ID list specified\n"
+                    + " Cannot download object."),
                     tr("Information"),
                     JOptionPane.INFORMATION_MESSAGE
             );
-        else
-            download(layer.isSelected(), cbType.getType(), id, referrers.isSelected());
-    }
-
-    /**
-     * Download the given primitive.
-     */
-    public void download(boolean newLayer, OsmPrimitiveType type, int id, boolean downloadReferrers) {
+            return;
+        }
+
+        processItems(layer.isSelected(), cbType.getType(), tfId.getIds(), referrers.isSelected());
+    }
+    
+    void processItems(boolean newLayer, OsmPrimitiveType type, 
+            final List<PrimitiveId> ids,
+            boolean downloadReferrers) {
         OsmDataLayer layer = getEditLayer();
         if ((layer == null) || newLayer) {
@@ -114,7 +153,111 @@
             Main.main.addLayer(layer);
         }
-        Main.worker.submit(new DownloadPrimitiveTask(new SimplePrimitiveId(id, type), layer));
+        final DownloadPrimitivesTask task = new DownloadPrimitivesTask(layer, ids);
+        Main.worker.submit(task);
+        
         if (downloadReferrers) {
-            Main.worker.submit(new DownloadReferrersTask(layer, id, type));
+            for (PrimitiveId id : ids) {
+                Main.worker.submit(new DownloadReferrersTask(layer, id));
+            }
+        }
+        
+        Runnable showErrorsAndWarnings = new Runnable() {
+            @Override
+            public void run() {
+                Set<PrimitiveId> errs = task.getMissingPrimitives();
+                if (errs != null && !errs.isEmpty()) {
+                    final ExtendedDialog dlg = reportProblemDialog(errs,
+                            trn("Object could not be downloaded", "Some objects could not be downloaded", errs.size()),
+                            trn("One object could not be downloaded.<br>", 
+                                "{0} objects could not be downloaded.<br>",
+                                errs.size(),
+                                errs.size())
+                            + tr("The server replied with response code 404.<br>"
+                                + "This usually means, the server does not know an object with the requested id."), 
+                            tr("missing objects:"),
+                            JOptionPane.ERROR_MESSAGE
+                    );
+                    try {
+                        SwingUtilities.invokeAndWait(new Runnable() {
+                            @Override
+                            public void run() {
+                                dlg.showDialog();
+                            }
+                        });
+                    } catch (InterruptedException ex) {
+                    } catch (InvocationTargetException ex) {
+                    }
+                }
+                
+                Set<PrimitiveId> del = new TreeSet<PrimitiveId>();
+                DataSet ds = getCurrentDataSet();
+                for (PrimitiveId id : ids) {
+                    OsmPrimitive osm = ds.getPrimitiveById(id);
+                    if (osm != null && osm.isDeleted()) {
+                        del.add(id);
+                    }
+                }
+                if (del != null && !del.isEmpty()) {
+                    final ExtendedDialog dlg = reportProblemDialog(del,
+                            trn("Object deleted", "Objects deleted", del.size()),
+                            trn(
+                                "One downloaded object is deleted.", 
+                                "{0} downloaded objects are deleted.",
+                                del.size(),
+                                del.size()),
+                            null,
+                            JOptionPane.WARNING_MESSAGE
+                    );
+                    SwingUtilities.invokeLater(new Runnable() {
+                        @Override
+                        public void run() {
+                            dlg.showDialog();
+                        }
+                    });
+                }
+            }
+        };
+        Main.worker.submit(showErrorsAndWarnings);
+    }
+    
+    private ExtendedDialog reportProblemDialog(Set<PrimitiveId> errs, 
+            String TITLE, String TEXT, String LIST_LABEL, int msgType) {
+        JPanel p = new JPanel(new GridBagLayout());
+        p.add(new HtmlPanel(TEXT), GBC.eop());
+        if (LIST_LABEL != null) {
+            JLabel missing = new JLabel(LIST_LABEL);
+            missing.setFont(missing.getFont().deriveFont(Font.PLAIN));
+            p.add(missing, GBC.eol());
+        }
+        JTextArea txt = new JTextArea();
+        txt.setFont(new Font("Monospaced", txt.getFont().getStyle(), txt.getFont().getSize()));
+        txt.setEditable(false);
+        txt.setBackground(p.getBackground());
+        txt.setColumns(40);
+        txt.setRows(1);
+        txt.setText(Utils.join(", ", errs));
+        JScrollPane scroll = new JScrollPane(txt);
+        p.add(scroll, GBC.eop().weight(1.0, 0.0).fill(GBC.HORIZONTAL));
+
+        return new ExtendedDialog(
+                Main.parent,
+                TITLE, 
+                new String[] { tr("Ok") })
+            .setButtonIcons(new String[] { "ok" })
+            .setIcon(msgType)
+            .setContent(p, false);
+    }
+
+    private static class SimpleListItem  {
+        final String data;
+        final String text;
+        
+        public SimpleListItem(String data, String text) {  
+            this.data = data;   
+            this.text = text; 
+        }
+        
+        @Override public String toString() { 
+            return text; 
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java	(revision 4081)
+++ trunk/src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java	(revision 4081)
@@ -0,0 +1,178 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.CheckParameterUtil.ensureParameterNotNull;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.DataSetMerger;
+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.PrimitiveId;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
+import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.io.OsmTransferException;
+import org.xml.sax.SAXException;
+
+public class DownloadPrimitivesTask extends PleaseWaitRunnable {
+    @SuppressWarnings("unused")
+    static private final Logger logger = Logger.getLogger(UpdatePrimitivesTask.class.getName());
+
+    private DataSet ds;
+    private boolean canceled;
+    private Exception lastException;
+    private List<PrimitiveId> ids;
+
+    private Set<PrimitiveId> missingPrimitives;
+
+    private OsmDataLayer layer;
+    private MultiFetchServerObjectReader multiObjectReader;
+    private OsmServerObjectReader objectReader;
+
+    /**
+     * Creates the  task
+     *
+     * @param layer the layer in which primitives are updated. Must not be null.
+     * @param toUpdate a collection of primitives to update from the server. Set to
+     * the empty collection if null.
+     * @throws IllegalArgumentException thrown if layer is null.
+     */
+    public DownloadPrimitivesTask(OsmDataLayer layer, List<PrimitiveId>  ids) throws IllegalArgumentException {
+        super(tr("Download objects"), false /* don't ignore exception */);
+        ensureParameterNotNull(layer, "layer");
+        this.ids = ids;
+        this.layer = layer;
+    }
+
+    @Override
+    protected void cancel() {
+        canceled = true;
+        synchronized(this) {
+            if (multiObjectReader != null) {
+                multiObjectReader.cancel();
+            }
+            if (objectReader != null) {
+                objectReader.cancel();
+            }
+        }
+    }
+
+    @Override
+    protected void finish() {
+        if (canceled)
+            return;
+        if (lastException != null) {
+            ExceptionDialogUtil.explainException(lastException);
+            return;
+        }
+        Runnable r = new Runnable() {
+            public void run() {
+                layer.mergeFrom(ds);
+                layer.onPostDownloadFromServer();
+            }
+        };
+
+        if (SwingUtilities.isEventDispatchThread()) {
+            r.run();
+        } else {
+            try {
+                SwingUtilities.invokeAndWait(r);
+            } catch(InterruptedException e) {
+                e.printStackTrace();
+            } catch(InvocationTargetException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    protected void initMultiFetchReader(MultiFetchServerObjectReader reader) {
+        getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to download ..."));
+        for (PrimitiveId id : ids) {
+            OsmPrimitive osm = layer.data.getPrimitiveById(id);
+            if (osm == null) {
+                switch (id.getType()) {
+                    case NODE: 
+                        osm = new Node(id.getUniqueId());
+                        break;
+                    case WAY:
+                        osm = new Node(id.getUniqueId());
+                        break;
+                    case RELATION:
+                        osm = new Node(id.getUniqueId());
+                        break;
+                    default: throw new AssertionError();
+                }
+            }
+            reader.append(osm);
+        }
+    }
+
+    @Override
+    protected void realRun() throws SAXException, IOException, OsmTransferException {
+        this.ds = new DataSet();
+        DataSet theirDataSet;
+        try {
+            synchronized(this) {
+                if (canceled) return;
+                multiObjectReader = new MultiFetchServerObjectReader();
+            }
+            initMultiFetchReader(multiObjectReader);
+            theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            missingPrimitives = multiObjectReader.getMissingPrimitives();
+            synchronized(this) {
+                multiObjectReader = null;
+            }
+            DataSetMerger merger = new DataSetMerger(ds, theirDataSet);
+            merger.merge();
+            // a way loaded with MultiFetch may have incomplete nodes because at least one of its
+            // nodes isn't present in the local data set. We therefore fully load all
+            // ways with incomplete nodes.
+            //
+            for (Way w : ds.getWays()) {
+                if (canceled) return;
+                if (w.hasIncompleteNodes()) {
+                    synchronized(this) {
+                        if (canceled) return;
+                        objectReader = new OsmServerObjectReader(w.getId(), OsmPrimitiveType.WAY, true /* full */);
+                    }
+                    theirDataSet = objectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                    synchronized (this) {
+                        objectReader = null;
+                    }
+                    merger = new DataSetMerger(ds, theirDataSet);
+                    merger.merge();
+                }
+            }
+            
+        } catch(Exception e) {
+            if (canceled) return;
+            lastException = e;
+        }
+    }
+    
+    /**
+     * replies the set of ids of all primitives for which a fetch request to the
+     * server was submitted but which are not available from the server (the server
+     * replied a return code of 404)
+     *
+     * @return the set of ids of missing primitives
+     */
+    public Set<PrimitiveId> getMissingPrimitives() {
+        return missingPrimitives;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java	(revision 4080)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/OsmIdTextField.java	(revision 4081)
@@ -4,6 +4,14 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
 import javax.swing.JTextField;
 import javax.swing.text.JTextComponent;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 
 /**
@@ -18,7 +26,24 @@
     }
 
-    public int getOsmId() {
+    public void setType(OsmPrimitiveType type) {
+        validator.type = type;
+    }
+
+    public long getOsmId() {
         return validator.getOsmId();
     }
+
+    /**
+     * Get entered ID list - supports "1,2,3" "1 2   ,3" or even "1 2 3 v2 6 v8"
+     * @return list of id's
+     */
+    public List<PrimitiveId> getIds() {
+        return validator.ids;
+    }
+
+    public boolean readOsmIds() {
+        return validator.readOsmIds();
+    }
+
 
     /**
@@ -32,4 +57,7 @@
         }
 
+        private List<PrimitiveId> ids = new ArrayList<PrimitiveId>();
+        private OsmPrimitiveType type;
+
         public OsmIdValidator(JTextComponent tc) {
             super(tc, false);
@@ -38,5 +66,5 @@
         @Override
         public boolean isValid() {
-            return getOsmId() > 0;
+            return getOsmId() > 0 || readOsmIds() != false;
         }
 
@@ -50,10 +78,11 @@
         }
 
-        public int getOsmId() {
+        public long getOsmId() {
             String value  = getComponent().getText();
             if (value == null || value.trim().length() == 0) return 0;
             try {
-                int osmId = Integer.parseInt(value.trim());
-                if (osmId > 0) return osmId;
+                long osmId = Long.parseLong(value.trim());
+                if (osmId > 0) 
+                    return osmId;
                 return 0;
             } catch(NumberFormatException e) {
@@ -61,4 +90,39 @@
             }
         }
+        
+        public boolean readOsmIds() {
+            String value  = getComponent().getText();
+            char c;
+            if (value == null || value.trim().length() == 0) return false;
+            try {
+                ids.clear();
+                StringTokenizer st = new StringTokenizer(value,",.+/ \t\n");
+                String s;
+                while (st.hasMoreTokens()) {
+                    s = st.nextToken();
+                    // convert tokens to int skipping v-words (version v2 etc)
+                    c = s.charAt(0);
+                    if (c=='v') {
+                        continue;
+                    }
+                    else if (c=='n') {
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s.substring(1)), OsmPrimitiveType.NODE));
+                    } else if (c=='w') {
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s.substring(1)), OsmPrimitiveType.WAY));
+                    } else if (c=='r') { 
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s.substring(1)), OsmPrimitiveType.RELATION));
+                    } else if (type==OsmPrimitiveType.NODE) {
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s), OsmPrimitiveType.NODE));
+                    } else if (type==OsmPrimitiveType.WAY) {
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s), OsmPrimitiveType.WAY));
+                    } else if (type==OsmPrimitiveType.RELATION) {
+                        ids.add(new SimplePrimitiveId(Long.parseLong(s), OsmPrimitiveType.RELATION));
+                    }
+                }
+                return true;
+            } catch(NumberFormatException e) {
+                return false;
+            }
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/widgets/OsmPrimitiveTypesComboBox.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/OsmPrimitiveTypesComboBox.java	(revision 4080)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/OsmPrimitiveTypesComboBox.java	(revision 4081)
@@ -19,5 +19,9 @@
 
     public OsmPrimitiveType getType() {
-        return (OsmPrimitiveType)this.getSelectedItem();
+        try {
+            return (OsmPrimitiveType)this.getSelectedItem();
+        } catch (Exception e) {
+            return null;
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 4080)
+++ trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 4081)
@@ -10,4 +10,5 @@
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.NoSuchElementException;
 import java.util.Set;
@@ -22,4 +23,5 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
+import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
@@ -49,5 +51,5 @@
 public class MultiFetchServerObjectReader extends OsmServerReader{
 
-    static private Logger logger = Logger.getLogger(MultiFetchServerObjectReader.class.getName());
+    static final private Logger logger = Logger.getLogger(MultiFetchServerObjectReader.class.getName());
     /**
      * the max. number of primitives retrieved in one step. Assuming IDs with 7 digits,
@@ -59,8 +61,8 @@
     static private int MAX_IDS_PER_REQUEST = 200;
 
-    private HashSet<Long> nodes;
-    private HashSet<Long> ways;
-    private HashSet<Long> relations;
-    private HashSet<Long> missingPrimitives;
+    private Set<Long> nodes;
+    private Set<Long> ways;
+    private Set<Long> relations;
+    private Set<PrimitiveId> missingPrimitives;
     private DataSet outputDataSet;
 
@@ -70,9 +72,9 @@
      */
     public MultiFetchServerObjectReader() {
-        nodes = new HashSet<Long>();
-        ways = new HashSet<Long>();
-        relations = new HashSet<Long>();
+        nodes = new LinkedHashSet<Long>();
+        ways = new LinkedHashSet<Long>();
+        relations = new LinkedHashSet<Long>();
         this.outputDataSet = new DataSet();
-        this.missingPrimitives = new HashSet<Long>();
+        this.missingPrimitives = new LinkedHashSet<PrimitiveId>();
     }
 
@@ -117,24 +119,17 @@
     }
 
-    /**
-     * appends a {@see Node}s id to the list of ids which will be fetched from the server.
-     *
-     * @param node  the node (ignored, if null)
-     * @return this
-     *
-     */
     public MultiFetchServerObjectReader append(DataSet ds, long id, OsmPrimitiveType type) {
         switch(type) {
         case NODE:
             Node n = (Node)ds.getPrimitiveById(id,type);
-            append(n);
+            appendNode(n);
             break;
         case WAY:
             Way w= (Way)ds.getPrimitiveById(id,type);
-            append(w);
+            appendWay(w);
             break;
         case RELATION:
             Relation r = (Relation)ds.getPrimitiveById(id,type);
-            append(r);
+            appendRelation(r);
             break;
         }
@@ -149,5 +144,5 @@
      *
      */
-    public MultiFetchServerObjectReader append(Node node) {
+    public MultiFetchServerObjectReader appendNode(Node node) {
         if (node == null) return this;
         remember(node.getPrimitiveId());
@@ -162,5 +157,5 @@
      *
      */
-    public MultiFetchServerObjectReader append(Way way) {
+    public MultiFetchServerObjectReader appendWay(Way way) {
         if (way == null) return this;
         if (way.isNew()) return this;
@@ -181,5 +176,5 @@
      *
      */
-    public MultiFetchServerObjectReader append(Relation relation) {
+    protected MultiFetchServerObjectReader appendRelation(Relation relation) {
         if (relation == null) return this;
         if (relation.isNew()) return this;
@@ -194,17 +189,17 @@
             }
             if (!member.getMember().isIncomplete()) {
-                appendGeneric(member.getMember());
-            }
-        }
-        return this;
-    }
-
-    protected MultiFetchServerObjectReader appendGeneric(OsmPrimitive primitive) {
+                append(member.getMember());
+            }
+        }
+        return this;
+    }
+
+    public MultiFetchServerObjectReader append(OsmPrimitive primitive) {
         if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.NODE))
-            return append((Node)primitive);
+            return appendNode((Node)primitive);
         else if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.WAY))
-            return append((Way)primitive);
+            return appendWay((Way)primitive);
         else if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.RELATION))
-            return append((Relation)primitive);
+            return appendRelation((Relation)primitive);
 
         return this;
@@ -225,5 +220,5 @@
         if (primitives == null) return this;
         for (OsmPrimitive primitive : primitives) {
-            appendGeneric(primitive);
+            append(primitive);
         }
         return this;
@@ -367,5 +362,5 @@
                 if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                     logger.warning(tr("Server replied with response code 404 for id {0}. Skipping.", Long.toString(id)));
-                    missingPrimitives.add(id);
+                    missingPrimitives.add(new SimplePrimitiveId(id, type));
                     continue;
                 }
@@ -449,5 +444,5 @@
         progressMonitor.beginTask(trn("Downloading {0} object from ''{1}''", "Downloading {0} objects from ''{1}''", n, n, OsmApi.getOsmApi().getBaseUrl()));
         try {
-            missingPrimitives = new HashSet<Long>();
+            missingPrimitives = new HashSet<PrimitiveId>();
             if (isCanceled())return null;
             fetchPrimitives(ways,OsmPrimitiveType.WAY, progressMonitor);
@@ -472,5 +467,5 @@
      * @return the set of ids of missing primitives
      */
-    public Set<Long> getMissingPrimitives() {
+    public Set<PrimitiveId> getMissingPrimitives() {
         return missingPrimitives;
     }
Index: trunk/src/org/openstreetmap/josm/tools/I18n.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 4080)
+++ trunk/src/org/openstreetmap/josm/tools/I18n.java	(revision 4081)
@@ -177,5 +177,5 @@
 
     /**
-     * Example: trn("Found {0} error!", "Found {0} errors!", i, Integer.toString(i));
+     * Example: trn("Found {0} error in {1}!", "Found {0} errors in {1}!", i, Integer.toString(i), url);
      */
     public static final String trn(String text, String pluralText, long n, Object... objects) {
