Ticket #12478: patch-ccp-primitives-copy.patch
File patch-ccp-primitives-copy.patch, 30.6 KB (added by , 9 years ago) |
---|
-
src/org/openstreetmap/josm/actions/PasteAction.java
diff --git a/src/org/openstreetmap/josm/actions/PasteAction.java b/src/org/openstreetmap/josm/actions/PasteAction.java index 6030f29..2b1ba60 100644
a b import java.awt.MouseInfo; 9 9 import java.awt.Point; 10 10 import java.awt.event.ActionEvent; 11 11 import java.awt.event.KeyEvent; 12 import java.util.ArrayList;13 import java.util.HashMap;14 import java.util.List;15 import java.util.Map;16 12 17 13 import org.openstreetmap.josm.Main; 18 import org.openstreetmap.josm.command.AddPrimitivesCommand;19 14 import org.openstreetmap.josm.data.coor.EastNorth; 20 import org.openstreetmap.josm.data.osm.NodeData;21 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;22 import org.openstreetmap.josm.data.osm.PrimitiveData;23 15 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy; 24 16 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener; 25 import org.openstreetmap.josm.data.osm.RelationData; 26 import org.openstreetmap.josm.data.osm.RelationMemberData; 27 import org.openstreetmap.josm.data.osm.WayData; 28 import org.openstreetmap.josm.gui.ExtendedDialog; 29 import org.openstreetmap.josm.gui.layer.Layer; 17 import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler; 30 18 import org.openstreetmap.josm.tools.Shortcut; 31 19 32 20 /** … … import org.openstreetmap.josm.tools.Shortcut; 35 23 */ 36 24 public final class PasteAction extends JosmAction implements PasteBufferChangedListener { 37 25 26 private final OsmTransferHandler transferHandler; 27 38 28 /** 39 29 * Constructs a new {@code PasteAction}. 40 30 */ … … public final class PasteAction extends JosmAction implements PasteBufferChangedL 46 36 Main.registerActionShortcut(this, 47 37 Shortcut.registerShortcut("system:paste:cua", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_INSERT, Shortcut.SHIFT)); 48 38 Main.pasteBuffer.addPasteBufferChangedListener(this); 39 transferHandler = new OsmTransferHandler(); 49 40 } 50 41 51 42 @Override 52 43 public void actionPerformed(ActionEvent e) { 53 if (!isEnabled())54 return;55 pasteData(Main.pasteBuffer, Main.pasteSource, e);56 }57 58 /**59 * Paste OSM primitives from the given paste buffer and OSM data layer source to the current edit layer.60 * @param pasteBuffer The paste buffer containing primitive ids to copy61 * @param source The OSM data layer used to look for primitive ids62 * @param e The ActionEvent that triggered this operation63 */64 public void pasteData(PrimitiveDeepCopy pasteBuffer, Layer source, ActionEvent e) {65 /* Find the middle of the pasteBuffer area */66 double maxEast = -1E100;67 double minEast = 1E100;68 double maxNorth = -1E100;69 double minNorth = 1E100;70 boolean incomplete = false;71 for (PrimitiveData data : pasteBuffer.getAll()) {72 if (data instanceof NodeData) {73 NodeData n = (NodeData) data;74 if (n.getEastNorth() != null) {75 double east = n.getEastNorth().east();76 double north = n.getEastNorth().north();77 if (east > maxEast) {78 maxEast = east;79 }80 if (east < minEast) {81 minEast = east;82 }83 if (north > maxNorth) {84 maxNorth = north;85 }86 if (north < minNorth) {87 minNorth = north;88 }89 }90 }91 if (data.isIncomplete()) {92 incomplete = true;93 }94 }95 96 // Allow to cancel paste if there are incomplete primitives97 if (incomplete && !confirmDeleteIncomplete()) {98 return;99 }100 101 44 // default to paste in center of map (pasted via menu or cursor not in MapView) 102 45 EastNorth mPosition = Main.map.mapView.getCenter(); 103 46 // We previously checked for modifier to know if the action has been trigerred via shortcut or via menu … … public final class PasteAction extends JosmAction implements PasteBufferChangedL 112 55 } 113 56 } 114 57 115 double offsetEast = mPosition.east() - (maxEast + minEast)/2.0; 116 double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0; 117 118 // Make a copy of pasteBuffer and map from old id to copied data id 119 List<PrimitiveData> bufferCopy = new ArrayList<>(); 120 List<PrimitiveData> toSelect = new ArrayList<>(); 121 Map<Long, Long> newNodeIds = new HashMap<>(); 122 Map<Long, Long> newWayIds = new HashMap<>(); 123 Map<Long, Long> newRelationIds = new HashMap<>(); 124 for (PrimitiveData data: pasteBuffer.getAll()) { 125 if (data.isIncomplete()) { 126 continue; 127 } 128 PrimitiveData copy = data.makeCopy(); 129 copy.clearOsmMetadata(); 130 if (data instanceof NodeData) { 131 newNodeIds.put(data.getUniqueId(), copy.getUniqueId()); 132 } else if (data instanceof WayData) { 133 newWayIds.put(data.getUniqueId(), copy.getUniqueId()); 134 } else if (data instanceof RelationData) { 135 newRelationIds.put(data.getUniqueId(), copy.getUniqueId()); 136 } 137 bufferCopy.add(copy); 138 if (pasteBuffer.getDirectlyAdded().contains(data)) { 139 toSelect.add(copy); 140 } 141 } 142 143 // Update references in copied buffer 144 for (PrimitiveData data:bufferCopy) { 145 if (data instanceof NodeData) { 146 NodeData nodeData = (NodeData) data; 147 if (Main.main.getEditLayer() == source) { 148 nodeData.setEastNorth(nodeData.getEastNorth().add(offsetEast, offsetNorth)); 149 } 150 } else if (data instanceof WayData) { 151 List<Long> newNodes = new ArrayList<>(); 152 for (Long oldNodeId: ((WayData) data).getNodes()) { 153 Long newNodeId = newNodeIds.get(oldNodeId); 154 if (newNodeId != null) { 155 newNodes.add(newNodeId); 156 } 157 } 158 ((WayData) data).setNodes(newNodes); 159 } else if (data instanceof RelationData) { 160 List<RelationMemberData> newMembers = new ArrayList<>(); 161 for (RelationMemberData member: ((RelationData) data).getMembers()) { 162 OsmPrimitiveType memberType = member.getMemberType(); 163 Long newId; 164 switch (memberType) { 165 case NODE: 166 newId = newNodeIds.get(member.getMemberId()); 167 break; 168 case WAY: 169 newId = newWayIds.get(member.getMemberId()); 170 break; 171 case RELATION: 172 newId = newRelationIds.get(member.getMemberId()); 173 break; 174 default: throw new AssertionError(); 175 } 176 if (newId != null) { 177 newMembers.add(new RelationMemberData(member.getRole(), memberType, newId)); 178 } 179 } 180 ((RelationData) data).setMembers(newMembers); 181 } 182 } 183 184 /* Now execute the commands to add the duplicated contents of the paste buffer to the map */ 185 Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect)); 186 Main.map.mapView.repaint(); 187 } 188 189 private static boolean confirmDeleteIncomplete() { 190 ExtendedDialog ed = new ExtendedDialog(Main.parent, 191 tr("Delete incomplete members?"), 192 new String[] {tr("Paste without incomplete members"), tr("Cancel")}); 193 ed.setButtonIcons(new String[] {"dialogs/relation/deletemembers", "cancel"}); 194 ed.setContent(tr("The copied data contains incomplete objects. " 195 + "When pasting the incomplete objects are removed. " 196 + "Do you want to paste the data without the incomplete objects?")); 197 ed.showDialog(); 198 return ed.getValue() == 1; 58 transferHandler.pasteOn(Main.getLayerManager().getEditLayer(), mPosition); 199 59 } 200 60 201 61 @Override -
src/org/openstreetmap/josm/data/osm/PrimitiveData.java
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 b public abstract class PrimitiveData extends AbstractPrimitive implements Seriali 80 80 oos.writeInt(version); 81 81 oos.writeInt(changesetId); 82 82 oos.writeInt(timestamp); 83 oos.writeObject(keys); 83 84 oos.defaultWriteObject(); 84 85 } 85 86 … … public abstract class PrimitiveData extends AbstractPrimitive implements Seriali 91 92 version = ois.readInt(); 92 93 changesetId = ois.readInt(); 93 94 timestamp = ois.readInt(); 95 keys = (String[]) ois.readObject(); 94 96 ois.defaultReadObject(); 95 97 } 96 98 } -
src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java
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 b import java.util.Set; 9 9 import java.util.concurrent.CopyOnWriteArrayList; 10 10 11 11 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor; 12 import org.openstreetmap.josm.gui.datatransfer.PasteBufferCompatibilityHelper; 12 13 13 14 /** 14 15 * This class allows to create and keep a deep copy of primitives. Provides methods to access directly added … … public class PrimitiveDeepCopy { 29 30 * Constructs a new {@code PrimitiveDeepCopy} without data. Use {@link #makeCopy(Collection)} after that. 30 31 */ 31 32 public PrimitiveDeepCopy() { 32 // Do nothing 33 // Send everything to Swing CCP 34 listeners.add(new PasteBufferCompatibilityHelper()); 33 35 } 34 36 35 37 /** … … public class PrimitiveDeepCopy { 97 99 firePasteBufferChanged(); 98 100 } 99 101 102 /** 103 * Gets the list of primitives that were explicitly added to this copy. 104 * @return The added primitives 105 */ 100 106 public List<PrimitiveData> getDirectlyAdded() { 101 107 return directlyAdded; 102 108 } 103 109 110 /** 111 * Gets the list of primitives that were implicitly added because they were referenced. 112 * @return The primitives 113 */ 104 114 public List<PrimitiveData> getReferenced() { 105 115 return referenced; 106 116 } 107 117 118 /** 119 * Gets a list of all primitives in this copy. 120 * @return The primitives 121 * @see #getDirectlyAdded() 122 * @see #getReferenced() 123 */ 108 124 public List<PrimitiveData> getAll() { 109 125 List<PrimitiveData> result = new ArrayList<>(directlyAdded.size() + referenced.size()); 110 126 result.addAll(directlyAdded); -
src/org/openstreetmap/josm/gui/MapFrame.java
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java index 62030d9..0a74071 100644
a b import java.awt.Component; 8 8 import java.awt.Container; 9 9 import java.awt.Dimension; 10 10 import java.awt.Font; 11 import java.awt.GraphicsEnvironment;12 11 import java.awt.GridBagLayout; 13 12 import java.awt.Rectangle; 14 13 import java.awt.event.ActionEvent; … … public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi 197 196 setLayout(new BorderLayout()); 198 197 199 198 mapView = new MapView(Main.getLayerManager(), contentPane, viewportData); 200 if (!GraphicsEnvironment.isHeadless()) {201 new FileDrop(mapView);202 }203 199 204 200 splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); 205 201 -
src/org/openstreetmap/josm/gui/MapView.java
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java index 5f4df08..f93608b 100644
a b import org.openstreetmap.josm.data.osm.OsmPrimitive; 51 51 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 52 52 import org.openstreetmap.josm.data.osm.visitor.paint.Rendering; 53 53 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache; 54 import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler; 54 55 import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable; 55 56 import org.openstreetmap.josm.gui.layer.GpxLayer; 56 57 import org.openstreetmap.josm.gui.layer.ImageryLayer; … … LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener { 532 533 for (JComponent c : getMapNavigationComponents(MapView.this)) { 533 534 add(c); 534 535 } 536 setTransferHandler(new OsmTransferHandler()); 535 537 } 536 538 537 539 /** -
new file src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java
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
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.datatransfer; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Toolkit; 7 import java.awt.datatransfer.Clipboard; 8 import java.awt.datatransfer.DataFlavor; 9 import java.awt.datatransfer.Transferable; 10 import java.awt.datatransfer.UnsupportedFlavorException; 11 import java.io.File; 12 import java.io.IOException; 13 import java.util.ArrayList; 14 import java.util.Arrays; 15 import java.util.Collection; 16 import java.util.HashMap; 17 import java.util.List; 18 import java.util.Map; 19 20 import javax.swing.TransferHandler; 21 22 import org.openstreetmap.josm.Main; 23 import org.openstreetmap.josm.actions.OpenFileAction; 24 import org.openstreetmap.josm.command.AddPrimitivesCommand; 25 import org.openstreetmap.josm.data.coor.EastNorth; 26 import org.openstreetmap.josm.data.osm.NodeData; 27 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 28 import org.openstreetmap.josm.data.osm.PrimitiveData; 29 import org.openstreetmap.josm.data.osm.RelationData; 30 import org.openstreetmap.josm.data.osm.RelationMemberData; 31 import org.openstreetmap.josm.data.osm.WayData; 32 import org.openstreetmap.josm.gui.ExtendedDialog; 33 import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable.Data; 34 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 35 36 /** 37 * This transfer hanlder provides the ability to transfer OSM data. It allows you to receive files and {@link Data} objects. 38 * @author Michael Zangl 39 */ 40 public class OsmTransferHandler extends TransferHandler { 41 42 private abstract static class AbstractDataFlavorSupport { 43 protected final DataFlavor df; 44 45 AbstractDataFlavorSupport(DataFlavor df) { 46 this.df = df; 47 } 48 49 protected boolean supports(TransferSupport support) { 50 return support.isDataFlavorSupported(df) && isCopy(support); 51 } 52 53 private boolean isCopy(TransferSupport support) { 54 return !support.isDrop() || (COPY & support.getSourceDropActions()) == COPY; 55 } 56 57 protected abstract boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt) 58 throws UnsupportedFlavorException, IOException; 59 } 60 61 private static Collection<AbstractDataFlavorSupport> SUPPORTED = Arrays 62 .asList(new AbstractDataFlavorSupport(DataFlavor.javaFileListFlavor) { 63 @Override 64 public boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt) 65 throws UnsupportedFlavorException, IOException { 66 @SuppressWarnings("unchecked") 67 List<File> files = (List<File>) support.getTransferable().getTransferData(df); 68 OpenFileAction.OpenFileTask task = new OpenFileAction.OpenFileTask(files, null); 69 task.setRecordHistory(true); 70 Main.worker.submit(task); 71 return true; 72 } 73 }, new AbstractDataFlavorSupport(PrimitiveTransferData.DATA_FLAVOR) { 74 @Override 75 public boolean importData(TransferSupport support, final OsmDataLayer layer, EastNorth pasteAt) 76 throws UnsupportedFlavorException, IOException { 77 PrimitiveTransferData pasteBuffer = (PrimitiveTransferData) support.getTransferable() 78 .getTransferData(df); 79 // Allow to cancel paste if there are incomplete primitives 80 if (pasteBuffer.hasIncompleteData() && !confirmDeleteIncomplete()) { 81 return false; 82 } 83 84 EastNorth center = pasteBuffer.getCenter(); 85 EastNorth offset = pasteAt.subtract(center); 86 87 // Make a copy of pasteBuffer and map from old id to copied data id 88 List<PrimitiveData> bufferCopy = new ArrayList<>(); 89 List<PrimitiveData> toSelect = new ArrayList<>(); 90 Map<Long, Long> newNodeIds = new HashMap<>(); 91 Map<Long, Long> newWayIds = new HashMap<>(); 92 Map<Long, Long> newRelationIds = new HashMap<>(); 93 for (PrimitiveData data : pasteBuffer.getAll()) { 94 if (data.isIncomplete()) { 95 continue; 96 } 97 PrimitiveData copy = data.makeCopy(); 98 copy.clearOsmMetadata(); 99 if (data instanceof NodeData) { 100 newNodeIds.put(data.getUniqueId(), copy.getUniqueId()); 101 } else if (data instanceof WayData) { 102 newWayIds.put(data.getUniqueId(), copy.getUniqueId()); 103 } else if (data instanceof RelationData) { 104 newRelationIds.put(data.getUniqueId(), copy.getUniqueId()); 105 } 106 bufferCopy.add(copy); 107 if (pasteBuffer.getDirectlyAdded().contains(data)) { 108 toSelect.add(copy); 109 } 110 } 111 112 // Update references in copied buffer 113 for (PrimitiveData data : bufferCopy) { 114 if (data instanceof NodeData) { 115 NodeData nodeData = (NodeData) data; 116 nodeData.setEastNorth(nodeData.getEastNorth().add(offset)); 117 } else if (data instanceof WayData) { 118 List<Long> newNodes = new ArrayList<>(); 119 for (Long oldNodeId : ((WayData) data).getNodes()) { 120 Long newNodeId = newNodeIds.get(oldNodeId); 121 if (newNodeId != null) { 122 newNodes.add(newNodeId); 123 } 124 } 125 ((WayData) data).setNodes(newNodes); 126 } else if (data instanceof RelationData) { 127 List<RelationMemberData> newMembers = new ArrayList<>(); 128 for (RelationMemberData member : ((RelationData) data).getMembers()) { 129 OsmPrimitiveType memberType = member.getMemberType(); 130 Long newId; 131 switch (memberType) { 132 case NODE: 133 newId = newNodeIds.get(member.getMemberId()); 134 break; 135 case WAY: 136 newId = newWayIds.get(member.getMemberId()); 137 break; 138 case RELATION: 139 newId = newRelationIds.get(member.getMemberId()); 140 break; 141 default: 142 throw new AssertionError(); 143 } 144 if (newId != null) { 145 newMembers.add(new RelationMemberData(member.getRole(), memberType, newId)); 146 } 147 } 148 ((RelationData) data).setMembers(newMembers); 149 } 150 } 151 152 /* Now execute the commands to add the duplicated contents of the paste buffer to the map */ 153 Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect) { 154 @Override 155 protected OsmDataLayer getLayer() { 156 return layer; 157 } 158 }); 159 Main.map.mapView.repaint(); 160 return true; 161 } 162 }); 163 164 165 @Override 166 public boolean canImport(TransferSupport support) { 167 // import everything for now, only support copy. 168 for (AbstractDataFlavorSupport df : SUPPORTED) { 169 if (df.supports(support)) { 170 return true; 171 } 172 } 173 return false; 174 } 175 176 @Override 177 public boolean importData(TransferSupport support) { 178 return importData(support, Main.getLayerManager().getEditLayer(), null); 179 } 180 181 private boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth center) { 182 for (AbstractDataFlavorSupport df : SUPPORTED) { 183 if (df.supports(support)) { 184 try { 185 df.importData(support, layer, center); 186 return true; 187 } catch (UnsupportedFlavorException | IOException e) { 188 Main.warn(e); 189 } 190 } 191 } 192 return super.importData(support); 193 } 194 195 /** 196 * Invoke a copy for the given data. 197 * @param data The data to copy. 198 */ 199 public static void copy(final PrimitiveTransferData data) { 200 getClippboard().setContents(new Transferable() { 201 @Override 202 public boolean isDataFlavorSupported(DataFlavor flavor) { 203 return flavor == PrimitiveTransferData.DATA_FLAVOR; 204 } 205 206 @Override 207 public DataFlavor[] getTransferDataFlavors() { 208 return new DataFlavor[] { PrimitiveTransferData.DATA_FLAVOR }; 209 } 210 211 @Override 212 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 213 if (flavor == PrimitiveTransferData.DATA_FLAVOR) { 214 return data; 215 } else { 216 throw new UnsupportedFlavorException(flavor); 217 } 218 } 219 }, null); 220 } 221 222 public void pasteOn(OsmDataLayer editLayer, EastNorth mPosition) { 223 Transferable transferable = getClippboard().getContents(null); 224 importData(new TransferSupport(Main.panel, transferable), editLayer, mPosition); 225 } 226 227 private static boolean confirmDeleteIncomplete() { 228 ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Delete incomplete members?"), 229 new String[] { tr("Paste without incomplete members"), tr("Cancel") }); 230 ed.setButtonIcons(new String[] { "dialogs/relation/deletemembers", "cancel" }); 231 ed.setContent(tr( 232 "The copied data contains incomplete objects. " + "When pasting the incomplete objects are removed. " 233 + "Do you want to paste the data without the incomplete objects?")); 234 ed.showDialog(); 235 return ed.getValue() == 1; 236 } 237 238 private static Clipboard getClippboard() { 239 //TODO: Might be unsupported in some cases, we need a fake clippboard then. 240 return Toolkit.getDefaultToolkit().getSystemClipboard(); 241 } 242 } -
new file src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java
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
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.datatransfer; 3 4 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy; 5 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener; 6 7 /** 8 * Temporary helper class that allows the paste buffer and Swing CCP to coexist. 9 * @author Michael Zangl 10 * @since xxx 11 */ 12 public class PasteBufferCompatibilityHelper implements PasteBufferChangedListener { 13 private OsmTransferHandler transferHandler = new OsmTransferHandler(); 14 @Override 15 public void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer) { 16 PrimitiveTransferData data = new PrimitiveTransferData(pasteBuffer); 17 OsmTransferHandler.copy(data); 18 } 19 } -
new file src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java
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
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.datatransfer; 3 4 import java.awt.datatransfer.DataFlavor; 5 import java.io.Serializable; 6 import java.util.ArrayList; 7 import java.util.Collection; 8 import java.util.Collections; 9 import java.util.HashSet; 10 import java.util.LinkedList; 11 import java.util.Queue; 12 13 import org.openstreetmap.josm.data.coor.EastNorth; 14 import org.openstreetmap.josm.data.osm.NodeData; 15 import org.openstreetmap.josm.data.osm.OsmPrimitive; 16 import org.openstreetmap.josm.data.osm.PrimitiveData; 17 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy; 18 import org.openstreetmap.josm.data.osm.Relation; 19 import org.openstreetmap.josm.data.osm.Way; 20 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 21 22 /** 23 * A list of primitives that are transfered. The list allows you to implicitly add primitives. 24 * It distingluishes between primitives that were directly added and implicitly added ones. 25 * @author Michael Zangl 26 * @since xxx 27 */ 28 public class PrimitiveTransferData implements Serializable { 29 private static final long serialVersionUID = 1L; 30 31 public static final DataFlavor DATA_FLAVOR = new DataFlavor(PrimitiveTransferData.class, "OSM Primitives"); 32 33 private static final class GetReferences implements ReferenceGetter { 34 @Override 35 public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) { 36 if (primitive instanceof Way) { 37 return ((Way) primitive).getNodes(); 38 } else if (primitive instanceof Relation) { 39 return ((Relation) primitive).getMemberPrimitives(); 40 } else { 41 return Collections.emptyList(); 42 } 43 } 44 } 45 46 // TODO: Java8: Replace by Function. 47 private interface ReferenceGetter { 48 Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive); 49 } 50 51 private final ArrayList<PrimitiveData> direct; 52 private final ArrayList<PrimitiveData> referenced; 53 54 /** 55 * 56 * @param primitives 57 * @param referencedGetter 58 */ 59 private PrimitiveTransferData(Collection<OsmPrimitive> primitives, ReferenceGetter referencedGetter) { 60 // convert to hash set first to remove dupplicates 61 HashSet<OsmPrimitive> visited = new HashSet<>(primitives); 62 this.direct = new ArrayList<>(visited.size()); 63 64 this.referenced = new ArrayList<>(); 65 Queue<OsmPrimitive> toCheck = new LinkedList<>(); 66 for (OsmPrimitive p : visited) { 67 direct.add(p.save()); 68 toCheck.addAll(referencedGetter.getReferedPrimitives(p)); 69 } 70 while (!toCheck.isEmpty()) { 71 OsmPrimitive p = toCheck.poll(); 72 if (visited.add(p)) { 73 referenced.add(p.save()); 74 toCheck.addAll(referencedGetter.getReferedPrimitives(p)); 75 } 76 } 77 } 78 79 /** 80 * Temporary copy constructor 81 * @param pdc The pdc. 82 */ 83 public PrimitiveTransferData(PrimitiveDeepCopy pdc) { 84 this.direct = new ArrayList<>(pdc.getDirectlyAdded()); 85 this.referenced = new ArrayList<>(pdc.getReferenced()); 86 } 87 88 /** 89 * Gets all primitives directly added. 90 * @return The primitives 91 */ 92 public ArrayList<PrimitiveData> getDirectlyAdded() { 93 return direct; 94 } 95 96 /** 97 * Gets all primitives that were added because they were referenced. 98 * @return The primitives 99 */ 100 public ArrayList<PrimitiveData> getReferenced() { 101 return referenced; 102 } 103 104 public Collection<PrimitiveData> getAll() { 105 ArrayList<PrimitiveData> list = new ArrayList<>(); 106 list.addAll(direct); 107 list.addAll(referenced); 108 return list; 109 } 110 111 public static PrimitiveTransferData getData(Collection<OsmPrimitive> primitives) { 112 return new PrimitiveTransferData(primitives, new ReferenceGetter() { 113 @Override 114 public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) { 115 return Collections.emptyList(); 116 } 117 }); 118 } 119 120 public static PrimitiveTransferData getDataWithReferences(Collection<OsmPrimitive> primitives) { 121 return new PrimitiveTransferData(primitives, new GetReferences()); 122 } 123 124 /** 125 * Compute the center of all nodes. 126 * @return The center or null if this buffer has no location. 127 */ 128 public EastNorth getCenter() { 129 BoundingXYVisitor visitor = new BoundingXYVisitor(); 130 for (PrimitiveData pd : getAll()) { 131 if (pd instanceof NodeData && !pd.isIncomplete()) { 132 visitor.visit(((NodeData) pd).getEastNorth()); 133 } 134 } 135 if (visitor.hasExtend()) { 136 return visitor.getBounds().getCenter(); 137 } else { 138 return null; 139 } 140 } 141 142 public boolean hasIncompleteData() { 143 for (PrimitiveData pd : getAll()) { 144 if (pd.isIncomplete()) { 145 return true; 146 } 147 } 148 return false; 149 } 150 }