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, 6 weeks 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.