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

Last change on this file since 6084 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

  • Property svn:eol-style set to native
File size: 8.4 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.command;
3
4import java.awt.GridBagLayout;
5import java.awt.geom.Area;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.LinkedHashMap;
10import java.util.Map;
11import java.util.Map.Entry;
12
13import javax.swing.JLabel;
14import javax.swing.JOptionPane;
15import javax.swing.JPanel;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.coor.LatLon;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.PrimitiveData;
22import org.openstreetmap.josm.data.osm.Relation;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
25import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
26import org.openstreetmap.josm.gui.layer.Layer;
27import org.openstreetmap.josm.gui.layer.OsmDataLayer;
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 */
38abstract public class Command extends PseudoCommand {
39
40 private static final class CloneVisitor extends AbstractVisitor {
41 public final Map<OsmPrimitive, PrimitiveData> orig = new LinkedHashMap<OsmPrimitive, PrimitiveData>();
42
43 @Override
44 public void visit(Node n) {
45 orig.put(n, n.save());
46 }
47 @Override
48 public void visit(Way w) {
49 orig.put(w, w.save());
50 }
51 @Override
52 public void visit(Relation e) {
53 orig.put(e, e.save());
54 }
55 }
56
57 /** the map of OsmPrimitives in the original state to OsmPrimitives in cloned state */
58 private Map<OsmPrimitive, PrimitiveData> cloneMap = new HashMap<OsmPrimitive, PrimitiveData>();
59
60 /** the layer which this command is applied to */
61 private final OsmDataLayer layer;
62
63 /**
64 * Creates a new command in the context of the current edit layer, if any
65 */
66 public Command() {
67 this.layer = Main.map != null && Main.map.mapView != null ? Main.map.mapView.getEditLayer() : null;
68 }
69
70 /**
71 * Creates a new command in the context of a specific data layer
72 *
73 * @param layer the data layer. Must not be null.
74 * @throws IllegalArgumentException thrown if layer is null
75 */
76 public Command(OsmDataLayer layer) throws IllegalArgumentException {
77 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
78 this.layer = layer;
79 }
80
81 /**
82 * Executes the command on the dataset. This implementation will remember all
83 * primitives returned by fillModifiedData for restoring them on undo.
84 * @return true
85 */
86 public boolean executeCommand() {
87 CloneVisitor visitor = new CloneVisitor();
88 Collection<OsmPrimitive> all = new ArrayList<OsmPrimitive>();
89 fillModifiedData(all, all, all);
90 for (OsmPrimitive osm : all) {
91 osm.accept(visitor);
92 }
93 cloneMap = visitor.orig;
94 return true;
95 }
96
97 /**
98 * Undoes the command.
99 * It can be assumed that all objects are in the same state they were before.
100 * It can also be assumed that executeCommand was called exactly once before.
101 *
102 * This implementation undoes all objects stored by a former call to executeCommand.
103 */
104 public void undoCommand() {
105 for (Entry<OsmPrimitive, PrimitiveData> e : cloneMap.entrySet()) {
106 OsmPrimitive primitive = e.getKey();
107 if (primitive.getDataSet() != null) {
108 e.getKey().load(e.getValue());
109 }
110 }
111 }
112
113 /**
114 * Called when a layer has been removed to have the command remove itself from
115 * any buffer if it is not longer applicable to the dataset (e.g. it was part of
116 * the removed layer)
117 *
118 * @param oldLayer the old layer
119 * @return true if this command
120 */
121 public boolean invalidBecauselayerRemoved(Layer oldLayer) {
122 if (!(oldLayer instanceof OsmDataLayer))
123 return false;
124 return layer == oldLayer;
125 }
126
127 /**
128 * Lets other commands access the original version
129 * of the object. Usually for undoing.
130 * @param osm The requested OSM object
131 * @return The original version of the requested object, if any
132 */
133 public PrimitiveData getOrig(OsmPrimitive osm) {
134 return cloneMap.get(osm);
135 }
136
137 /**
138 * Replies the layer this command is (or was) applied to.
139 *
140 */
141 protected OsmDataLayer getLayer() {
142 return layer;
143 }
144
145 /**
146 * Fill in the changed data this command operates on.
147 * Add to the lists, don't clear them.
148 *
149 * @param modified The modified primitives
150 * @param deleted The deleted primitives
151 * @param added The added primitives
152 */
153 abstract public void fillModifiedData(Collection<OsmPrimitive> modified,
154 Collection<OsmPrimitive> deleted,
155 Collection<OsmPrimitive> added);
156
157 /**
158 * Return the primitives that take part in this command.
159 */
160 @Override public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
161 return cloneMap.keySet();
162 }
163
164 /**
165 * Check whether user is about to operate on data outside of the download area.
166 * Request confirmation if he is.
167 *
168 * @param operation the operation name which is used for setting some preferences
169 * @param dialogTitle the title of the dialog being displayed
170 * @param outsideDialogMessage the message text to be displayed when data is outside of the download area
171 * @param incompleteDialogMessage the message text to be displayed when data is incomplete
172 * @param area the area used to determine whether data is outlying
173 * @param primitives the primitives to operate on
174 * @param ignore {@code null} or a primitive to be ignored
175 * @return true, if operating on outlying primitives is OK; false, otherwise
176 */
177 public static boolean checkAndConfirmOutlyingOperation(String operation,
178 String dialogTitle, String outsideDialogMessage, String incompleteDialogMessage,
179 Area area, Collection<? extends OsmPrimitive> primitives,
180 Collection<? extends OsmPrimitive> ignore) {
181 boolean outside = false;
182 boolean incomplete = false;
183 for (OsmPrimitive osm : primitives) {
184 if (osm.isIncomplete()) {
185 incomplete = true;
186 } else if (area != null && isOutlying(osm, area)
187 && (ignore == null || !ignore.contains(osm))) {
188 outside = true;
189 }
190 }
191 if (outside) {
192 JPanel msg = new JPanel(new GridBagLayout());
193 msg.add(new JLabel("<html>" + outsideDialogMessage + "</html>"));
194 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
195 operation + "_outside_nodes",
196 Main.parent,
197 msg,
198 dialogTitle,
199 JOptionPane.YES_NO_OPTION,
200 JOptionPane.QUESTION_MESSAGE,
201 JOptionPane.YES_OPTION);
202 if(!answer)
203 return false;
204 }
205 if (incomplete) {
206 JPanel msg = new JPanel(new GridBagLayout());
207 msg.add(new JLabel("<html>" + incompleteDialogMessage + "</html>"));
208 boolean answer = ConditionalOptionPaneUtil.showConfirmationDialog(
209 operation + "_incomplete",
210 Main.parent,
211 msg,
212 dialogTitle,
213 JOptionPane.YES_NO_OPTION,
214 JOptionPane.QUESTION_MESSAGE,
215 JOptionPane.YES_OPTION);
216 if(!answer)
217 return false;
218 }
219 return true;
220 }
221
222 private static boolean isOutlying(OsmPrimitive osm, Area area) {
223 if (osm instanceof Node && !osm.isNewOrUndeleted()) {
224 LatLon coor = ((Node) osm).getCoor();
225 return coor != null && !area.contains(coor);
226 } else if (osm instanceof Way) {
227 for (Node n : ((Way) osm).getNodes()) {
228 if (isOutlying(n, area)) {
229 return true;
230 }
231 }
232 return false;
233 }
234 return false;
235 }
236}
Note: See TracBrowser for help on using the repository browser.