Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 12048)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 12049)
@@ -55,5 +55,4 @@
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
-import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.ListenerList;
 import org.openstreetmap.josm.tools.SubclassFilteredCollection;
@@ -104,5 +103,5 @@
  * @author imi
  */
-public final class DataSet implements Data, ProjectionChangeListener {
+public final class DataSet extends QuadBucketPrimitiveStore implements Data, ProjectionChangeListener {
 
     /**
@@ -216,10 +215,10 @@
         try {
             Map<OsmPrimitive, OsmPrimitive> primMap = new HashMap<>();
-            for (Node n : copyFrom.nodes) {
+            for (Node n : copyFrom.getNodes()) {
                 Node newNode = new Node(n);
                 primMap.put(n, newNode);
                 addPrimitive(newNode);
             }
-            for (Way w : copyFrom.ways) {
+            for (Way w : copyFrom.getWays()) {
                 Way newWay = new Way(w);
                 primMap.put(w, newWay);
@@ -233,5 +232,6 @@
             // Because relations can have other relations as members we first clone all relations
             // and then get the cloned members
-            for (Relation r : copyFrom.relations) {
+            Collection<Relation> relations = copyFrom.getRelations();
+            for (Relation r : relations) {
                 Relation newRelation = new Relation(r, r.isNew());
                 newRelation.setMembers(null);
@@ -239,5 +239,5 @@
                 addPrimitive(newRelation);
             }
-            for (Relation r : copyFrom.relations) {
+            for (Relation r : relations) {
                 Relation newRelation = (Relation) primMap.get(r);
                 List<RelationMember> newMembers = new ArrayList<>();
@@ -418,10 +418,4 @@
 
     /**
-     * All nodes goes here, even when included in other data (ways etc). This enables the instant
-     * conversion of the whole DataSet by iterating over this data structure.
-     */
-    private final QuadBuckets<Node> nodes = new QuadBuckets<>();
-
-    /**
      * Gets a filtered collection of primitives matching the given predicate.
      * @param <T> The primitive type.
@@ -443,13 +437,9 @@
     }
 
-    /**
-     * Searches for nodes in the given bounding box.
-     * @param bbox the bounding box
-     * @return List of nodes in the given bbox. Can be empty but not null
-     */
+    @Override
     public List<Node> searchNodes(BBox bbox) {
         lock.readLock().lock();
         try {
-            return nodes.search(bbox);
+            return super.searchNodes(bbox);
         } finally {
             lock.readLock().unlock();
@@ -458,23 +448,4 @@
 
     /**
-     * Determines if the given node can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
-     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
-     *
-     * @param n The node to search
-     * @return {@code true} if {@code n} ban be retrieved in this data set, {@code false} otherwise
-     * @since 7501
-     */
-    public boolean containsNode(Node n) {
-        return nodes.contains(n);
-    }
-
-    /**
-     * All ways (Streets etc.) in the DataSet.
-     *
-     * The way nodes are stored only in the way list.
-     */
-    private final QuadBuckets<Way> ways = new QuadBuckets<>();
-
-    /**
      * Replies an unmodifiable collection of ways in this dataset
      *
@@ -485,42 +456,12 @@
     }
 
-    /**
-     * Searches for ways in the given bounding box.
-     * @param bbox the bounding box
-     * @return List of ways in the given bbox. Can be empty but not null
-     */
+    @Override
     public List<Way> searchWays(BBox bbox) {
         lock.readLock().lock();
         try {
-            return ways.search(bbox);
+            return super.searchWays(bbox);
         } finally {
             lock.readLock().unlock();
         }
-    }
-
-    /**
-     * Determines if the given way can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
-     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
-     *
-     * @param w The way to search
-     * @return {@code true} if {@code w} ban be retrieved in this data set, {@code false} otherwise
-     * @since 7501
-     */
-    public boolean containsWay(Way w) {
-        return ways.contains(w);
-    }
-
-    /**
-     * All relations/relationships
-     */
-    private final Collection<Relation> relations = new ArrayList<>();
-
-    /**
-     * Replies an unmodifiable collection of relations in this dataset
-     *
-     * @return an unmodifiable collection of relations in this dataset
-     */
-    public Collection<Relation> getRelations() {
-        return getPrimitives(Relation.class::isInstance);
     }
 
@@ -530,11 +471,9 @@
      * @return List of relations in the given bbox. Can be empty but not null
      */
+    @Override
     public List<Relation> searchRelations(BBox bbox) {
         lock.readLock().lock();
         try {
-            // QuadBuckets might be useful here (don't forget to do reindexing after some of rm is changed)
-            return relations.stream()
-                    .filter(r -> r.getBBox().intersects(bbox))
-                    .collect(Collectors.toList());
+            return super.searchRelations(bbox);
         } finally {
             lock.readLock().unlock();
@@ -543,13 +482,10 @@
 
     /**
-     * Determines if the given relation can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
-     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
-     *
-     * @param r The relation to search
-     * @return {@code true} if {@code r} ban be retrieved in this data set, {@code false} otherwise
-     * @since 7501
-     */
-    public boolean containsRelation(Relation r) {
-        return relations.contains(r);
+     * Replies an unmodifiable collection of relations in this dataset
+     *
+     * @return an unmodifiable collection of relations in this dataset
+     */
+    public Collection<Relation> getRelations() {
+        return getPrimitives(Relation.class::isInstance);
     }
 
@@ -605,4 +541,5 @@
      * @param primitive the primitive.
      */
+    @Override
     public void addPrimitive(OsmPrimitive primitive) {
         Objects.requireNonNull(primitive, "primitive");
@@ -616,14 +553,5 @@
             primitive.setDataset(this);
             primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reindexRelation to work properly)
-            boolean success = false;
-            if (primitive instanceof Node) {
-                success = nodes.add((Node) primitive);
-            } else if (primitive instanceof Way) {
-                success = ways.add((Way) primitive);
-            } else if (primitive instanceof Relation) {
-                success = relations.add((Relation) primitive);
-            }
-            if (!success)
-                throw new JosmRuntimeException("failed to add primitive: "+primitive);
+            super.addPrimitive(primitive);
             firePrimitivesAdded(Collections.singletonList(primitive), false);
         } finally {
@@ -647,14 +575,5 @@
             if (primitive == null)
                 return;
-            boolean success = false;
-            if (primitive instanceof Node) {
-                success = nodes.remove(primitive);
-            } else if (primitive instanceof Way) {
-                success = ways.remove(primitive);
-            } else if (primitive instanceof Relation) {
-                success = relations.remove(primitive);
-            }
-            if (!success)
-                throw new JosmRuntimeException("failed to remove primitive: "+primitive);
+            super.removePrimitive(primitive);
             clearSelection(primitiveId);
             allPrimitives.remove(primitive);
@@ -1124,5 +1043,5 @@
         beginUpdate();
         try {
-            for (Relation relation : relations) {
+            for (Relation relation : getRelations()) {
                 List<RelationMember> members = relation.getMembers();
 
@@ -1181,43 +1100,4 @@
         }
         return false;
-    }
-
-    private void reindexNode(Node node, LatLon newCoor, EastNorth eastNorth) {
-        if (!nodes.remove(node))
-            throw new JosmRuntimeException("Reindexing node failed to remove");
-        node.setCoorInternal(newCoor, eastNorth);
-        if (!nodes.add(node))
-            throw new JosmRuntimeException("Reindexing node failed to add");
-        for (OsmPrimitive primitive: node.getReferrers()) {
-            if (primitive instanceof Way) {
-                reindexWay((Way) primitive);
-            } else {
-                reindexRelation((Relation) primitive);
-            }
-        }
-    }
-
-    private void reindexWay(Way way) {
-        BBox before = way.getBBox();
-        if (!ways.remove(way))
-            throw new JosmRuntimeException("Reindexing way failed to remove");
-        way.updatePosition();
-        if (!ways.add(way))
-            throw new JosmRuntimeException("Reindexing way failed to add");
-        if (!way.getBBox().equals(before)) {
-            for (OsmPrimitive primitive: way.getReferrers()) {
-                reindexRelation((Relation) primitive);
-            }
-        }
-    }
-
-    private static void reindexRelation(Relation relation) {
-        BBox before = relation.getBBox();
-        relation.updatePosition();
-        if (!before.equals(relation.getBBox())) {
-            for (OsmPrimitive primitive: relation.getReferrers()) {
-                reindexRelation((Relation) primitive);
-            }
-        }
     }
 
@@ -1372,5 +1252,5 @@
         try {
             cleanupDeleted(Stream.concat(
-                    nodes.stream(), Stream.concat(ways.stream(), relations.stream())));
+                    getNodes().stream(), Stream.concat(getNodes().stream(), getNodes().stream())));
         } finally {
             endUpdate();
@@ -1379,8 +1259,8 @@
 
     private void cleanupDeleted(Stream<? extends OsmPrimitive> it) {
-        clearSelection(it
-                .filter(primitive -> primitive.isDeleted() && (!primitive.isVisible() || primitive.isNew()))
-                .peek(allPrimitives::remove)
-                .peek(primitive -> primitive.setDataset(null)));
+        it.filter(primitive -> primitive.isDeleted() && (!primitive.isVisible() || primitive.isNew()))
+                .collect(Collectors.toList())
+                .forEach(primitive -> this.removePrimitive(primitive.getPrimitiveId()));
+
     }
 
@@ -1388,6 +1268,6 @@
      * Removes all primitives from the dataset and resets the currently selected primitives
      * to the empty collection. Also notifies selection change listeners if necessary.
-     *
-     */
+     */
+    @Override
     public void clear() {
         beginUpdate();
@@ -1397,7 +1277,5 @@
                 primitive.setDataset(null);
             }
-            nodes.clear();
-            ways.clear();
-            relations.clear();
+            super.clear();
             allPrimitives.clear();
         } finally {
Index: trunk/src/org/openstreetmap/josm/data/osm/QuadBucketPrimitiveStore.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/QuadBucketPrimitiveStore.java	(revision 12049)
+++ trunk/src/org/openstreetmap/josm/data/osm/QuadBucketPrimitiveStore.java	(revision 12049)
@@ -0,0 +1,200 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.osm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.tools.JosmRuntimeException;
+
+/**
+ * Stores primitives in quad buckets. This can be used to hold a collection of primitives, e.g. in a {@link DataSet}
+ *
+ * This class does not do any synchronization.
+ * @author Michael Zangl
+ * @since 12048
+ */
+public class QuadBucketPrimitiveStore {
+    /**
+     * All nodes goes here, even when included in other data (ways etc). This enables the instant
+     * conversion of the whole DataSet by iterating over this data structure.
+     */
+    private final QuadBuckets<Node> nodes = new QuadBuckets<>();
+
+    /**
+     * All ways (Streets etc.) in the DataSet.
+     *
+     * The way nodes are stored only in the way list.
+     */
+    private final QuadBuckets<Way> ways = new QuadBuckets<>();
+
+    /**
+     * All relations/relationships
+     */
+    private final Collection<Relation> relations = new ArrayList<>();
+
+    /**
+     * Searches for nodes in the given bounding box.
+     * @param bbox the bounding box
+     * @return List of nodes in the given bbox. Can be empty but not null
+     */
+    public List<Node> searchNodes(BBox bbox) {
+        return nodes.search(bbox);
+    }
+
+    /**
+     * Determines if the given node can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
+     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
+     *
+     * @param n The node to search
+     * @return {@code true} if {@code n} ban be retrieved in this data set, {@code false} otherwise
+     * @since 7501
+     */
+    public boolean containsNode(Node n) {
+        return nodes.contains(n);
+    }
+
+    /**
+     * Searches for ways in the given bounding box.
+     * @param bbox the bounding box
+     * @return List of ways in the given bbox. Can be empty but not null
+     */
+    public List<Way> searchWays(BBox bbox) {
+        return ways.search(bbox);
+    }
+
+    /**
+     * Determines if the given way can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
+     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
+     *
+     * @param w The way to search
+     * @return {@code true} if {@code w} ban be retrieved in this data set, {@code false} otherwise
+     * @since 7501
+     */
+    public boolean containsWay(Way w) {
+        return ways.contains(w);
+    }
+
+    /**
+     * Searches for relations in the given bounding box.
+     * @param bbox the bounding box
+     * @return List of relations in the given bbox. Can be empty but not null
+     */
+    public List<Relation> searchRelations(BBox bbox) {
+        // QuadBuckets might be useful here (don't forget to do reindexing after some of rm is changed)
+        return relations.stream()
+                .filter(r -> r.getBBox().intersects(bbox))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Determines if the given relation can be retrieved in the data set through its bounding box. Useful for dataset consistency test.
+     * For efficiency reasons this method does not lock the dataset, you have to lock it manually.
+     *
+     * @param r The relation to search
+     * @return {@code true} if {@code r} ban be retrieved in this data set, {@code false} otherwise
+     * @since 7501
+     */
+    public boolean containsRelation(Relation r) {
+        return relations.contains(r);
+    }
+
+    /**
+     * Adds a primitive to this quad bucket store
+     *
+     * @param primitive the primitive.
+     */
+    public void addPrimitive(OsmPrimitive primitive) {
+        boolean success = false;
+        if (primitive instanceof Node) {
+            success = nodes.add((Node) primitive);
+        } else if (primitive instanceof Way) {
+            success = ways.add((Way) primitive);
+        } else if (primitive instanceof Relation) {
+            success = relations.add((Relation) primitive);
+        }
+        if (!success) {
+            throw new JosmRuntimeException("failed to add primitive: "+primitive);
+        }
+    }
+
+    protected void removePrimitive(OsmPrimitive primitive) {
+        boolean success = false;
+        if (primitive instanceof Node) {
+            success = nodes.remove(primitive);
+        } else if (primitive instanceof Way) {
+            success = ways.remove(primitive);
+        } else if (primitive instanceof Relation) {
+            success = relations.remove(primitive);
+        }
+        if (!success) {
+            throw new JosmRuntimeException("failed to remove primitive: "+primitive);
+        }
+    }
+
+    /**
+     * Re-index the relation after it's position was changed.
+     * @param node The node to re-index
+     * @param newCoor The new coordinates
+     * @param eastNorth The new east/north position
+     */
+    protected void reindexNode(Node node, LatLon newCoor, EastNorth eastNorth) {
+        if (!nodes.remove(node))
+            throw new JosmRuntimeException("Reindexing node failed to remove");
+        node.setCoorInternal(newCoor, eastNorth);
+        if (!nodes.add(node))
+            throw new JosmRuntimeException("Reindexing node failed to add");
+        for (OsmPrimitive primitive: node.getReferrers()) {
+            if (primitive instanceof Way) {
+                reindexWay((Way) primitive);
+            } else {
+                reindexRelation((Relation) primitive);
+            }
+        }
+    }
+
+    /**
+     * Re-index the way after it's position was changed.
+     * @param way The way to re-index
+     */
+    protected void reindexWay(Way way) {
+        BBox before = way.getBBox();
+        if (!ways.remove(way))
+            throw new JosmRuntimeException("Reindexing way failed to remove");
+        way.updatePosition();
+        if (!ways.add(way))
+            throw new JosmRuntimeException("Reindexing way failed to add");
+        if (!way.getBBox().equals(before)) {
+            for (OsmPrimitive primitive: way.getReferrers()) {
+                reindexRelation((Relation) primitive);
+            }
+        }
+    }
+
+    /**
+     * Re-index the relation after it's position was changed.
+     * @param relation The relation to re-index
+     */
+    protected static void reindexRelation(Relation relation) {
+        BBox before = relation.getBBox();
+        relation.updatePosition();
+        if (!before.equals(relation.getBBox())) {
+            for (OsmPrimitive primitive: relation.getReferrers()) {
+                reindexRelation((Relation) primitive);
+            }
+        }
+    }
+
+
+    /**
+     * Removes all primitives from the this store.
+     */
+    public void clear() {
+        nodes.clear();
+        ways.clear();
+        relations.clear();
+    }
+}
