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

Last change on this file was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • 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.Collections;
10import java.util.EnumMap;
11import java.util.List;
12import java.util.Map;
13
14import javax.swing.TransferHandler.TransferSupport;
15
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.OsmDataManager;
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.MainApplication;
27import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
28import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTagTransferData;
29import org.openstreetmap.josm.tools.StreamUtils;
30
31/**
32 * This class helps pasting tags from other primitives. It handles resolving conflicts.
33 * @author Michael Zangl
34 * @since 10737
35 */
36public class PrimitiveTagTransferPaster extends AbstractTagPaster {
37 /**
38 * Create a new {@link PrimitiveTagTransferPaster}
39 */
40 public PrimitiveTagTransferPaster() {
41 super(PrimitiveTagTransferData.FLAVOR);
42 }
43
44 @Override
45 public boolean importTagsOn(TransferSupport support, Collection<? extends OsmPrimitive> selection)
46 throws UnsupportedFlavorException, IOException {
47 Object o = support.getTransferable().getTransferData(df);
48 if (!(o instanceof PrimitiveTagTransferData))
49 return false;
50 PrimitiveTagTransferData data = (PrimitiveTagTransferData) o;
51
52 TagPasteSupport tagPaster = new TagPasteSupport(data, selection);
53 List<Command> commands = tagPaster.execute().stream()
54 .map(tag -> Collections.singletonMap(tag.getKey(), "".equals(tag.getValue()) ? null : tag.getValue()))
55 .map(tags -> new ChangePropertyCommand(OsmDataManager.getInstance().getEditDataSet(), selection, tags))
56 .filter(cmd -> cmd.getObjectsNumber() > 0)
57 .collect(StreamUtils.toUnmodifiableList());
58 commitCommands(selection, commands);
59 return true;
60 }
61
62 @Override
63 protected Map<String, String> getTags(TransferSupport support) throws UnsupportedFlavorException, IOException {
64 PrimitiveTagTransferData data = (PrimitiveTagTransferData) support.getTransferable().getTransferData(df);
65
66 TagPasteSupport tagPaster = new TagPasteSupport(data, Arrays.asList(new Node()));
67 return new TagMap(tagPaster.execute());
68 }
69
70 private static class TagPasteSupport {
71 private final PrimitiveTagTransferData data;
72 private final Collection<? extends IPrimitive> selection;
73 private final List<Tag> tags = new ArrayList<>();
74
75 /**
76 * Constructs a new {@code TagPasteSupport}.
77 * @param data source tags to paste
78 * @param selection target primitives
79 */
80 TagPasteSupport(PrimitiveTagTransferData data, Collection<? extends IPrimitive> selection) {
81 super();
82 this.data = data;
83 this.selection = selection;
84 }
85
86 /**
87 * Pastes the tags from a homogeneous source (the selection consisting
88 * of one type of {@link OsmPrimitive}s only).
89 *
90 * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
91 * regardless of their type, receive the same tags.
92 */
93 protected void pasteFromHomogeneousSource() {
94 TagCollection tc = null;
95 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
96 TagCollection tc1 = data.getForPrimitives(type);
97 if (!tc1.isEmpty()) {
98 tc = tc1;
99 }
100 }
101 if (tc == null)
102 // no tags found to paste. Abort.
103 return;
104
105 if (!tc.isApplicableToPrimitive()) {
106 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(MainApplication.getMainFrame());
107 dialog.populate(tc, data.getStatistics(), getTargetStatistics());
108 dialog.setVisible(true);
109 if (dialog.isCanceled())
110 return;
111 buildTags(dialog.getResolution());
112 } else {
113 // no conflicts in the source tags to resolve. Just apply the tags to the target primitives
114 buildTags(tc);
115 }
116 }
117
118 /**
119 * Replies true if this a heterogeneous source can be pasted without conflict to targets
120 *
121 * @return true if this a heterogeneous source can be pasted without conflicts to targets
122 */
123 protected boolean canPasteFromHeterogeneousSourceWithoutConflict() {
124 return OsmPrimitiveType.dataValues().stream()
125 .filter(this::hasTargetPrimitives)
126 .map(data::getForPrimitives)
127 .allMatch(tc -> tc.isEmpty() || tc.isApplicableToPrimitive());
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(MainApplication.getMainFrame());
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.