Index: trunk/src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java	(revision 14944)
+++ trunk/src/org/openstreetmap/josm/data/osm/ChangesetDataSet.java	(revision 14946)
@@ -1,5 +1,7 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.data.osm;
-
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -7,11 +9,12 @@
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.Logging;
 
 /**
- * A ChangesetDataSet holds the content of a changeset.
+ * A ChangesetDataSet holds the content of a changeset. Typically, a primitive is modified only once in a changeset,
+ * but if there are multiple modifications, the first and last are kept. Further intermediate versions are not kept.
  */
 public class ChangesetDataSet {
@@ -47,6 +50,6 @@
     }
 
-    private final Map<PrimitiveId, HistoryOsmPrimitive> primitives = new HashMap<>();
-    private final Map<PrimitiveId, ChangesetModificationType> modificationTypes = new HashMap<>();
+    /** maps an id to either one {@link ChangesetDataSetEntry} or an array of {@link ChangesetDataSetEntry} */
+    private final Map<PrimitiveId, Object> entryMap = new HashMap<>();
 
     /**
@@ -57,10 +60,35 @@
      * @throws IllegalArgumentException if primitive is null
      * @throws IllegalArgumentException if cmt is null
+     * @throws IllegalArgumentException if the same primitive was already stored with a higher or equal version
      */
     public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) {
         CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
         CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
-        primitives.put(primitive.getPrimitiveId(), primitive);
-        modificationTypes.put(primitive.getPrimitiveId(), cmt);
+        DefaultChangesetDataSetEntry csEntry = new DefaultChangesetDataSetEntry(cmt, primitive);
+        Object val = entryMap.get(primitive.getPrimitiveId());
+        ChangesetDataSetEntry[] entries;
+        if (val == null) {
+            entryMap.put(primitive.getPrimitiveId(), csEntry);
+            return;
+        }
+        if (val instanceof ChangesetDataSetEntry) {
+            entries = new ChangesetDataSetEntry[2];
+            entries[0] = (ChangesetDataSetEntry) val;
+            if (primitive.getVersion() <= entries[0].getPrimitive().getVersion()) {
+                throw new IllegalArgumentException(
+                        tr("Changeset {0}: Unexpected order of versions for {1}: v{2} is not higher than v{3}",
+                                String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
+                                primitive.getVersion(), entries[0].getPrimitive().getVersion()));
+            }
+        } else {
+            entries = (ChangesetDataSetEntry[]) val;
+        }
+        if (entries[1] != null) {
+            Logging.info("Changeset {0}: Change of {1} v{2} is replaced by version v{3}",
+                    String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
+                    entries[1].getPrimitive().getVersion(), primitive.getVersion());
+        }
+        entries[1] = csEntry;
+        entryMap.put(primitive.getPrimitiveId(), entries);
     }
 
@@ -72,22 +100,22 @@
     public boolean contains(PrimitiveId id) {
         if (id == null) return false;
-        return primitives.containsKey(id);
-    }
-
-    /**
-     * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or
+        return entryMap.containsKey(id);
+    }
+
+    /**
+     * Replies the last modification type for the object with id <code>id</code>. Replies null, if id is null or
      * if the object with id <code>id</code> isn't in the changeset content.
      *
      * @param id the id
-     * @return the modification type
+     * @return the last modification type or null
      */
     public ChangesetModificationType getModificationType(PrimitiveId id) {
-        if (!contains(id)) return null;
-        return modificationTypes.get(id);
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null ? e.getModificationType() : null;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was created in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
@@ -96,11 +124,11 @@
      */
     public boolean isCreated(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.CREATED == getModificationType(id);
+        ChangesetDataSetEntry e = getFirstEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.CREATED;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was updated in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
@@ -109,11 +137,11 @@
      */
     public boolean isUpdated(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.UPDATED == getModificationType(id);
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.UPDATED;
     }
 
     /**
      * Replies true if the primitive with id <code>id</code> was deleted in this
-     * changeset. Replies false, if id is null.
+     * changeset. Replies false, if id is null or not in the dataset.
      *
      * @param id the id
@@ -122,47 +150,83 @@
      */
     public boolean isDeleted(PrimitiveId id) {
-        if (!contains(id)) return false;
-        return ChangesetModificationType.DELETED == getModificationType(id);
-    }
-
-    /**
-     * Replies the set of primitives with a specific modification type
-     *
-     * @param cmt the modification type. Must not be null.
-     * @return the set of primitives
-     * @throws IllegalArgumentException if cmt is null
-     */
-    public Set<HistoryOsmPrimitive> getPrimitivesByModificationType(ChangesetModificationType cmt) {
-        CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
-        return modificationTypes.entrySet().stream()
-                .filter(entry -> entry.getValue() == cmt)
-                .map(entry -> primitives.get(entry.getKey()))
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Replies the number of objects in the dataset
-     *
-     * @return the number of objects in the dataset
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null && e.getModificationType() == ChangesetModificationType.DELETED;
+    }
+
+    /**
+     * Replies the number of primitives in the dataset.
+     *
+     * @return the number of primitives in the dataset.
      */
     public int size() {
-        return primitives.size();
+        return entryMap.size();
     }
 
     /**
      * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
+     * null, if there is no such primitive in the data set. If the primitive was modified
+     * multiple times, the last version is returned.
+     *
+     * @param id the id
+     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
+     */
+    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
+        ChangesetDataSetEntry e = getLastEntry(id);
+        return e != null ? e.getPrimitive() : null;
+    }
+
+    /**
+     * @return an unmodifiable set of all primitives in this dataset.
+     * @since 14946
+     */
+    public Set<PrimitiveId> getIds() {
+        return Collections.unmodifiableSet(entryMap.keySet());
+    }
+
+    /**
+     * Replies the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
      * null, if there is no such primitive in the data set.
-     *
-     * @param id the id
-     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
-     */
-    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
-        if (id == null) return null;
-        return primitives.get(id);
-    }
-
-    /**
-     * Returns an iterator over dataset entries.
-     * @return an iterator over dataset entries
+     * @param id the id
+     * @return the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
+     * @since 14946
+     */
+    public ChangesetDataSetEntry getFirstEntry(PrimitiveId id) {
+        if (id == null)
+            return null;
+        Object val = entryMap.get(id);
+        if (val == null)
+            return null;
+        if (val instanceof ChangesetDataSetEntry[]) {
+            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+            return entries[0];
+        } else {
+            return (ChangesetDataSetEntry) val;
+        }
+    }
+
+    /**
+     * Replies the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
+     * null, if there is no such primitive in the data set.
+     * @param id the id
+     * @return the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
+     * @since 14946
+     */
+    public ChangesetDataSetEntry getLastEntry(PrimitiveId id) {
+        if (id == null)
+            return null;
+        Object val = entryMap.get(id);
+        if (val == null)
+            return null;
+        if (val instanceof ChangesetDataSetEntry[]) {
+            ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+            return entries[1];
+        } else {
+            return (ChangesetDataSetEntry) val;
+        }
+    }
+
+    /**
+     * Returns an iterator over dataset entries. The elements are returned in no particular order.
+     * @return an iterator over dataset entries. If a primitive was changed multiple times, only the last entry is returned.
      */
     public Iterator<ChangesetDataSetEntry> iterator() {
@@ -170,9 +234,17 @@
     }
 
-    private static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
+    /**
+     * Class to keep one entry of a changeset: the combination of modification type and primitive.
+     */
+    public static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
         private final ChangesetModificationType modificationType;
         private final HistoryOsmPrimitive primitive;
 
-        DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
+        /**
+         * Construct new entry.
+         * @param modificationType the modification type
+         * @param primitive the primitive
+         */
+        public DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
             this.modificationType = modificationType;
             this.primitive = primitive;
@@ -188,11 +260,16 @@
             return primitive;
         }
+
+        @Override
+        public String toString() {
+            return modificationType.toString() + " " + primitive.toString();
+        }
     }
 
     private class DefaultIterator implements Iterator<ChangesetDataSetEntry> {
-        private final Iterator<Entry<PrimitiveId, ChangesetModificationType>> typeIterator;
+        private final Iterator<Entry<PrimitiveId, Object>> typeIterator;
 
         DefaultIterator() {
-            typeIterator = modificationTypes.entrySet().iterator();
+            typeIterator = entryMap.entrySet().iterator();
         }
 
@@ -204,6 +281,15 @@
         @Override
         public ChangesetDataSetEntry next() {
-            Entry<PrimitiveId, ChangesetModificationType> next = typeIterator.next();
-            return new DefaultChangesetDataSetEntry(next.getValue(), primitives.get(next.getKey()));
+            Entry<PrimitiveId, Object> next = typeIterator.next();
+            // get last entry
+            Object val = next.getValue();
+            ChangesetDataSetEntry last;
+            if (val instanceof ChangesetDataSetEntry[]) {
+                ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
+                last = entries[1];
+            } else {
+                last = (ChangesetDataSetEntry) val;
+            }
+            return new DefaultChangesetDataSetEntry(last.getModificationType(), last.getPrimitive());
         }
 
Index: trunk/src/org/openstreetmap/josm/io/AbstractParser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/AbstractParser.java	(revision 14944)
+++ trunk/src/org/openstreetmap/josm/io/AbstractParser.java	(revision 14946)
@@ -29,4 +29,6 @@
     protected HistoryOsmPrimitive currentPrimitive;
     protected Locator locator;
+    /** if true, replace user information in input by anonymous user */
+    protected boolean useAnonymousUser;
 
     @Override
@@ -112,15 +114,18 @@
         boolean visible = getMandatoryAttributeBoolean(atts, "visible");
 
-        Long uid = getAttributeLong(atts, "uid");
-        String userStr = atts.getValue("user");
-        User user;
-        if (userStr != null) {
-            if (uid != null) {
-                user = User.createOsmUser(uid, userStr);
-                user.setPreferredName(userStr);
-            } else {
-                user = User.createLocalUser(userStr);
+        User user = null;
+        if (!useAnonymousUser) {
+            Long uid = getAttributeLong(atts, "uid");
+            String userStr = atts.getValue("user");
+            if (userStr != null) {
+                if (uid != null) {
+                    user = User.createOsmUser(uid, userStr);
+                    user.setPreferredName(userStr);
+                } else {
+                    user = User.createLocalUser(userStr);
+                }
             }
-        } else {
+        }
+        if (user == null) {
             user = User.getAnonymous();
         }
Index: trunk/src/org/openstreetmap/josm/io/OsmChangesetContentParser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmChangesetContentParser.java	(revision 14944)
+++ trunk/src/org/openstreetmap/josm/io/OsmChangesetContentParser.java	(revision 14946)
@@ -35,4 +35,7 @@
 
     private class Parser extends AbstractParser {
+        Parser(boolean useAnonymousUser) {
+            this.useAnonymousUser = useAnonymousUser;
+        }
 
         /** the current change modification type */
@@ -121,5 +124,4 @@
      * @throws IllegalArgumentException if source is {@code null}.
      */
-    @SuppressWarnings("resource")
     public OsmChangesetContentParser(InputStream source) {
         CheckParameterUtil.ensureParameterNotNull(source, "source");
@@ -147,4 +149,18 @@
      */
     public ChangesetDataSet parse(ProgressMonitor progressMonitor) throws XmlParsingException {
+        return parse(progressMonitor, false);
+    }
+
+    /**
+     * Parses the content.
+     *
+     * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null
+     * @param useAnonymousUser if true, replace all user information with the anonymous user
+     * @return the parsed data
+     * @throws XmlParsingException if something went wrong. Check for chained
+     * exceptions.
+     * @since 14946
+     */
+    public ChangesetDataSet parse(ProgressMonitor progressMonitor, boolean useAnonymousUser) throws XmlParsingException {
         if (progressMonitor == null) {
             progressMonitor = NullProgressMonitor.INSTANCE;
@@ -153,5 +169,5 @@
             progressMonitor.beginTask("");
             progressMonitor.indeterminateSubTask(tr("Parsing changeset content ..."));
-            XmlUtils.parseSafeSAX(source, new Parser());
+            XmlUtils.parseSafeSAX(source, new Parser(useAnonymousUser));
         } catch (XmlParsingException e) {
             throw e;
@@ -172,5 +188,5 @@
      */
     public ChangesetDataSet parse() throws XmlParsingException {
-        return parse(null);
+        return parse(null, false);
     }
 }
Index: trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 14944)
+++ trunk/src/org/openstreetmap/josm/io/OsmServerChangesetReader.java	(revision 14946)
@@ -27,4 +27,22 @@
  */
 public class OsmServerChangesetReader extends OsmServerReader {
+    final boolean useAnonymousUser;
+
+    /**
+     * Constructs a new {@code OsmServerChangesetReader} with default settings.
+     */
+    public OsmServerChangesetReader() {
+        this(false);
+    }
+
+    /**
+     * Constructs a new {@code OsmServerChangesetReader}
+     * @param useAnonymousUser if true, replace all user information with the anonymous user
+     * @since 14946
+     */
+    public OsmServerChangesetReader(boolean useAnonymousUser) {
+        super();
+        this.useAnonymousUser = useAnonymousUser;
+    }
 
     /**
@@ -199,5 +217,5 @@
                 monitor.setCustomText(tr("Downloading content for changeset {0} ...", id));
                 OsmChangesetContentParser parser = new OsmChangesetContentParser(in);
-                result = parser.parse(monitor.createSubTaskMonitor(1, true));
+                result = parser.parse(monitor.createSubTaskMonitor(1, true), useAnonymousUser);
             } catch (IOException e) {
                 Logging.warn(e);
