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

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

fix #15224 - CCE: TagTransferData cannot be cast to PrimitiveTagTransferData

  • Property svn:eol-style set to native
File size: 8.1 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 form 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 commands.add(new ChangePropertyCommand(selection, tag.getKey(), "".equals(tag.getValue()) ? null : tag.getValue()));
53 }
54 commitCommands(selection, commands);
55 return true;
56 }
57
58 @Override
59 protected Map<String, String> getTags(TransferSupport support) throws UnsupportedFlavorException, IOException {
60 PrimitiveTagTransferData data = (PrimitiveTagTransferData) support.getTransferable().getTransferData(df);
61
62 TagPasteSupport tagPaster = new TagPasteSupport(data, Arrays.asList(new Node()));
63 return new TagMap(tagPaster.execute());
64 }
65
66 private static class TagPasteSupport {
67 private final PrimitiveTagTransferData data;
68 private final Collection<? extends IPrimitive> selection;
69 private final List<Tag> tags = new ArrayList<>();
70
71 /**
72 * Constructs a new {@code TagPasteSupport}.
73 * @param data source tags to paste
74 * @param selection target primitives
75 */
76 TagPasteSupport(PrimitiveTagTransferData data, Collection<? extends IPrimitive> selection) {
77 super();
78 this.data = data;
79 this.selection = selection;
80 }
81
82 /**
83 * Pastes the tags from a homogeneous source (the selection consisting
84 * of one type of {@link OsmPrimitive}s only).
85 *
86 * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
87 * regardless of their type, receive the same tags.
88 */
89 protected void pasteFromHomogeneousSource() {
90 TagCollection tc = null;
91 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
92 TagCollection tc1 = data.getForPrimitives(type);
93 if (!tc1.isEmpty()) {
94 tc = tc1;
95 }
96 }
97 if (tc == null)
98 // no tags found to paste. Abort.
99 return;
100
101 if (!tc.isApplicableToPrimitive()) {
102 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
103 dialog.populate(tc, data.getStatistics(), getTargetStatistics());
104 dialog.setVisible(true);
105 if (dialog.isCanceled())
106 return;
107 buildTags(dialog.getResolution());
108 } else {
109 // no conflicts in the source tags to resolve. Just apply the tags to the target primitives
110 buildTags(tc);
111 }
112 }
113
114 /**
115 * Replies true if this a heterogeneous source can be pasted without conflict to targets
116 *
117 * @return true if this a heterogeneous source can be pasted without conflicts to targets
118 */
119 protected boolean canPasteFromHeterogeneousSourceWithoutConflict() {
120 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
121 if (hasTargetPrimitives(type)) {
122 TagCollection tc = data.getForPrimitives(type);
123 if (!tc.isEmpty() && !tc.isApplicableToPrimitive())
124 return false;
125 }
126 }
127 return true;
128 }
129
130 /**
131 * Pastes the tags in the current selection of the paste buffer to a set of target primitives.
132 */
133 protected void pasteFromHeterogeneousSource() {
134 if (canPasteFromHeterogeneousSourceWithoutConflict()) {
135 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
136 if (!data.getForPrimitives(type).isEmpty() && hasTargetPrimitives(type)) {
137 buildTags(data.getForPrimitives(type));
138 }
139 }
140 } else {
141 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
142 dialog.populate(
143 data.getForPrimitives(OsmPrimitiveType.NODE),
144 data.getForPrimitives(OsmPrimitiveType.WAY),
145 data.getForPrimitives(OsmPrimitiveType.RELATION),
146 data.getStatistics(),
147 getTargetStatistics()
148 );
149 dialog.setVisible(true);
150 if (dialog.isCanceled())
151 return;
152 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
153 if (!data.getForPrimitives(type).isEmpty() && hasTargetPrimitives(type)) {
154 buildTags(dialog.getResolution(type));
155 }
156 }
157 }
158 }
159
160 protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
161 Map<OsmPrimitiveType, Integer> ret = new EnumMap<>(OsmPrimitiveType.class);
162 for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
163 int count = (int) selection.stream().filter(p -> type == p.getType()).count();
164 if (count > 0) {
165 ret.put(type, count);
166 }
167 }
168 return ret;
169 }
170
171 /**
172 * Replies true if there is at least one primitive of type <code>type</code>
173 * is in the target collection
174 *
175 * @param type the type to look for
176 * @return true if there is at least one primitive of type <code>type</code> in the collection
177 * <code>selection</code>
178 */
179 protected boolean hasTargetPrimitives(OsmPrimitiveType type) {
180 return selection.stream().anyMatch(p -> type == p.getType());
181 }
182
183 protected void buildTags(TagCollection tc) {
184 for (String key : tc.getKeys()) {
185 tags.add(new Tag(key, tc.getValues(key).iterator().next()));
186 }
187 }
188
189 /**
190 * Performs the paste operation.
191 * @return list of tags
192 */
193 public List<Tag> execute() {
194 tags.clear();
195 if (data.isHeterogeneousSource()) {
196 pasteFromHeterogeneousSource();
197 } else {
198 pasteFromHomogeneousSource();
199 }
200 return tags;
201 }
202
203 @Override
204 public String toString() {
205 return "PasteSupport [data=" + data + ", selection=" + selection + ']';
206 }
207 }
208}
Note: See TracBrowser for help on using the repository browser.