source: josm/trunk/src/org/openstreetmap/josm/command/AddPrimitivesCommand.java @ 11609

Last change on this file since 11609 was 11609, checked in by Don-vip, 9 months ago

fix #14410 - AddPrimitivesCommand undo remove existing data (patch by Tyndare)

  • Property svn:eol-style set to native
File size: 7.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.command;
3
4import static org.openstreetmap.josm.tools.I18n.trn;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Objects;
11import java.util.Optional;
12import java.util.stream.Collectors;
13
14import javax.swing.Icon;
15
16import org.openstreetmap.josm.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.NodeData;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.PrimitiveData;
21import org.openstreetmap.josm.gui.layer.OsmDataLayer;
22import org.openstreetmap.josm.tools.CheckParameterUtil;
23import org.openstreetmap.josm.tools.JosmRuntimeException;
24
25/**
26 * Add primitives to a data layer.
27 * @since 2305
28 */
29public class AddPrimitivesCommand extends Command {
30
31    private List<PrimitiveData> data;
32    private Collection<PrimitiveData> toSelect;
33    private List<PrimitiveData> preExistingData;
34
35    // only filled on undo
36    private List<OsmPrimitive> createdPrimitives;
37
38    /**
39     * Constructs a new {@code AddPrimitivesCommand} to add data to the current edit layer.
40     * @param data The OSM primitives data to add. Must not be {@code null}
41     */
42    public AddPrimitivesCommand(List<PrimitiveData> data) {
43        this(data, data);
44    }
45
46    /**
47     * Constructs a new {@code AddPrimitivesCommand} to add data to the current edit layer.
48     * @param data The OSM primitives to add. Must not be {@code null}
49     * @param toSelect The OSM primitives to select at the end. Can be {@code null}
50     * @since 5953
51     */
52    public AddPrimitivesCommand(List<PrimitiveData> data, List<PrimitiveData> toSelect) {
53        init(data, toSelect);
54    }
55
56    /**
57     * Constructs a new {@code AddPrimitivesCommand} to add data to the given layer.
58     * @param data The OSM primitives data to add. Must not be {@code null}
59     * @param toSelect The OSM primitives to select at the end. Can be {@code null}
60     * @param layer The target data layer. Must not be {@code null}
61     */
62    public AddPrimitivesCommand(List<PrimitiveData> data, List<PrimitiveData> toSelect, OsmDataLayer layer) {
63        super(layer);
64        init(data, toSelect);
65    }
66
67    private void init(List<PrimitiveData> data, List<PrimitiveData> toSelect) {
68        CheckParameterUtil.ensureParameterNotNull(data, "data");
69        this.data = new ArrayList<>(data);
70        if (toSelect == data) {
71            this.toSelect = this.data;
72        } else if (toSelect != null) {
73            this.toSelect = new ArrayList<>(toSelect);
74        }
75    }
76
77    @Override
78    public boolean executeCommand() {
79        DataSet ds = getAffectedDataSet();
80        if (createdPrimitives == null) { // first time execution
81            List<OsmPrimitive> newPrimitives = new ArrayList<>(data.size());
82            preExistingData = new ArrayList<>();
83
84            for (PrimitiveData pd : data) {
85                OsmPrimitive primitive = ds.getPrimitiveById(pd);
86                boolean created = primitive == null;
87                if (primitive == null) {
88                    primitive = pd.getType().newInstance(pd.getUniqueId(), true);
89                } else {
90                    preExistingData.add(primitive.save());
91                }
92                if (pd instanceof NodeData) { // Load nodes immediately because they can't be added to dataset without coordinates
93                    primitive.load(pd);
94                }
95                if (created) {
96                    ds.addPrimitive(primitive);
97                }
98                newPrimitives.add(primitive);
99            }
100
101            // Then load ways and relations
102            for (int i = 0; i < newPrimitives.size(); i++) {
103                if (!(newPrimitives.get(i) instanceof Node)) {
104                    newPrimitives.get(i).load(data.get(i));
105                }
106            }
107            newPrimitives.stream().forEach(p -> p.setModified(true));
108        } else { // redo
109            // When redoing this command, we have to add the same objects, otherwise
110            // a subsequent command (e.g. MoveCommand) cannot be redone.
111            for (OsmPrimitive osm : createdPrimitives) {
112                if (preExistingData.stream().anyMatch(pd -> pd.getUniqueId() == osm.getUniqueId())) {
113                    Optional<PrimitiveData> o = data.stream().filter(pd -> pd.getUniqueId() == osm.getUniqueId()).findAny();
114                    if (o.isPresent()) {
115                        osm.load(o.get());
116                    }
117                } else {
118                    ds.addPrimitive(osm);
119                }
120            }
121        }
122        if (toSelect != null) {
123            ds.setSelected(toSelect.stream().map(ds::getPrimitiveById).collect(Collectors.toList()));
124        }
125        return true;
126    }
127
128    @Override public void undoCommand() {
129        DataSet ds = getAffectedDataSet();
130        if (createdPrimitives == null) {
131            createdPrimitives = new ArrayList<>(data.size());
132            for (PrimitiveData pd : data) {
133                OsmPrimitive p = ds.getPrimitiveById(pd);
134                createdPrimitives.add(p);
135            }
136            createdPrimitives = PurgeCommand.topoSort(createdPrimitives);
137        }
138        for (OsmPrimitive osm : createdPrimitives) {
139            Optional<PrimitiveData> previous = preExistingData.stream().filter(pd -> pd.getUniqueId() == osm.getUniqueId()).findAny();
140            if (previous.isPresent()) {
141                osm.load(previous.get());
142            } else {
143                ds.removePrimitive(osm);
144            }
145        }
146    }
147
148    @Override
149    public String getDescriptionText() {
150        int size = data != null ? data.size() : createdPrimitives.size();
151        return trn("Added {0} object", "Added {0} objects", size, size);
152    }
153
154    @Override
155    public Icon getDescriptionIcon() {
156        return null;
157    }
158
159    @Override
160    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
161            Collection<OsmPrimitive> added) {
162        // Does nothing because we don't want to create OsmPrimitives.
163    }
164
165    @Override
166    public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
167        if (createdPrimitives != null)
168            return createdPrimitives;
169
170        Collection<OsmPrimitive> prims = new HashSet<>();
171        for (PrimitiveData d : data) {
172            prims.add(Optional.ofNullable(getAffectedDataSet().getPrimitiveById(d)).orElseThrow(
173                    () -> new JosmRuntimeException("No primitive found for " + d)));
174        }
175        return prims;
176    }
177
178    @Override
179    public int hashCode() {
180        return Objects.hash(super.hashCode(), data, toSelect, preExistingData, createdPrimitives);
181    }
182
183    @Override
184    public boolean equals(Object obj) {
185        if (this == obj) return true;
186        if (obj == null || getClass() != obj.getClass()) return false;
187        if (!super.equals(obj)) return false;
188        AddPrimitivesCommand that = (AddPrimitivesCommand) obj;
189        return Objects.equals(data, that.data) &&
190               Objects.equals(toSelect, that.toSelect) &&
191               Objects.equals(preExistingData, that.preExistingData) &&
192               Objects.equals(createdPrimitives, that.createdPrimitives);
193    }
194}
Note: See TracBrowser for help on using the repository browser.