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

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

fix #15387 - IAE occurs when pasting tags into relation editor using "Paste tags from buffer" (regression from #13036)

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