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

Last change on this file since 8674 was 8510, checked in by Don-vip, 9 years ago

checkstyle: enable relevant whitespace checks and fix them

  • Property svn:eol-style set to native
File size: 11.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.command;
3
4import java.awt.GridBagLayout;
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.HashMap;
8import java.util.LinkedHashMap;
9import java.util.Map;
10import java.util.Map.Entry;
11
12import javax.swing.JOptionPane;
13import javax.swing.JPanel;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.coor.EastNorth;
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.data.osm.Node;
19import org.openstreetmap.josm.data.osm.OsmPrimitive;
20import org.openstreetmap.josm.data.osm.PrimitiveData;
21import org.openstreetmap.josm.data.osm.Relation;
22import org.openstreetmap.josm.data.osm.Way;
23import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
24import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
25import org.openstreetmap.josm.gui.layer.Layer;
26import org.openstreetmap.josm.gui.layer.OsmDataLayer;
27import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
28import org.openstreetmap.josm.tools.CheckParameterUtil;
29
30/**
31 * Classes implementing Command modify a dataset in a specific way. A command is
32 * one atomic action on a specific dataset, such as move or delete.
33 *
34 * The command remembers the {@link OsmDataLayer} it is operating on.
35 *
36 * @author imi
37 */
38public abstract class Command extends PseudoCommand {
39
40 private static final class CloneVisitor extends AbstractVisitor {
41 public final Map<OsmPrimitive, PrimitiveData> orig = new LinkedHashMap<>();
42
43 @Override
44 public void visit(Node n) {
45 orig.put(n, n.save());
46 }
47
48 @Override
49 public void visit(Way w) {
50 orig.put(w, w.save());
51 }
52
53 @Override
54 public void visit(Relation e) {
55 orig.put(e, e.save());
56 }
57 }
58
59 /**
60 * Small helper for holding the interesting part of the old data state of the objects.
61 */
62 public static class OldNodeState {
63
64 private final LatLon latlon;
65 private final EastNorth eastNorth; // cached EastNorth to be used for applying exact displacement
66 private final boolean modified;
67
68 /**
69 * Constructs a new {@code OldNodeState} for the given node.
70 * @param node The node whose state has to be remembered
71 */
72 public OldNodeState(Node node) {
73 latlon = node.getCoor();
74 eastNorth = node.getEastNorth();
75 modified = node.isModified();
76 }
77
78 /**
79 * Returns old lat/lon.
80 * @return old lat/lon
81 * @see Node#getCoor()
82 */
83 public final LatLon getLatlon() {
84 return latlon;
85 }
86
87 /**
88 * Returns old east/north.
89 * @return old east/north
90 * @see Node#getEastNorth()
91 */
92 public final EastNorth getEastNorth() {
93 return eastNorth;
94 }
95
96 /**
97 * Returns old modified state.
98 * @return old modified state
99 * @see Node #isModified()
100 */
101 public final boolean isModified() {
102 return modified;
103 }
104
105 @Override
106 public int hashCode() {
107 final int prime = 31;
108 int result = 1;
109 result = prime * result + ((eastNorth == null) ? 0 : eastNorth.hashCode());
110 result = prime * result + ((latlon == null) ? 0 : latlon.hashCode());
111 result = prime * result + (modified ? 1231 : 1237);
112 return result;
113 }
114
115 @Override
116 public boolean equals(Object obj) {
117 if (this == obj)
118 return true;
119 if (obj == null)
120 return false;
121 if (getClass() != obj.getClass())
122 return false;
123 OldNodeState other = (OldNodeState) obj;
124 if (eastNorth == null) {
125 if (other.eastNorth != null)
126 return false;
127 } else if (!eastNorth.equals(other.eastNorth))
128 return false;
129 if (latlon == null) {
130 if (other.latlon != null)
131 return false;
132 } else if (!latlon.equals(other.latlon))
133 return false;
134 if (modified != other.modified)
135 return false;
136 return true;
137 }
138 }
139
140 /** the map of OsmPrimitives in the original state to OsmPrimitives in cloned state */
141 private Map<OsmPrimitive, PrimitiveData> cloneMap = new HashMap<>();
142
143 /** the layer which this command is applied to */
144 private final OsmDataLayer layer;
145
146 /**
147 * Creates a new command in the context of the current edit layer, if any
148 */
149 public Command() {
150 this.layer = Main.main == null ? null : Main.main.getEditLayer();
151 }
152
153 /**
154 * Creates a new command in the context of a specific data layer
155 *
156 * @param layer the data layer. Must not be null.
157 * @throws IllegalArgumentException if layer is null
158 */
159 public Command(OsmDataLayer layer) {
160 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
161 this.layer = layer;
162 }
163
164 /**
165 * Executes the command on the dataset. This implementation will remember all
166 * primitives returned by fillModifiedData for restoring them on undo.
167 * @return true
168 */
169 public boolean executeCommand() {
170 CloneVisitor visitor = new CloneVisitor();
171 Collection<OsmPrimitive> all = new ArrayList<>();
172 fillModifiedData(all, all, all);
173 for (OsmPrimitive osm : all) {
174 osm.accept(visitor);
175 }
176 cloneMap = visitor.orig;
177 return true;
178 }
179
180 /**
181 * Undoes the command.
182 * It can be assumed that all objects are in the same state they were before.
183 * It can also be assumed that executeCommand was called exactly once before.
184 *
185 * This implementation undoes all objects stored by a former call to executeCommand.
186 */
187 public void undoCommand() {
188 for (Entry<OsmPrimitive, PrimitiveData> e : cloneMap.entrySet()) {
189 OsmPrimitive primitive = e.getKey();
190 if (primitive.getDataSet() != null) {
191 e.getKey().load(e.getValue());
192 }
193 }
194 }
195
196 /**
197 * Called when a layer has been removed to have the command remove itself from
198 * any buffer if it is not longer applicable to the dataset (e.g. it was part of
199 * the removed layer)
200 *
201 * @param oldLayer the old layer
202 * @return true if this command
203 */
204 public boolean invalidBecauselayerRemoved(Layer oldLayer) {
205 if (!(oldLayer instanceof OsmDataLayer))
206 return false;
207 return layer == oldLayer;
208 }
209
210 /**
211 * Lets other commands access the original version
212 * of the object. Usually for undoing.
213 * @param osm The requested OSM object
214 * @return The original version of the requested object, if any
215 */
216 public PrimitiveData getOrig(OsmPrimitive osm) {
217 return cloneMap.get(osm);
218 }
219
220 /**
221 * Replies the layer this command is (or was) applied to.
222 *
223 */
224 protected OsmDataLayer getLayer() {
225 return layer;
226 }
227
228 /**
229 * Fill in the changed data this command operates on.
230 * Add to the lists, don't clear them.
231 *
232 * @param modified The modified primitives
233 * @param deleted The deleted primitives
234 * @param added The added primitives
235 */
236 public abstract void fillModifiedData(Collection<OsmPrimitive> modified,
237 Collection<OsmPrimitive> deleted,
238 Collection<OsmPrimitive> added);
239
240 /**
241 * Return the primitives that take part in this command.
242 */
243 @Override public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
244 return cloneMap.keySet();
245 }
246
247 /**
248 * Check whether user is about to operate on data outside of the download area.
249 * Request confirmation if he is.
250 *
251 * @param operation the operation name which is used for setting some preferences
252 * @param dialogTitle the title of the dialog being displayed
253 * @param outsideDialogMessage the message text to be displayed when data is outside of the download area
254 * @param incompleteDialogMessage the message text to be displayed when data is incomplete
255 * @param primitives the primitives to operate on
256 * @param ignore {@code null} or a primitive to be ignored
257 * @return true, if operating on outlying primitives is OK; false, otherwise
258 */
259 public static boolean checkAndConfirmOutlyingOperation(String operation,
260 String dialogTitle, String outsideDialogMessage, String incompleteDialogMessage,
261 Collection<? extends OsmPrimitive> primitives,
262 Collection<? extends OsmPrimitive> ignore) {
263 boolean outside = false;
264 boolean incomplete = false;
265 for (OsmPrimitive osm : primitives) {
266 if (osm.isIncomplete()) {
267 incomplete = true;
268 } else if (osm.isOutsideDownloadArea()
269 && (ignore == null || !ignore.contains(osm))) {
270 outside = true;
271 }
272 }
273 if (outside) {
274 JPanel msg = new JPanel(new GridBagLayout());
275 msg.add(new JMultilineLabel("<html>" + outsideDialogMessage + "</html>"));
276 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
277 operation + "_outside_nodes",
278 Main.parent,
279 msg,
280 dialogTitle,
281 JOptionPane.YES_NO_OPTION,
282 JOptionPane.QUESTION_MESSAGE,
283 JOptionPane.YES_OPTION);
284 if (!answer)
285 return false;
286 }
287 if (incomplete) {
288 JPanel msg = new JPanel(new GridBagLayout());
289 msg.add(new JMultilineLabel("<html>" + incompleteDialogMessage + "</html>"));
290 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
291 operation + "_incomplete",
292 Main.parent,
293 msg,
294 dialogTitle,
295 JOptionPane.YES_NO_OPTION,
296 JOptionPane.QUESTION_MESSAGE,
297 JOptionPane.YES_OPTION);
298 if (!answer)
299 return false;
300 }
301 return true;
302 }
303
304 @Override
305 public int hashCode() {
306 final int prime = 31;
307 int result = 1;
308 result = prime * result + ((cloneMap == null) ? 0 : cloneMap.hashCode());
309 result = prime * result + ((layer == null) ? 0 : layer.hashCode());
310 return result;
311 }
312
313 @Override
314 public boolean equals(Object obj) {
315 if (this == obj)
316 return true;
317 if (obj == null)
318 return false;
319 if (getClass() != obj.getClass())
320 return false;
321 Command other = (Command) obj;
322 if (cloneMap == null) {
323 if (other.cloneMap != null)
324 return false;
325 } else if (!cloneMap.equals(other.cloneMap))
326 return false;
327 if (layer == null) {
328 if (other.layer != null)
329 return false;
330 } else if (!layer.equals(other.layer))
331 return false;
332 return true;
333 }
334}
Note: See TracBrowser for help on using the repository browser.