source: josm/trunk/src/org/openstreetmap/josm/command/Command.java@ 12987

Last change on this file since 12987 was 12809, checked in by bastiK, 7 years ago

replace abstract class AbstractVisitor by interface OsmPrimitiveVisitor; deprecate Visitor

  • data.osm.visitor.Visitor awkwardly mixes OsmPrimitive types and Changeset class; this may have been used in the past, but is no longer needed; AbstractVisitor should have been a super-interface of Visitor in the first place
  • hopefully, this is binary compatible and plugins can be updated gracefully
  • Property svn:eol-style set to native
File size: 11.1 KB
RevLine 
[8378]1// License: GPL. For details, see LICENSE file.
[21]2package org.openstreetmap.josm.command;
3
[3034]4import java.util.ArrayList;
[22]5import java.util.Collection;
[146]6import java.util.HashMap;
[3034]7import java.util.LinkedHashMap;
[146]8import java.util.Map;
[86]9import java.util.Map.Entry;
[9371]10import java.util.Objects;
[21]11
[630]12import org.openstreetmap.josm.Main;
[6173]13import org.openstreetmap.josm.data.coor.EastNorth;
14import org.openstreetmap.josm.data.coor.LatLon;
[10467]15import org.openstreetmap.josm.data.osm.DataSet;
[146]16import org.openstreetmap.josm.data.osm.Node;
[22]17import org.openstreetmap.josm.data.osm.OsmPrimitive;
[2284]18import org.openstreetmap.josm.data.osm.PrimitiveData;
[1523]19import org.openstreetmap.josm.data.osm.Relation;
[146]20import org.openstreetmap.josm.data.osm.Way;
[12809]21import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
[12636]22import org.openstreetmap.josm.gui.MainApplication;
[304]23import org.openstreetmap.josm.gui.layer.Layer;
24import org.openstreetmap.josm.gui.layer.OsmDataLayer;
[2844]25import org.openstreetmap.josm.tools.CheckParameterUtil;
[21]26
27/**
28 * Classes implementing Command modify a dataset in a specific way. A command is
[22]29 * one atomic action on a specific dataset, such as move or delete.
[21]30 *
[12718]31 * The command remembers the {@link DataSet} it is operating on.
[2284]32 *
[21]33 * @author imi
[10599]34 * @since 21 (creation)
35 * @since 10599 (signature)
[21]36 */
[10599]37public abstract class Command implements PseudoCommand {
[21]38
[10948]39 /** IS_OK : operation is okay */
40 public static final int IS_OK = 0;
41 /** IS_OUTSIDE : operation on element outside of download area */
42 public static final int IS_OUTSIDE = 1;
43 /** IS_INCOMPLETE: operation on incomplete target */
44 public static final int IS_INCOMPLETE = 2;
45
[12809]46 private static final class CloneVisitor implements OsmPrimitiveVisitor {
[7005]47 public final Map<OsmPrimitive, PrimitiveData> orig = new LinkedHashMap<>();
[304]48
[6084]49 @Override
[1750]50 public void visit(Node n) {
[2284]51 orig.put(n, n.save());
[1750]52 }
[8510]53
[6084]54 @Override
[1750]55 public void visit(Way w) {
[2284]56 orig.put(w, w.save());
[1750]57 }
[8510]58
[6084]59 @Override
[1750]60 public void visit(Relation e) {
[2284]61 orig.put(e, e.save());
[1750]62 }
63 }
[304]64
[6173]65 /**
66 * Small helper for holding the interesting part of the old data state of the objects.
67 */
68 public static class OldNodeState {
69
[10248]70 private final LatLon latLon;
[8285]71 private final EastNorth eastNorth; // cached EastNorth to be used for applying exact displacement
72 private final boolean modified;
[6173]73
74 /**
75 * Constructs a new {@code OldNodeState} for the given node.
76 * @param node The node whose state has to be remembered
77 */
[8510]78 public OldNodeState(Node node) {
[10248]79 latLon = node.getCoor();
[6173]80 eastNorth = node.getEastNorth();
81 modified = node.isModified();
82 }
[8285]83
84 /**
85 * Returns old lat/lon.
86 * @return old lat/lon
87 * @see Node#getCoor()
[10248]88 * @since 10248
[8285]89 */
[10248]90 public final LatLon getLatLon() {
91 return latLon;
[8285]92 }
93
94 /**
95 * Returns old east/north.
96 * @return old east/north
97 * @see Node#getEastNorth()
98 */
99 public final EastNorth getEastNorth() {
100 return eastNorth;
101 }
102
103 /**
104 * Returns old modified state.
105 * @return old modified state
106 * @see Node #isModified()
107 */
108 public final boolean isModified() {
109 return modified;
110 }
[8447]111
112 @Override
113 public int hashCode() {
[10248]114 return Objects.hash(latLon, eastNorth, modified);
[8447]115 }
116
117 @Override
118 public boolean equals(Object obj) {
[9371]119 if (this == obj) return true;
120 if (obj == null || getClass() != obj.getClass()) return false;
121 OldNodeState that = (OldNodeState) obj;
122 return modified == that.modified &&
[10248]123 Objects.equals(latLon, that.latLon) &&
[9371]124 Objects.equals(eastNorth, that.eastNorth);
[8447]125 }
[6173]126 }
127
[1750]128 /** the map of OsmPrimitives in the original state to OsmPrimitives in cloned state */
[7005]129 private Map<OsmPrimitive, PrimitiveData> cloneMap = new HashMap<>();
[304]130
[12718]131 /**
132 * the layer which this command is applied to
133 * @deprecated to be removed end of 2017. Use {@link #data} instead
134 */
135 @Deprecated
[5759]136 private final OsmDataLayer layer;
[22]137
[11240]138 /** the dataset which this command is applied to */
139 private final DataSet data;
140
[5759]141 /**
142 * Creates a new command in the context of the current edit layer, if any
[12726]143 * @deprecated to be removed end of 2017. Use {@link #Command(DataSet)} instead
[5759]144 */
[12726]145 @Deprecated
[1750]146 public Command() {
[12636]147 this.layer = MainApplication.getLayerManager().getEditLayer();
[12718]148 this.data = layer != null ? layer.data : Main.main.getEditDataSet();
[1750]149 }
[1856]150
[1750]151 /**
[1856]152 * Creates a new command in the context of a specific data layer
[2284]153 *
[2308]154 * @param layer the data layer. Must not be null.
[8291]155 * @throws IllegalArgumentException if layer is null
[12718]156 * @deprecated to be removed end of 2017. Use {@link #Command(DataSet)} instead
[1856]157 */
[12718]158 @Deprecated
[8291]159 public Command(OsmDataLayer layer) {
[2844]160 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
[1856]161 this.layer = layer;
[11240]162 this.data = layer.data;
[1856]163 }
164
165 /**
[11240]166 * Creates a new command in the context of a specific data set, without data layer
167 *
168 * @param data the data set. Must not be null.
169 * @throws IllegalArgumentException if data is null
170 * @since 11240
171 */
172 public Command(DataSet data) {
173 CheckParameterUtil.ensureParameterNotNull(data, "data");
174 this.layer = null;
175 this.data = data;
176 }
177
178 /**
[1750]179 * Executes the command on the dataset. This implementation will remember all
180 * primitives returned by fillModifiedData for restoring them on undo.
[10452]181 * <p>
182 * The layer should be invalidated after execution so that it can be re-painted.
[5759]183 * @return true
[10452]184 * @see #invalidateAffectedLayers()
[1750]185 */
186 public boolean executeCommand() {
187 CloneVisitor visitor = new CloneVisitor();
[7005]188 Collection<OsmPrimitive> all = new ArrayList<>();
[1750]189 fillModifiedData(all, all, all);
190 for (OsmPrimitive osm : all) {
[6009]191 osm.accept(visitor);
[1750]192 }
193 cloneMap = visitor.orig;
194 return true;
195 }
[86]196
[1750]197 /**
198 * Undoes the command.
199 * It can be assumed that all objects are in the same state they were before.
200 * It can also be assumed that executeCommand was called exactly once before.
201 *
202 * This implementation undoes all objects stored by a former call to executeCommand.
203 */
204 public void undoCommand() {
[2284]205 for (Entry<OsmPrimitive, PrimitiveData> e : cloneMap.entrySet()) {
[2683]206 OsmPrimitive primitive = e.getKey();
207 if (primitive.getDataSet() != null) {
208 e.getKey().load(e.getValue());
209 }
[1750]210 }
211 }
[304]212
[1750]213 /**
214 * Called when a layer has been removed to have the command remove itself from
215 * any buffer if it is not longer applicable to the dataset (e.g. it was part of
216 * the removed layer)
[2284]217 *
[10663]218 * @param oldLayer the old layer that was removed
219 * @return true if this command is invalid after that layer is removed.
[12718]220 * @deprecated to be removed end of 2017.
[1750]221 */
[12718]222 @Deprecated
[1750]223 public boolean invalidBecauselayerRemoved(Layer oldLayer) {
224 return layer == oldLayer;
225 }
[304]226
[630]227 /**
228 * Lets other commands access the original version
229 * of the object. Usually for undoing.
[5759]230 * @param osm The requested OSM object
231 * @return The original version of the requested object, if any
[630]232 */
[2284]233 public PrimitiveData getOrig(OsmPrimitive osm) {
[5759]234 return cloneMap.get(osm);
[630]235 }
[94]236
[1750]237 /**
238 * Replies the layer this command is (or was) applied to.
[8931]239 * @return the layer this command is (or was) applied to
[12718]240 * @deprecated to be removed end of 2017. Use {@link #getAffectedDataSet} instead
[1750]241 */
[12718]242 @Deprecated
[6881]243 protected OsmDataLayer getLayer() {
[1750]244 return layer;
245 }
[630]246
[1750]247 /**
[10467]248 * Gets the data set this command affects.
249 * @return The data set. May be <code>null</code> if no layer was set and no edit layer was found.
250 * @since 10467
251 */
252 public DataSet getAffectedDataSet() {
[11240]253 return data;
[10467]254 }
255
256 /**
[1750]257 * Fill in the changed data this command operates on.
258 * Add to the lists, don't clear them.
259 *
260 * @param modified The modified primitives
261 * @param deleted The deleted primitives
262 * @param added The added primitives
263 */
[6883]264 public abstract void fillModifiedData(Collection<OsmPrimitive> modified,
[1750]265 Collection<OsmPrimitive> deleted,
266 Collection<OsmPrimitive> added);
267
[3262]268 /**
269 * Return the primitives that take part in this command.
[8945]270 * The collection is computed during execution.
[3262]271 */
[8931]272 @Override
273 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
[3262]274 return cloneMap.keySet();
275 }
[1750]276
[3262]277 /**
[4458]278 * Check whether user is about to operate on data outside of the download area.
[10948]279 *
280 * @param primitives the primitives to operate on
281 * @param ignore {@code null} or a primitive to be ignored
282 * @return true, if operating on outlying primitives is OK; false, otherwise
283 */
[10970]284 public static int checkOutlyingOrIncompleteOperation(
[10948]285 Collection<? extends OsmPrimitive> primitives,
286 Collection<? extends OsmPrimitive> ignore) {
287 int res = 0;
288 for (OsmPrimitive osm : primitives) {
289 if (osm.isIncomplete()) {
290 res |= IS_INCOMPLETE;
291 } else if (osm.isOutsideDownloadArea()
292 && (ignore == null || !ignore.contains(osm))) {
293 res |= IS_OUTSIDE;
294 }
295 }
296 return res;
297 }
298
299 /**
[12348]300 * Ensures that all primitives that are participating in this command belong to the affected data set.
301 *
302 * Commands may use this in their update methods to check the consitency of the primitives they operate on.
303 * @throws AssertionError if no {@link DataSet} is set or if any primitive does not belong to that dataset.
304 */
305 protected void ensurePrimitivesAreInDataset() {
306 for (OsmPrimitive primitive : this.getParticipatingPrimitives()) {
307 if (primitive.getDataSet() != this.getAffectedDataSet()) {
308 throw new AssertionError("Primitive is of wrong data set for this command: " + primitive);
309 }
310 }
311 }
312
[8447]313 @Override
314 public int hashCode() {
[11243]315 return Objects.hash(cloneMap, layer, data);
[8447]316 }
317
318 @Override
319 public boolean equals(Object obj) {
[9371]320 if (this == obj) return true;
321 if (obj == null || getClass() != obj.getClass()) return false;
322 Command command = (Command) obj;
323 return Objects.equals(cloneMap, command.cloneMap) &&
[11240]324 Objects.equals(layer, command.layer) &&
325 Objects.equals(data, command.data);
[8447]326 }
[10452]327
328 /**
329 * Invalidate all layers that were affected by this command.
330 * @see Layer#invalidate()
[12718]331 * @deprecated to be removed end of 2017.
[10452]332 */
[12718]333 @Deprecated
[10452]334 public void invalidateAffectedLayers() {
335 OsmDataLayer layer = getLayer();
336 if (layer != null) {
337 layer.invalidate();
338 }
339 }
[21]340}
Note: See TracBrowser for help on using the repository browser.