source: josm/trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/PrimitiveTagTransferPaster.java@ 12769

Last change on this file since 12769 was 12769, checked in by Don-vip, 7 years ago

fix #15262 - see #13036 - fix regression in ChangePropertyCommand + optimize commands creation when pasting tags

  • Property svn:eol-style set to native
File size: 8.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.datatransfer.importers;
3
4import java.awt.datatransfer.UnsupportedFlavorException;
5import java.io.IOException;
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Collection;
9import java.util.EnumMap;
10import java.util.List;
11import java.util.Map;
12
13import javax.swing.TransferHandler.TransferSupport;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.command.ChangePropertyCommand;
17import org.openstreetmap.josm.command.Command;
18import org.openstreetmap.josm.data.osm.IPrimitive;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
22import org.openstreetmap.josm.data.osm.Tag;
23import org.openstreetmap.josm.data.osm.TagCollection;
24import org.openstreetmap.josm.data.osm.TagMap;
25import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
26import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTagTransferData;
27
28/**
29 * This class helps pasting tags from other primitives. It handles resolving conflicts.
30 * @author Michael Zangl
31 * @since 10737
32 */
33public class PrimitiveTagTransferPaster extends AbstractTagPaster {
34 /**
35 * Create a new {@link PrimitiveTagTransferPaster}
36 */
37 public PrimitiveTagTransferPaster() {
38 super(PrimitiveTagTransferData.FLAVOR);
39 }
40
41 @Override
42 public boolean importTagsOn(TransferSupport support, Collection<? extends OsmPrimitive> selection)
43 throws UnsupportedFlavorException, IOException {
44 Object o = support.getTransferable().getTransferData(df);
45 if (!(o instanceof PrimitiveTagTransferData))
46 return false;
47 PrimitiveTagTransferData data = (PrimitiveTagTransferData) o;
48
49 TagPasteSupport tagPaster = new TagPasteSupport(data, selection);
50 List<Command> commands = new ArrayList<>();
51 for (Tag tag : tagPaster.execute()) {
52 ChangePropertyCommand cmd = new ChangePropertyCommand(selection, tag.getKey(), "".equals(tag.getValue()) ? null : tag.getValue());
53 if (cmd.getObjectsNumber() > 0) {
54 commands.add(cmd);
55 }
56 }
57 commitCommands(selection, commands);
58 return true;
59 }
60
61 @Override
62 protected Map<String, String> getTags(TransferSupport support) throws UnsupportedFlavorException, IOException {
63 PrimitiveTagTransferData data = (PrimitiveTagTransferData) support.getTransferable().getTransferData(df);
64
65 TagPasteSupport tagPaster = new TagPasteSupport(data, Arrays.asList(new Node()));
66 return new TagMap(tagPaster.execute());
67 }
68
69 private static class TagPasteSupport {
70 private final PrimitiveTagTransferData data;
71 private final Collection<? extends IPrimitive> selection;
72 private final List<Tag> tags = new ArrayList<>();
73
74 /**
75 * Constructs a new {@code TagPasteSupport}.
76 * @param data source tags to paste
77 * @param selection target primitives
78 */
79 TagPasteSupport(PrimitiveTagTransferData data, Collection<? extends IPrimitive> selection) {
80 super();
81 this.data = data;
82 this.selection = selection;
83 }
84
85 /**
86 * Pastes the tags from a homogeneous source (the selection consisting
87 * of one type of {@link OsmPrimitive}s only).
88 *
89 * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
90 * regardless of their type, receive the same tags.
91 */
92 protected void pasteFromHomogeneousSource() {
93 TagCollection tc = null;
94 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
95 TagCollection tc1 = data.getForPrimitives(type);
96 if (!tc1.isEmpty()) {
97 tc = tc1;
98 }
99 }
100 if (tc == null)
101 // no tags found to paste. Abort.
102 return;
103
104 if (!tc.isApplicableToPrimitive()) {
105 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
106 dialog.populate(tc, data.getStatistics(), getTargetStatistics());
107 dialog.setVisible(true);
108 if (dialog.isCanceled())
109 return;
110 buildTags(dialog.getResolution());
111 } else {
112 // no conflicts in the source tags to resolve. Just apply the tags to the target primitives
113 buildTags(tc);
114 }
115 }
116
117 /**
118 * Replies true if this a heterogeneous source can be pasted without conflict to targets
119 *
120 * @return true if this a heterogeneous source can be pasted without conflicts to targets
121 */
122 protected boolean canPasteFromHeterogeneousSourceWithoutConflict() {
123 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
124 if (hasTargetPrimitives(type)) {
125 TagCollection tc = data.getForPrimitives(type);
126 if (!tc.isEmpty() && !tc.isApplicableToPrimitive())
127 return false;
128 }
129 }
130 return true;
131 }
132
133 /**
134 * Pastes the tags in the current selection of the paste buffer to a set of target primitives.
135 */
136 protected void pasteFromHeterogeneousSource() {
137 if (canPasteFromHeterogeneousSourceWithoutConflict()) {
138 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
139 if (!data.getForPrimitives(type).isEmpty() && hasTargetPrimitives(type)) {
140 buildTags(data.getForPrimitives(type));
141 }
142 }
143 } else {
144 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
145 dialog.populate(
146 data.getForPrimitives(OsmPrimitiveType.NODE),
147 data.getForPrimitives(OsmPrimitiveType.WAY),
148 data.getForPrimitives(OsmPrimitiveType.RELATION),
149 data.getStatistics(),
150 getTargetStatistics()
151 );
152 dialog.setVisible(true);
153 if (dialog.isCanceled())
154 return;
155 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
156 if (!data.getForPrimitives(type).isEmpty() && hasTargetPrimitives(type)) {
157 buildTags(dialog.getResolution(type));
158 }
159 }
160 }
161 }
162
163 protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
164 Map<OsmPrimitiveType, Integer> ret = new EnumMap<>(OsmPrimitiveType.class);
165 for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
166 int count = (int) selection.stream().filter(p -> type == p.getType()).count();
167 if (count > 0) {
168 ret.put(type, count);
169 }
170 }
171 return ret;
172 }
173
174 /**
175 * Replies true if there is at least one primitive of type <code>type</code>
176 * is in the target collection
177 *
178 * @param type the type to look for
179 * @return true if there is at least one primitive of type <code>type</code> in the collection
180 * <code>selection</code>
181 */
182 protected boolean hasTargetPrimitives(OsmPrimitiveType type) {
183 return selection.stream().anyMatch(p -> type == p.getType());
184 }
185
186 protected void buildTags(TagCollection tc) {
187 for (String key : tc.getKeys()) {
188 tags.add(new Tag(key, tc.getValues(key).iterator().next()));
189 }
190 }
191
192 /**
193 * Performs the paste operation.
194 * @return list of tags
195 */
196 public List<Tag> execute() {
197 tags.clear();
198 if (data.isHeterogeneousSource()) {
199 pasteFromHeterogeneousSource();
200 } else {
201 pasteFromHomogeneousSource();
202 }
203 return tags;
204 }
205
206 @Override
207 public String toString() {
208 return "PasteSupport [data=" + data + ", selection=" + selection + ']';
209 }
210 }
211}
Note: See TracBrowser for help on using the repository browser.