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, 7 years ago

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

  • Property svn:eol-style set to native
File size: 7.2 KB
RevLine 
[2305]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.command;
3
[2844]4import static org.openstreetmap.josm.tools.I18n.trn;
[2305]5
6import java.util.ArrayList;
7import java.util.Collection;
[3262]8import java.util.HashSet;
[2305]9import java.util.List;
[9371]10import java.util.Objects;
[11553]11import java.util.Optional;
[11609]12import java.util.stream.Collectors;
[8456]13
[4918]14import javax.swing.Icon;
[2305]15
[3703]16import org.openstreetmap.josm.data.osm.DataSet;
[3257]17import org.openstreetmap.josm.data.osm.Node;
[3440]18import org.openstreetmap.josm.data.osm.NodeData;
[2305]19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.PrimitiveData;
[3660]21import org.openstreetmap.josm.gui.layer.OsmDataLayer;
[5953]22import org.openstreetmap.josm.tools.CheckParameterUtil;
[11374]23import org.openstreetmap.josm.tools.JosmRuntimeException;
[2305]24
[3703]25/**
26 * Add primitives to a data layer.
[5953]27 * @since 2305
[3703]28 */
[2305]29public class AddPrimitivesCommand extends Command {
30
[11609]31 private List<PrimitiveData> data;
32 private Collection<PrimitiveData> toSelect;
33 private List<PrimitiveData> preExistingData;
[2305]34
[3703]35 // only filled on undo
[8840]36 private List<OsmPrimitive> createdPrimitives;
[3703]37
[5953]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 */
[2305]42 public AddPrimitivesCommand(List<PrimitiveData> data) {
[5953]43 this(data, data);
[2305]44 }
[5953]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 }
[6069]66
[8512]67 private void init(List<PrimitiveData> data, List<PrimitiveData> toSelect) {
[5953]68 CheckParameterUtil.ensureParameterNotNull(data, "data");
[11609]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);
[5953]74 }
[3660]75 }
[2305]76
[8510]77 @Override
78 public boolean executeCommand() {
[11609]79 DataSet ds = getAffectedDataSet();
[3703]80 if (createdPrimitives == null) { // first time execution
[7005]81 List<OsmPrimitive> newPrimitives = new ArrayList<>(data.size());
[11609]82 preExistingData = new ArrayList<>();
[2305]83
[3703]84 for (PrimitiveData pd : data) {
[11609]85 OsmPrimitive primitive = ds.getPrimitiveById(pd);
[3703]86 boolean created = primitive == null;
[11609]87 if (primitive == null) {
[3703]88 primitive = pd.getType().newInstance(pd.getUniqueId(), true);
[11609]89 } else {
90 preExistingData.add(primitive.save());
[3703]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) {
[11609]96 ds.addPrimitive(primitive);
[3703]97 }
98 newPrimitives.add(primitive);
99 }
[2305]100
[5953]101 // Then load ways and relations
[8510]102 for (int i = 0; i < newPrimitives.size(); i++) {
[3703]103 if (!(newPrimitives.get(i) instanceof Node)) {
104 newPrimitives.get(i).load(data.get(i));
105 }
[3257]106 }
[10804]107 newPrimitives.stream().forEach(p -> p.setModified(true));
[3703]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) {
[11609]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 }
[3440]120 }
[2305]121 }
[11609]122 if (toSelect != null) {
123 ds.setSelected(toSelect.stream().map(ds::getPrimitiveById).collect(Collectors.toList()));
124 }
[2305]125 return true;
126 }
127
128 @Override public void undoCommand() {
[10467]129 DataSet ds = getAffectedDataSet();
[3703]130 if (createdPrimitives == null) {
[7005]131 createdPrimitives = new ArrayList<>(data.size());
[5953]132 for (PrimitiveData pd : data) {
133 OsmPrimitive p = ds.getPrimitiveById(pd);
134 createdPrimitives.add(p);
[3703]135 }
136 createdPrimitives = PurgeCommand.topoSort(createdPrimitives);
[11609]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 {
[3703]143 ds.removePrimitive(osm);
144 }
[2305]145 }
146 }
147
[4918]148 @Override
149 public String getDescriptionText() {
[3703]150 int size = data != null ? data.size() : createdPrimitives.size();
[4918]151 return trn("Added {0} object", "Added {0} objects", size, size);
[2305]152 }
153
154 @Override
[4918]155 public Icon getDescriptionIcon() {
156 return null;
157 }
158
159 @Override
[2305]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
[3262]165 @Override
166 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
[3703]167 if (createdPrimitives != null)
168 return createdPrimitives;
[6069]169
[7005]170 Collection<OsmPrimitive> prims = new HashSet<>();
[3262]171 for (PrimitiveData d : data) {
[11553]172 prims.add(Optional.ofNullable(getAffectedDataSet().getPrimitiveById(d)).orElseThrow(
173 () -> new JosmRuntimeException("No primitive found for " + d)));
[3262]174 }
175 return prims;
176 }
[8456]177
178 @Override
179 public int hashCode() {
[11609]180 return Objects.hash(super.hashCode(), data, toSelect, preExistingData, createdPrimitives);
[8456]181 }
182
183 @Override
184 public boolean equals(Object obj) {
[9371]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) &&
[11374]190 Objects.equals(toSelect, that.toSelect) &&
[11609]191 Objects.equals(preExistingData, that.preExistingData) &&
192 Objects.equals(createdPrimitives, that.createdPrimitives);
[8456]193 }
[2305]194}
Note: See TracBrowser for help on using the repository browser.