diff --git a/src/org/openstreetmap/josm/actions/PasteAction.java b/src/org/openstreetmap/josm/actions/PasteAction.java
index 6030f29..2b1ba60 100644
--- a/src/org/openstreetmap/josm/actions/PasteAction.java
+++ b/src/org/openstreetmap/josm/actions/PasteAction.java
@@ -9,24 +9,12 @@ import java.awt.MouseInfo;
 import java.awt.Point;
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.command.AddPrimitivesCommand;
 import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.osm.NodeData;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
-import org.openstreetmap.josm.data.osm.PrimitiveData;
 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
-import org.openstreetmap.josm.data.osm.RelationData;
-import org.openstreetmap.josm.data.osm.RelationMemberData;
-import org.openstreetmap.josm.data.osm.WayData;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
 import org.openstreetmap.josm.tools.Shortcut;
 
 /**
@@ -35,6 +23,8 @@ import org.openstreetmap.josm.tools.Shortcut;
  */
 public final class PasteAction extends JosmAction implements PasteBufferChangedListener {
 
+    private final OsmTransferHandler transferHandler;
+
     /**
      * Constructs a new {@code PasteAction}.
      */
@@ -46,58 +36,11 @@ public final class PasteAction extends JosmAction implements PasteBufferChangedL
         Main.registerActionShortcut(this,
                 Shortcut.registerShortcut("system:paste:cua", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_INSERT, Shortcut.SHIFT));
         Main.pasteBuffer.addPasteBufferChangedListener(this);
+        transferHandler = new OsmTransferHandler();
     }
 
     @Override
     public void actionPerformed(ActionEvent e) {
-        if (!isEnabled())
-            return;
-        pasteData(Main.pasteBuffer, Main.pasteSource, e);
-    }
-
-    /**
-     * Paste OSM primitives from the given paste buffer and OSM data layer source to the current edit layer.
-     * @param pasteBuffer The paste buffer containing primitive ids to copy
-     * @param source The OSM data layer used to look for primitive ids
-     * @param e The ActionEvent that triggered this operation
-     */
-    public void pasteData(PrimitiveDeepCopy pasteBuffer, Layer source, ActionEvent e) {
-        /* Find the middle of the pasteBuffer area */
-        double maxEast = -1E100;
-        double minEast = 1E100;
-        double maxNorth = -1E100;
-        double minNorth = 1E100;
-        boolean incomplete = false;
-        for (PrimitiveData data : pasteBuffer.getAll()) {
-            if (data instanceof NodeData) {
-                NodeData n = (NodeData) data;
-                if (n.getEastNorth() != null) {
-                    double east = n.getEastNorth().east();
-                    double north = n.getEastNorth().north();
-                    if (east > maxEast) {
-                        maxEast = east;
-                    }
-                    if (east < minEast) {
-                        minEast = east;
-                    }
-                    if (north > maxNorth) {
-                        maxNorth = north;
-                    }
-                    if (north < minNorth) {
-                        minNorth = north;
-                    }
-                }
-            }
-            if (data.isIncomplete()) {
-                incomplete = true;
-            }
-        }
-
-        // Allow to cancel paste if there are incomplete primitives
-        if (incomplete && !confirmDeleteIncomplete()) {
-            return;
-        }
-
         // default to paste in center of map (pasted via menu or cursor not in MapView)
         EastNorth mPosition = Main.map.mapView.getCenter();
         // We previously checked for modifier to know if the action has been trigerred via shortcut or via menu
@@ -112,90 +55,7 @@ public final class PasteAction extends JosmAction implements PasteBufferChangedL
             }
         }
 
-        double offsetEast  = mPosition.east() - (maxEast + minEast)/2.0;
-        double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
-
-        // Make a copy of pasteBuffer and map from old id to copied data id
-        List<PrimitiveData> bufferCopy = new ArrayList<>();
-        List<PrimitiveData> toSelect = new ArrayList<>();
-        Map<Long, Long> newNodeIds = new HashMap<>();
-        Map<Long, Long> newWayIds = new HashMap<>();
-        Map<Long, Long> newRelationIds = new HashMap<>();
-        for (PrimitiveData data: pasteBuffer.getAll()) {
-            if (data.isIncomplete()) {
-                continue;
-            }
-            PrimitiveData copy = data.makeCopy();
-            copy.clearOsmMetadata();
-            if (data instanceof NodeData) {
-                newNodeIds.put(data.getUniqueId(), copy.getUniqueId());
-            } else if (data instanceof WayData) {
-                newWayIds.put(data.getUniqueId(), copy.getUniqueId());
-            } else if (data instanceof RelationData) {
-                newRelationIds.put(data.getUniqueId(), copy.getUniqueId());
-            }
-            bufferCopy.add(copy);
-            if (pasteBuffer.getDirectlyAdded().contains(data)) {
-                toSelect.add(copy);
-            }
-        }
-
-        // Update references in copied buffer
-        for (PrimitiveData data:bufferCopy) {
-            if (data instanceof NodeData) {
-                NodeData nodeData = (NodeData) data;
-                if (Main.main.getEditLayer() == source) {
-                    nodeData.setEastNorth(nodeData.getEastNorth().add(offsetEast, offsetNorth));
-                }
-            } else if (data instanceof WayData) {
-                List<Long> newNodes = new ArrayList<>();
-                for (Long oldNodeId: ((WayData) data).getNodes()) {
-                    Long newNodeId = newNodeIds.get(oldNodeId);
-                    if (newNodeId != null) {
-                        newNodes.add(newNodeId);
-                    }
-                }
-                ((WayData) data).setNodes(newNodes);
-            } else if (data instanceof RelationData) {
-                List<RelationMemberData> newMembers = new ArrayList<>();
-                for (RelationMemberData member: ((RelationData) data).getMembers()) {
-                    OsmPrimitiveType memberType = member.getMemberType();
-                    Long newId;
-                    switch (memberType) {
-                    case NODE:
-                        newId = newNodeIds.get(member.getMemberId());
-                        break;
-                    case WAY:
-                        newId = newWayIds.get(member.getMemberId());
-                        break;
-                    case RELATION:
-                        newId = newRelationIds.get(member.getMemberId());
-                        break;
-                    default: throw new AssertionError();
-                    }
-                    if (newId != null) {
-                        newMembers.add(new RelationMemberData(member.getRole(), memberType, newId));
-                    }
-                }
-                ((RelationData) data).setMembers(newMembers);
-            }
-        }
-
-        /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
-        Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect));
-        Main.map.mapView.repaint();
-    }
-
-    private static boolean confirmDeleteIncomplete() {
-        ExtendedDialog ed = new ExtendedDialog(Main.parent,
-                tr("Delete incomplete members?"),
-                new String[] {tr("Paste without incomplete members"), tr("Cancel")});
-        ed.setButtonIcons(new String[] {"dialogs/relation/deletemembers", "cancel"});
-        ed.setContent(tr("The copied data contains incomplete objects.  "
-                + "When pasting the incomplete objects are removed.  "
-                + "Do you want to paste the data without the incomplete objects?"));
-        ed.showDialog();
-        return ed.getValue() == 1;
+        transferHandler.pasteOn(Main.getLayerManager().getEditLayer(), mPosition);
     }
 
     @Override
diff --git a/src/org/openstreetmap/josm/data/osm/PrimitiveData.java b/src/org/openstreetmap/josm/data/osm/PrimitiveData.java
index 5664dd3..693d277 100644
--- a/src/org/openstreetmap/josm/data/osm/PrimitiveData.java
+++ b/src/org/openstreetmap/josm/data/osm/PrimitiveData.java
@@ -80,6 +80,7 @@ public abstract class PrimitiveData extends AbstractPrimitive implements Seriali
         oos.writeInt(version);
         oos.writeInt(changesetId);
         oos.writeInt(timestamp);
+        oos.writeObject(keys);
         oos.defaultWriteObject();
     }
 
@@ -91,6 +92,7 @@ public abstract class PrimitiveData extends AbstractPrimitive implements Seriali
         version = ois.readInt();
         changesetId = ois.readInt();
         timestamp = ois.readInt();
+        keys = (String[]) ois.readObject();
         ois.defaultReadObject();
     }
 }
diff --git a/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java b/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java
index 03db280..010677b 100644
--- a/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java
+++ b/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java
@@ -9,6 +9,7 @@ import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
+import org.openstreetmap.josm.gui.datatransfer.PasteBufferCompatibilityHelper;
 
 /**
  * This class allows to create and keep a deep copy of primitives. Provides methods to access directly added
@@ -29,7 +30,8 @@ public class PrimitiveDeepCopy {
      * Constructs a new {@code PrimitiveDeepCopy} without data. Use {@link #makeCopy(Collection)} after that.
      */
     public PrimitiveDeepCopy() {
-        // Do nothing
+        // Send everything to Swing CCP
+        listeners.add(new PasteBufferCompatibilityHelper());
     }
 
     /**
@@ -97,14 +99,28 @@ public class PrimitiveDeepCopy {
         firePasteBufferChanged();
     }
 
+    /**
+     * Gets the list of primitives that were explicitly added to this copy.
+     * @return The added primitives
+     */
     public List<PrimitiveData> getDirectlyAdded() {
         return directlyAdded;
     }
 
+    /**
+     * Gets the list of primitives that were implicitly added because they were referenced.
+     * @return The primitives
+     */
     public List<PrimitiveData> getReferenced() {
         return referenced;
     }
 
+    /**
+     * Gets a list of all primitives in this copy.
+     * @return The primitives
+     * @see #getDirectlyAdded()
+     * @see #getReferenced()
+     */
     public List<PrimitiveData> getAll() {
         List<PrimitiveData> result = new ArrayList<>(directlyAdded.size() + referenced.size());
         result.addAll(directlyAdded);
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index 62030d9..0a74071 100644
--- a/src/org/openstreetmap/josm/gui/MapFrame.java
+++ b/src/org/openstreetmap/josm/gui/MapFrame.java
@@ -8,7 +8,6 @@ import java.awt.Component;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Font;
-import java.awt.GraphicsEnvironment;
 import java.awt.GridBagLayout;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
@@ -197,9 +196,6 @@ public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi
         setLayout(new BorderLayout());
 
         mapView = new MapView(Main.getLayerManager(), contentPane, viewportData);
-        if (!GraphicsEnvironment.isHeadless()) {
-            new FileDrop(mapView);
-        }
 
         splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
 
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index 5f4df08..f93608b 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -51,6 +51,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
 import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
+import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
 import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
@@ -532,6 +533,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         for (JComponent c : getMapNavigationComponents(MapView.this)) {
             add(c);
         }
+        setTransferHandler(new OsmTransferHandler());
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java b/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java
new file mode 100644
index 0000000..5424349
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java
@@ -0,0 +1,242 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.datatransfer;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.TransferHandler;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.OpenFileAction;
+import org.openstreetmap.josm.command.AddPrimitivesCommand;
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.NodeData;
+import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.data.osm.PrimitiveData;
+import org.openstreetmap.josm.data.osm.RelationData;
+import org.openstreetmap.josm.data.osm.RelationMemberData;
+import org.openstreetmap.josm.data.osm.WayData;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable.Data;
+import org.openstreetmap.josm.gui.layer.OsmDataLayer;
+
+/**
+ * This transfer hanlder provides the ability to transfer OSM data. It allows you to receive files and {@link Data} objects.
+ * @author Michael Zangl
+ */
+public class OsmTransferHandler extends TransferHandler {
+
+    private abstract static class AbstractDataFlavorSupport {
+        protected final DataFlavor df;
+
+        AbstractDataFlavorSupport(DataFlavor df) {
+            this.df = df;
+        }
+
+        protected boolean supports(TransferSupport support) {
+            return support.isDataFlavorSupported(df) && isCopy(support);
+        }
+
+        private boolean isCopy(TransferSupport support) {
+            return !support.isDrop() || (COPY & support.getSourceDropActions()) == COPY;
+        }
+
+        protected abstract boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt)
+                throws UnsupportedFlavorException, IOException;
+    }
+
+    private static Collection<AbstractDataFlavorSupport> SUPPORTED = Arrays
+            .asList(new AbstractDataFlavorSupport(DataFlavor.javaFileListFlavor) {
+                @Override
+                public boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt)
+                        throws UnsupportedFlavorException, IOException {
+                    @SuppressWarnings("unchecked")
+                    List<File> files = (List<File>) support.getTransferable().getTransferData(df);
+                    OpenFileAction.OpenFileTask task = new OpenFileAction.OpenFileTask(files, null);
+                    task.setRecordHistory(true);
+                    Main.worker.submit(task);
+                    return true;
+                }
+            }, new AbstractDataFlavorSupport(PrimitiveTransferData.DATA_FLAVOR) {
+                @Override
+                public boolean importData(TransferSupport support, final OsmDataLayer layer, EastNorth pasteAt)
+                        throws UnsupportedFlavorException, IOException {
+                    PrimitiveTransferData pasteBuffer = (PrimitiveTransferData) support.getTransferable()
+                            .getTransferData(df);
+                    // Allow to cancel paste if there are incomplete primitives
+                    if (pasteBuffer.hasIncompleteData() && !confirmDeleteIncomplete()) {
+                        return false;
+                    }
+
+                    EastNorth center = pasteBuffer.getCenter();
+                    EastNorth offset = pasteAt.subtract(center);
+
+                    // Make a copy of pasteBuffer and map from old id to copied data id
+                    List<PrimitiveData> bufferCopy = new ArrayList<>();
+                    List<PrimitiveData> toSelect = new ArrayList<>();
+                    Map<Long, Long> newNodeIds = new HashMap<>();
+                    Map<Long, Long> newWayIds = new HashMap<>();
+                    Map<Long, Long> newRelationIds = new HashMap<>();
+                    for (PrimitiveData data : pasteBuffer.getAll()) {
+                        if (data.isIncomplete()) {
+                            continue;
+                        }
+                        PrimitiveData copy = data.makeCopy();
+                        copy.clearOsmMetadata();
+                        if (data instanceof NodeData) {
+                            newNodeIds.put(data.getUniqueId(), copy.getUniqueId());
+                        } else if (data instanceof WayData) {
+                            newWayIds.put(data.getUniqueId(), copy.getUniqueId());
+                        } else if (data instanceof RelationData) {
+                            newRelationIds.put(data.getUniqueId(), copy.getUniqueId());
+                        }
+                        bufferCopy.add(copy);
+                        if (pasteBuffer.getDirectlyAdded().contains(data)) {
+                            toSelect.add(copy);
+                        }
+                    }
+
+                    // Update references in copied buffer
+                    for (PrimitiveData data : bufferCopy) {
+                        if (data instanceof NodeData) {
+                            NodeData nodeData = (NodeData) data;
+                            nodeData.setEastNorth(nodeData.getEastNorth().add(offset));
+                        } else if (data instanceof WayData) {
+                            List<Long> newNodes = new ArrayList<>();
+                            for (Long oldNodeId : ((WayData) data).getNodes()) {
+                                Long newNodeId = newNodeIds.get(oldNodeId);
+                                if (newNodeId != null) {
+                                    newNodes.add(newNodeId);
+                                }
+                            }
+                            ((WayData) data).setNodes(newNodes);
+                        } else if (data instanceof RelationData) {
+                            List<RelationMemberData> newMembers = new ArrayList<>();
+                            for (RelationMemberData member : ((RelationData) data).getMembers()) {
+                                OsmPrimitiveType memberType = member.getMemberType();
+                                Long newId;
+                                switch (memberType) {
+                                case NODE:
+                                    newId = newNodeIds.get(member.getMemberId());
+                                    break;
+                                case WAY:
+                                    newId = newWayIds.get(member.getMemberId());
+                                    break;
+                                case RELATION:
+                                    newId = newRelationIds.get(member.getMemberId());
+                                    break;
+                                default:
+                                    throw new AssertionError();
+                                }
+                                if (newId != null) {
+                                    newMembers.add(new RelationMemberData(member.getRole(), memberType, newId));
+                                }
+                            }
+                            ((RelationData) data).setMembers(newMembers);
+                        }
+                    }
+
+                    /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
+                    Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect) {
+                        @Override
+                        protected OsmDataLayer getLayer() {
+                            return layer;
+                        }
+                    });
+                    Main.map.mapView.repaint();
+                    return true;
+                }
+            });
+
+
+    @Override
+    public boolean canImport(TransferSupport support) {
+        // import everything for now, only support copy.
+        for (AbstractDataFlavorSupport df : SUPPORTED) {
+            if (df.supports(support)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean importData(TransferSupport support) {
+        return importData(support, Main.getLayerManager().getEditLayer(), null);
+    }
+
+    private boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth center) {
+        for (AbstractDataFlavorSupport df : SUPPORTED) {
+            if (df.supports(support)) {
+                try {
+                    df.importData(support, layer, center);
+                    return true;
+                } catch (UnsupportedFlavorException | IOException e) {
+                    Main.warn(e);
+                }
+            }
+        }
+        return super.importData(support);
+    }
+
+    /**
+     * Invoke a copy for the given data.
+     * @param data The data to copy.
+     */
+    public static void copy(final PrimitiveTransferData data) {
+        getClippboard().setContents(new Transferable() {
+            @Override
+            public boolean isDataFlavorSupported(DataFlavor flavor) {
+                return flavor == PrimitiveTransferData.DATA_FLAVOR;
+            }
+
+            @Override
+            public DataFlavor[] getTransferDataFlavors() {
+                return new DataFlavor[] { PrimitiveTransferData.DATA_FLAVOR };
+            }
+
+            @Override
+            public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
+                if (flavor == PrimitiveTransferData.DATA_FLAVOR) {
+                    return data;
+                } else {
+                    throw new UnsupportedFlavorException(flavor);
+                }
+            }
+        }, null);
+    }
+
+    public void pasteOn(OsmDataLayer editLayer, EastNorth mPosition) {
+        Transferable transferable = getClippboard().getContents(null);
+        importData(new TransferSupport(Main.panel, transferable), editLayer, mPosition);
+    }
+
+    private static boolean confirmDeleteIncomplete() {
+        ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Delete incomplete members?"),
+                new String[] { tr("Paste without incomplete members"), tr("Cancel") });
+        ed.setButtonIcons(new String[] { "dialogs/relation/deletemembers", "cancel" });
+        ed.setContent(tr(
+                "The copied data contains incomplete objects.  " + "When pasting the incomplete objects are removed.  "
+                        + "Do you want to paste the data without the incomplete objects?"));
+        ed.showDialog();
+        return ed.getValue() == 1;
+    }
+
+    private static Clipboard getClippboard() {
+        //TODO: Might be unsupported in some cases, we need a fake clippboard then.
+        return Toolkit.getDefaultToolkit().getSystemClipboard();
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java b/src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java
new file mode 100644
index 0000000..338a823
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java
@@ -0,0 +1,19 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.datatransfer;
+
+import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
+import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
+
+/**
+ * Temporary helper class that allows the paste buffer and Swing CCP to coexist.
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class PasteBufferCompatibilityHelper implements PasteBufferChangedListener {
+    private OsmTransferHandler transferHandler = new OsmTransferHandler();
+    @Override
+    public void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer) {
+        PrimitiveTransferData data = new PrimitiveTransferData(pasteBuffer);
+        OsmTransferHandler.copy(data);
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java b/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java
new file mode 100644
index 0000000..71b860c
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java
@@ -0,0 +1,150 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.datatransfer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.osm.NodeData;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.PrimitiveData;
+import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
+import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+
+/**
+ * A list of primitives that are transfered. The list allows you to implicitly add primitives.
+ * It distingluishes between primitives that were directly added and implicitly added ones.
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class PrimitiveTransferData implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final DataFlavor DATA_FLAVOR = new DataFlavor(PrimitiveTransferData.class, "OSM Primitives");
+
+    private static final class GetReferences implements ReferenceGetter {
+        @Override
+        public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) {
+            if (primitive instanceof Way) {
+                return ((Way) primitive).getNodes();
+            } else if (primitive instanceof Relation) {
+                return ((Relation) primitive).getMemberPrimitives();
+            } else {
+                return Collections.emptyList();
+            }
+        }
+    }
+
+    // TODO: Java8: Replace by Function.
+    private interface ReferenceGetter {
+        Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive);
+    }
+
+    private final ArrayList<PrimitiveData> direct;
+    private final ArrayList<PrimitiveData> referenced;
+
+    /**
+     *
+     * @param primitives
+     * @param referencedGetter
+     */
+    private PrimitiveTransferData(Collection<OsmPrimitive> primitives, ReferenceGetter referencedGetter) {
+        // convert to hash set first to remove dupplicates
+        HashSet<OsmPrimitive> visited = new HashSet<>(primitives);
+        this.direct = new ArrayList<>(visited.size());
+
+        this.referenced = new ArrayList<>();
+        Queue<OsmPrimitive> toCheck = new LinkedList<>();
+        for (OsmPrimitive p : visited) {
+            direct.add(p.save());
+            toCheck.addAll(referencedGetter.getReferedPrimitives(p));
+        }
+        while (!toCheck.isEmpty()) {
+            OsmPrimitive p = toCheck.poll();
+            if (visited.add(p)) {
+                referenced.add(p.save());
+                toCheck.addAll(referencedGetter.getReferedPrimitives(p));
+            }
+        }
+    }
+
+    /**
+     * Temporary copy constructor
+     * @param pdc The pdc.
+     */
+    public PrimitiveTransferData(PrimitiveDeepCopy pdc) {
+        this.direct = new ArrayList<>(pdc.getDirectlyAdded());
+        this.referenced = new ArrayList<>(pdc.getReferenced());
+    }
+
+    /**
+     * Gets all primitives directly added.
+     * @return The primitives
+     */
+    public ArrayList<PrimitiveData> getDirectlyAdded() {
+        return direct;
+    }
+
+    /**
+     * Gets all primitives that were added because they were referenced.
+     * @return The primitives
+     */
+    public ArrayList<PrimitiveData> getReferenced() {
+        return referenced;
+    }
+
+    public Collection<PrimitiveData> getAll() {
+        ArrayList<PrimitiveData> list = new ArrayList<>();
+        list.addAll(direct);
+        list.addAll(referenced);
+        return list;
+    }
+
+    public static PrimitiveTransferData getData(Collection<OsmPrimitive> primitives) {
+        return new PrimitiveTransferData(primitives, new ReferenceGetter() {
+            @Override
+            public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) {
+                return Collections.emptyList();
+            }
+        });
+    }
+
+    public static PrimitiveTransferData getDataWithReferences(Collection<OsmPrimitive> primitives) {
+        return new PrimitiveTransferData(primitives, new GetReferences());
+    }
+
+    /**
+     * Compute the center of all nodes.
+     * @return The center or null if this buffer has no location.
+     */
+    public EastNorth getCenter() {
+        BoundingXYVisitor visitor = new BoundingXYVisitor();
+        for (PrimitiveData pd : getAll()) {
+            if (pd instanceof NodeData && !pd.isIncomplete()) {
+                visitor.visit(((NodeData) pd).getEastNorth());
+            }
+        }
+        if (visitor.hasExtend()) {
+            return visitor.getBounds().getCenter();
+        } else {
+            return null;
+        }
+    }
+
+    public boolean hasIncompleteData() {
+        for (PrimitiveData pd : getAll()) {
+            if (pd.isIncomplete()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
