source: josm/trunk/src/org/openstreetmap/josm/command/DeleteCommand.java@ 988

Last change on this file since 988 was 988, checked in by stoecker, 16 years ago

make advanced object deletion available for outside, so validator can use it

  • Property svn:eol-style set to native
File size: 9.5 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.command;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.Iterator;
13import java.util.LinkedList;
14import java.util.List;
15
16import javax.swing.JLabel;
17import javax.swing.JOptionPane;
18import javax.swing.tree.DefaultMutableTreeNode;
19import javax.swing.tree.MutableTreeNode;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Relation;
25import org.openstreetmap.josm.data.osm.RelationMember;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.WaySegment;
28import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
29import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
30import org.openstreetmap.josm.tools.ImageProvider;
31
32/**
33 * A command to delete a number of primitives from the dataset.
34 * @author imi
35 */
36public class DeleteCommand extends Command {
37
38 /**
39 * The primitive that get deleted.
40 */
41 private final Collection<? extends OsmPrimitive> data;
42
43 /**
44 * Constructor for a collection of data
45 */
46 public DeleteCommand(Collection<? extends OsmPrimitive> data) {
47 this.data = data;
48 }
49 /**
50 * Constructor for a single data item. Use the collection
51 * constructor to delete multiple objects.
52 */
53 public DeleteCommand(OsmPrimitive data) {
54 this.data = Collections.singleton(data);
55 }
56
57 @Override public boolean executeCommand() {
58 super.executeCommand();
59 for (OsmPrimitive osm : data) {
60 osm.delete(true);
61 }
62 return true;
63 }
64
65 @Override public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
66 deleted.addAll(data);
67 }
68
69 @Override public MutableTreeNode description() {
70 NameVisitor v = new NameVisitor();
71
72 if (data.size() == 1) {
73 data.iterator().next().visit(v);
74 return new DefaultMutableTreeNode(new JLabel(tr("Delete {1} {0}", v.name, tr(v.className)), v.icon, JLabel.HORIZONTAL));
75 }
76
77 String cname = null;
78 String cnamem = null;
79 for (OsmPrimitive osm : data) {
80 osm.visit(v);
81 if (cname == null)
82 {
83 cname = v.className;
84 cnamem = v.classNamePlural;
85 }
86 else if (!cname.equals(v.className))
87 {
88 cname = "object";
89 cnamem = trn("object", "objects", 2);
90 }
91 }
92 DefaultMutableTreeNode root = new DefaultMutableTreeNode(new JLabel(
93 tr("Delete {0} {1}", data.size(), trn(cname, cnamem, data.size())), ImageProvider.get("data", cname), JLabel.HORIZONTAL));
94 for (OsmPrimitive osm : data) {
95 osm.visit(v);
96 root.add(new DefaultMutableTreeNode(v.toLabel()));
97 }
98 return root;
99 }
100
101 /**
102 * Delete the primitives and everything they reference.
103 *
104 * If a node is deleted, the node and all ways and relations
105 * the node is part of are deleted as well.
106 *
107 * If a way is deleted, all relations the way is member of are also deleted.
108 *
109 * If a way is deleted, only the way and no nodes are deleted.
110 *
111 * @param selection The list of all object to be deleted.
112 * @return command A command to perform the deletions, or null of there is
113 * nothing to delete.
114 */
115 public static Command deleteWithReferences(Collection<? extends OsmPrimitive> selection) {
116 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
117 for (OsmPrimitive osm : selection)
118 osm.visit(v);
119 v.data.addAll(selection);
120 if (v.data.isEmpty())
121 return null;
122 return new DeleteCommand(v.data);
123 }
124
125 /**
126 * Try to delete all given primitives.
127 *
128 * If a node is used by a way, it's removed from that way. If a node or a
129 * way is used by a relation, inform the user and do not delete.
130 *
131 * If this would cause ways with less than 2 nodes to be created, delete
132 * these ways instead. If they are part of a relation, inform the user
133 * and do not delete.
134 *
135 * @param selection The objects to delete.
136 * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well
137 * @return command A command to perform the deletions, or null of there is
138 * nothing to delete.
139 */
140 private static int testRelation(Relation ref, OsmPrimitive osm)
141 {
142 NameVisitor n = new NameVisitor();
143 ref.visit(n);
144 NameVisitor s = new NameVisitor();
145 osm.visit(s);
146 String role = new String();
147 for (RelationMember m : ref.members)
148 {
149 if (m.member == osm)
150 {
151 role = m.role;
152 break;
153 }
154 }
155 if (role.length() > 0)
156 {
157 return JOptionPane.showConfirmDialog(Main.parent,
158 tr("Selection \"{0}\" is used by relation \"{1}\" with role {2}.\nDelete from relation?", s.name, n.name, role),
159 tr("Conflicting relation"), JOptionPane.YES_NO_OPTION);
160 }
161 else
162 {
163 return JOptionPane.showConfirmDialog(Main.parent,
164 tr("Selection \"{0}\" is used by relation \"{1}\".\nDelete from relation?", s.name, n.name),
165 tr("Conflicting relation"), JOptionPane.YES_NO_OPTION);
166 }
167 }
168
169 public static Command delete(Collection<? extends OsmPrimitive> selection)
170 {
171 return delete(selection, true);
172 }
173
174 public static Command delete(Collection<? extends OsmPrimitive> selection, boolean alsoDeleteNodesInWay) {
175 if (selection.isEmpty()) return null;
176
177 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>(selection);
178 Collection<Way> waysToBeChanged = new HashSet<Way>();
179 HashMap<OsmPrimitive, Collection<OsmPrimitive>> relationsToBeChanged = new HashMap<OsmPrimitive, Collection<OsmPrimitive>>();
180
181 if (alsoDeleteNodesInWay) {
182 // Delete untagged nodes that are to be unreferenced.
183 Collection<OsmPrimitive> delNodes = new HashSet<OsmPrimitive>();
184 for (OsmPrimitive osm : del) {
185 if (osm instanceof Way) {
186 for (Node n : ((Way)osm).nodes) {
187 if (!n.tagged) {
188 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
189 n.visit(v);
190 v.data.removeAll(del);
191 if (v.data.isEmpty()) {
192 delNodes.add(n);
193 }
194 }
195 }
196 }
197 }
198 del.addAll(delNodes);
199 }
200
201 for (OsmPrimitive osm : del) {
202 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
203 osm.visit(v);
204 for (OsmPrimitive ref : v.data) {
205 if (del.contains(ref)) continue;
206 if (ref instanceof Way) {
207 waysToBeChanged.add((Way) ref);
208 } else if (ref instanceof Relation) {
209 if (testRelation((Relation)ref, osm) == JOptionPane.YES_OPTION)
210 {
211 Collection<OsmPrimitive> relset = relationsToBeChanged.get(ref);
212 if(relset == null) relset = new HashSet<OsmPrimitive>();
213 relset.add(osm);
214 relationsToBeChanged.put(ref, relset);
215 }
216 else
217 return null;
218 } else {
219 return null;
220 }
221 }
222 }
223
224 Collection<Command> cmds = new LinkedList<Command>();
225 for (Way w : waysToBeChanged) {
226 Way wnew = new Way(w);
227 wnew.nodes.removeAll(del);
228 if (wnew.nodes.size() < 2) {
229 del.add(w);
230
231 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
232 w.visit(v);
233 for (OsmPrimitive ref : v.data) {
234 if (del.contains(ref)) continue;
235 if (ref instanceof Relation) {
236 Boolean found = false;
237 Collection<OsmPrimitive> relset = relationsToBeChanged.get(ref);
238 if (relset == null)
239 relset = new HashSet<OsmPrimitive>();
240 else
241 {
242 for (OsmPrimitive m : relset) {
243 if(m == w)
244 {
245 found = true;
246 break;
247 }
248 }
249 }
250 if (!found)
251 {
252 if (testRelation((Relation)ref, w) == JOptionPane.YES_OPTION)
253 {
254 relset.add(w);
255 relationsToBeChanged.put(ref, relset);
256 }
257 else
258 return null;
259 }
260 } else {
261 return null;
262 }
263 }
264 } else {
265 cmds.add(new ChangeCommand(w, wnew));
266 }
267 }
268
269 Iterator<OsmPrimitive> iterator = relationsToBeChanged.keySet().iterator();
270 while(iterator.hasNext())
271 {
272 Relation cur = (Relation)iterator.next();
273 Relation rel = new Relation(cur);
274 for(OsmPrimitive osm : relationsToBeChanged.get(cur))
275 {
276 for (RelationMember rm : rel.members) {
277 if (rm.member == osm)
278 {
279 RelationMember mem = new RelationMember();
280 mem.role = rm.role;
281 mem.member = rm.member;
282 rel.members.remove(mem);
283 break;
284 }
285 }
286 }
287 cmds.add(new ChangeCommand(cur, rel));
288 }
289
290 if (!del.isEmpty()) cmds.add(new DeleteCommand(del));
291
292 return new SequenceCommand(tr("Delete"), cmds);
293 }
294
295 public static Command deleteWaySegment(WaySegment ws) {
296 List<Node> n1 = new ArrayList<Node>(),
297 n2 = new ArrayList<Node>();
298
299 n1.addAll(ws.way.nodes.subList(0, ws.lowerIndex + 1));
300 n2.addAll(ws.way.nodes.subList(ws.lowerIndex + 1, ws.way.nodes.size()));
301
302 if (n1.size() < 2 && n2.size() < 2) {
303 return new DeleteCommand(Collections.singleton(ws.way));
304 }
305
306 Way wnew = new Way(ws.way);
307 wnew.nodes.clear();
308
309 if (n1.size() < 2) {
310 wnew.nodes.addAll(n2);
311 return new ChangeCommand(ws.way, wnew);
312 } else if (n2.size() < 2) {
313 wnew.nodes.addAll(n1);
314 return new ChangeCommand(ws.way, wnew);
315 } else {
316 Collection<Command> cmds = new LinkedList<Command>();
317
318 wnew.nodes.addAll(n1);
319 cmds.add(new ChangeCommand(ws.way, wnew));
320
321 Way wnew2 = new Way();
322 if (wnew.keys != null) {
323 wnew2.keys = new HashMap<String, String>(wnew.keys);
324 wnew2.checkTagged();
325 wnew2.checkDirectionTagged();
326 }
327 wnew2.nodes.addAll(n2);
328 cmds.add(new AddCommand(wnew2));
329
330 return new SequenceCommand(tr("Split way segment"), cmds);
331 }
332 }
333}
Note: See TracBrowser for help on using the repository browser.